July 25, 2018


LBaaS Examples

A variety of examples on how to interact with the Load Balancer as a Service offering

This example covers a variety of API calls that you might want to use when interacting with the Load Balancer as a Service offering. General information about this service can be found on the Load Balancer as a Service Documentation

"""
Manages LBaaS instances.

This sample shows how to use a variety of the common methods when working with LBaaS instances. This example is more a proof of concept to show basically how it all works, and not really intended to be used as is.
"""

import SoftLayer
import json

class lbaas():
    GREEN = '\033[92m'    
    UNDERLINE = '\033[4m'
    END = '\033[0m'
    def __init__(self):
        self.client = SoftLayer.Client()
        self.orderData = {}

    def getLBaaSByName(self, name):
        """Finds a LBaaS instance uuid based on its name """
        filter = {"name":{"operation": name}}
        lbaas_items = self.client['Network_LBaaS_LoadBalancer'].getAllObjects(filter=filter)
        if not lbaas_items:
            print("The LBaaS name '%s' doesn't exists." % name)
        else:
            return lbaas_items[0]['uuid']

    def getLBaaSById(self, lbaasId):
        """Finds a LBaaS instance based uuid on its Id"""
        lbaas = self.client['Network_LBaaS_LoadBalancer'].getObject(id=lbaasId)
        return lbaas['uuid']

    def cancelLBaaS(self, uuid):
        """ Cancel a Load Balancer as a Service """
        try:
            response = self.client['Network_LBaaS_LoadBalancer'].cancelLoadBalancer(uuid)

            if response:
                print("The LBaaS was successfuly canceled")

        except (SoftLayer.SoftLayerAPIError, Exception) as e:            
            print("Unable to cancel the LBaaS: %s, %s" % (e.faultCode, e.faultString))

    def addProtocols(self, uuid, protocolConfig):
        """ This sample shows how to add protocols to the LBaaS.
            https://softlayer.github.io/reference/services/SoftLayer_Network_LBaaS_Listener/updateLoadBalancerProtocols/
            https://softlayer.github.io/reference/datatypes/SoftLayer_Network_LBaaS_LoadBalancerProtocolConfiguration
        """
        try:
            # Create the service and add the protocols
            lbaasListenerService = self.client['Network_LBaaS_Listener']
            response = lbaasListenerService.updateLoadBalancerProtocols(uuid, protocolConfig)

            print(json.dumps(response, sort_keys=True, indent=2, separators=(',', ': ')))

        except (SoftLayer.SoftLayerAPIError, Exception) as e:            
            print("Unable to add protocols: %s, %s" % (e.faultCode, e.faultString))


    def addServerInstances(self, uuid, serverInstances):
        """ This sample shows how to add server instances to the LBaaS.
            https://softlayer.github.io/reference/services/SoftLayer_Network_LBaaS_Member/addLoadBalancerMembers/
            https://softlayer.github.io/reference/datatypes/SoftLayer_Network_LBaaS_LoadBalancerServerInstanceInfo/
        """
        try:
            # Create the service and add new members (server instances)
            lbaasMemberService = self.client['Network_LBaaS_Member']
            response = lbaasMemberService.addLoadBalancerMembers(uuid, serverInstances)

            print(json.dumps(response, sort_keys=True, indent=2, separators=(',', ': ')))

        except (SoftLayer.SoftLayerAPIError, Exception) as e:            
            print("Unable to add members (server instances): %s, %s" % (e.faultCode, e.faultString))

    def removeServerInstances(self, uuid, members=None):
        """ This sample shows how to remove LBaaS servers (members).
        https://softlayer.github.io/reference/services/SoftLayer_Network_LBaaS_LoadBalancer/getLoadBalancer
        https://softlayer.github.io/reference/services/SoftLayer_Network_LBaaS_Member/deleteLoadBalancerMembers/
        https://softlayer.github.io/reference/services/SoftLayer_Network_LBaaS_LoadBalancer/getMembers/
        """
        try:
            # Create the service and delete selected server instances
            lbaasMemberService = self.client['Network_LBaaS_Member']
            response = lbaasMemberService.deleteLoadBalancerMembers(uuid, members)

            print(json.dumps(response, sort_keys=True, indent=2, separators=(',', ': ')))

        except (SoftLayer.SoftLayerAPIError, Exception) as e:            
            print("Unable to remove instances: %s, %s" % (e.faultCode, e.faultString)) 

    def title(self, text):
        """Prints out a pretty colored title"""
        return self.GREEN + self.UNDERLINE + "\n" + text + self.END + "\n"

    def convertToMB(self, value):    
        """quick math"""
        return "{0:.2f}".format(float(value)/(10**6))
   
    def printOverview(self, uuid, listeners):
        """Prints out some general info about a LBaaS"""
        statusTable = PrettyTable(['Instances Up','Instances Down'])
        metricsTable = PrettyTable(['Throughput (bps)','Data Processed (MB)', 
                                    'Conection Rate (cps)', 'Active Connections'])
        healthTable = PrettyTable(['Front-End Protocol','Front-End Port', 
                                   'Back-End Protocol', 'Back-End Port', 'Is Healthy'])

        statusTable.title = "SERVER STATUS"
        metricsTable.title = "METRICS"
        healthTable.title = "HEALTH STATUS"
                        
        try:  
            # Following retrieves statistics anmd members which are used to print 
            # information about status and metrics         
            statics = self.client['Network_LBaaS_LoadBalancer'].getLoadBalancerStatistics(uuid)
            members = self.client['Network_LBaaS_LoadBalancer'].getLoadBalancerMemberHealth(uuid)

            # Adding data about status and metrics
            statusTable.add_row([statics['numberOfMembersUp'], statics['numberOfMembersDown']])
            metricsTable.add_row([statics['throughput'], self.convertToMB(statics['dataProcessedByMonth']), 
                                  statics['connectionRate'], statics['totalConnections']])
            # Adding data about health status if there are members
            if members:
                for listener in listeners:
                    for member in members:
                        if listener['defaultPool']['uuid'] == member['poolUuid']:
                            healthy = all(m['status'] == "UP" for m in member['membersHealth'])

                    healthTable.add_row([listener['protocol'], listener['protocolPort'], 
                                         listener['defaultPool']['protocol'], 
                                         listener['defaultPool']['protocolPort'], healthy])

            print (self.title("OVERVIEW:"))
            print (statusTable)
            print (metricsTable)
            print (healthTable)

        except SoftLayer.SoftLayerAPIError as e:            
            print("Unable to retrieve LBaaS overview:: %s, %s" % (e.faultCode, e.faultString))
    
    def printServerInstances(self, dc, members):
        """Prints out some details about the servers on a LB"""
        instancesTable = PrettyTable(['Id','Name','Type','Private Ip', 'Weight'])

        # Filters in order to retrieve only instances in the same datacenter
        guestFilter = {"virtualGuests":{"datacenter":{"name":{"operation": dc}}}}
        hardwareFilter = {"hardware":{"datacenter":{"name":{"operation": dc}}}}
        mask = "mask[id,fullyQualifiedDomainName,primaryBackendIpAddress]"
        # Retrieve instances
        guests = self.client['Account'].getVirtualGuests(filter=guestFilter, mask=mask)
        servers = self.client['Account'].getHardware(filter=hardwareFilter, mask=mask)

        # Adding data to the table about the instances  
        for m in members:
            for g in guests:
                if g['primaryBackendIpAddress'] == m['address']:
                    instancesTable.add_row([g['id'], g['fullyQualifiedDomainName'],
                                            "Virtual Server",m['address'], m['weight']])
            
            for s in servers:
                if s['primaryBackendIpAddress'] == m['address']:
                    instancesTable.add_row([s['id'], s['fullyQualifiedDomainName'],
                                            "Hardware Server",m['address'], m['weight']])

        print (self.title("SERVER INSTANCES:"))
        print (instancesTable)
    
    def printHealthChecks(self, listeners, healthMonitors):
        healthMTable = PrettyTable(['Protocol','Port','Interval (sec)',
                                   'Timeout (sec)', 'Max Trials'])
        # Adding data to the table
        for hm in healthMonitors:
            for l in listeners:
                if l['defaultPool']['healthMonitor']['uuid'] == hm['uuid']:
                    healthMTable.add_row([hm['monitorType'], l['defaultPool']['protocolPort'], 
                                          hm['interval'], hm['timeout'], hm['maxRetries']])

        print (self.title("HEALTH CHECKS:"))
        print (healthMTable)

    def printDetails(self, uuid, servers=False, checks=False):
        """Prints out details of a LBaaS"""
        table = PrettyTable(['ID','UUID','Description', 
                             'Address', 'Type', 'Location', 'Status'])
                        
        try:
            # Retrieve needed LBaaS information by using masks in the getLoadBalancer method
            mask = "mask[members,listeners[defaultPool[healthMonitor]],healthMonitors]"
            lbaas = self.client['Network_LBaaS_LoadBalancer'].getLoadBalancer(uuid, mask=mask)
            
            isPublic = "Public" if lbaas['isPublic'] == 1 else "Private"
            # Adding data to the table
            table.title = "LBaaS Name: " + lbaas['name']
            table.add_row([lbaas['id'], lbaas['uuid'], lbaas['description'], lbaas['address'],
                              isPublic,lbaas['datacenter']['longName'],lbaas['operatingStatus']])

            print (self.title("DETAILS:"))
            print (table)

            self.printOverview(uuid, lbaas['listeners'])

            if servers:
                self.printServerInstances(lbaas['datacenter']['name'], lbaas['members'])
            
            if checks:
                self.printHealthChecks(lbaas['listeners'], lbaas['healthMonitors'])
            
        except SoftLayer.SoftLayerAPIError as e:            
            print("Unable to retrieve LBaaS details: %s, %s" % (e.faultCode, e.faultString))

    def selectProtocol(self, uuid):
        """It prints a table and waits until an option is selected"""
        protocolTable = PrettyTable(['Option','UUID','Front-End Protocol','Front-End Port', 
                                     'Back-End Protocol', 'Back-End Port'])
        protocolTable.title = "PROTOCOLS"
        
        # Retrieve the listeners in LBaaS using masks
        mask = "mask[members,listeners[defaultPool]]"
        lbaas = self.client['Network_LBaaS_LoadBalancer'].getLoadBalancer(uuid, mask=mask)
        listeners = lbaas['listeners']

        # Adding data about health status if there are members
        for l in listeners:
            protocolTable.add_row([str(listeners.index(l)), l['uuid'],
                                   l['protocol'], l['protocolPort'], 
                                   l['defaultPool']['protocol'], 
                                   l['defaultPool']['protocolPort']])

        print (protocolTable)
        var = int(input("\n\nSelect the protocol you want to remove (0 to " + str(len(listeners) - 1) + ") : "))

        return [str(protocolTable._rows[var][1])]

    def removeProtocols(self, uuid, listeners=None):
        """Removes the selected protocols from a LBaaS"""
        if not listeners:
            listeners = self.selectProtocol(uuid)

        try:
            # Create the service and delete selected protocols
            lbaasListenerService = self.client['Network_LBaaS_Listener']
            response = lbaasListenerService.deleteLoadBalancerProtocols(uuid, listeners)

            print(json.dumps(response, sort_keys=True, indent=2, separators=(',', ': ')))

        except (SoftLayer.SoftLayerAPIError, Exception) as e:            
            print("Unable to remove the protocols: %s, %s" % (e.faultCode, e.faultString))

    def listLBaaSItems(self, dc=None):
        """ List all LBaaS items in the account. """
        filter = None
        table = PrettyTable(['ID','UUID','Name', 'Description', 
                             'Address', 'Type', 'Location', 'Status'])
        
        if dc:
            filter = {"datacenter":{"name":{"operation": dc}}}
        
        try:
            # Retrieves all LBaaS in the account
            items = self.client['Network_LBaaS_LoadBalancer'].getAllObjects(filter=filter)
            # Add data to the table and print the list
            for i in items:
                isPublic = "Public" if i['isPublic'] == 1 else "Private"
                table.add_row([i['id'], i['uuid'], i['name'], i['description'], i['address'],
                              isPublic,i['datacenter']['longName'],i['operatingStatus']])

            print (table)

        except SoftLayer.SoftLayerAPIError as e:            
            print("Unable to list all LBaaS in the account: %s, %s" % (e.faultCode, e.faultString))

    def getPackageId(self, pkg):
        """Finds the packageId for the LBaaS"""
        filter = {"keyName":{"operation":pkg}}
        pkg_list = self.client['Product_Package'].getAllObjects(filter=filter)
        return pkg_list[0]['id']

    def getStandardPrices(self,pkg_id):
        """Gets price IDs needed to order a LBaaS"""
        key_names = ["LOAD_BALANCER_BANDWIDTH", "LOAD_BALANCER_DATA_PROCESSED", "LOAD_BALANCER_UPTIME", "LOAD_BALANCER_AS_A_SERVICE"]
                
        items = self.client['Product_Package'].getItems(id=pkg_id)
        prices = []

        for item_keyname in key_names:
            matching_item = [i for i in items
                                 if i['keyName'] == item_keyname][0]
            
            price_id = [p['id'] for p in matching_item['prices']
                            if not p['locationGroupId']][0]
            prices.append(price_id)
        
        return prices

    def setPlan(self, dc=None, pkg="LBAAS"):
        """Sets basic order information"""
        # Retrieve the package id
        pkg_id = self.getPackageId(pkg)
        # We'll retrieve generic prices according to the package
        prices = self.getStandardPrices(pkg_id)
        # Starting building the orderData
        self.orderData['complexType'] = "SoftLayer_Container_Product_Order_Network_LoadBalancer_AsAService"
        self.orderData['location'] = dc
        self.orderData['packageId'] = pkg_id
        self.orderData['useHourlyPricing'] = True            # LBaaS service is only available as an hourly service
        self.orderData['prices'] = [{'id': price_id} for price_id in prices]
    
    def setNetworkSettings(self, is_public=True, ibm_allocation=True, subnet=None):
        """Sets the basic network settings for a LBaaS order"""
        self.orderData['isPublic'] = is_public
        if is_public:            
            self.orderData['useSystemPublicIpPool'] = ibm_allocation
        
        self.orderData['subnets'] = [{"id": subnet}]

    def setBasicAndHealthChecks(self, name=None, desc=None, protocolConfigs=None, health_checks=None):
        """Sets healthchecks for a LBaaS order"""
        self.orderData['name'] = name
        self.orderData['description'] = desc
        self.orderData['protocolConfigurations'] = protocolConfigs
        self.orderData['healthMonitorConfigurations'] = health_checks
    
    def setServerInstances(self, server_instances=None):
        """Sets server instances for a LBaaS order"""
        self.orderData['serverInstancesInformation'] = server_instances
    
    def orderLBaaS(self, placeOrder):
        """Orders a LBaaS instance"""
        try:
            # verifyOrder() will check your order for errors.
            # placeOrder() when you're ready to order. 
            if not placeOrder:
                response = self.client['Product_Order'].verifyOrder(self.orderData)
            else:
                response = self.client['Product_Order'].placeOrder(self.orderData)
            
            print("\nDetailed information below about the order:\n")
            print(json.dumps(response, sort_keys=True, indent=2, separators=(',', ': ')))
        except SoftLayer.SoftLayerAPIError as e:            
            print("Unable to place the order: %s, %s" % (e.faultCode, e.faultString))        

if __name__ == "__main__":
    # Set Name, ID, or UUID for LBaaS
    # lbaasName = "My-LBaaS-Name"
    # lbaasId = 12345
    lbaasUUID = "1f454229-a7b0-4182-8953-975c0a045f79"
    main = lbaas()


    # Set the datacener location where you want to order
    package = "LBAAS"
    datacenter = "MEXICO"

    lbass_name = "My-LBaaS-name"
    description = "A description sample"
    
    # Set False for private network
    isPublic = True
    # Set False if you want to allocate from a Public Subnet, please ensure 
    # that TCP port 56501 is not blocked by a firewall or any other services.
    ibmAllocation = True
    # The private subnet id you want to place the load balancer
    subnetId = 830460                      

    # Protocol Configuration sample
    protocolConfigurations = [{
        "frontendProtocol":"HTTP",
        "frontendPort":80,
        "backendProtocol":"HTTP",
        "backendPort":8080,
        "sslCertificate": 0,                    # ID of SSL Certificate if you have one
        "loadBalancingMethod":"ROUNDROBIN",     # ROUNDROBIN, LEASTCONNECTION, WEIGHTED_RR
        "maxConn":10000
    }]

    # It must have the same number of objects than protocolConfigurations. 
    # LBaaS will be ordered with default values if it is not set.
    healthChecks=[{
        "protocol": "HTTP",
        "port": 80,
        "interval": 5,
        "timeout": 2,
        "maxTrials": 2,
        "path": ""
    }]

    serverInstances = [
        { "privateIpAddress": "10.148.92.136", "weight": 50 },
        { "privateIpAddress": "10.190.112.133", "weight": 100 }
    ]

    main.setPlan(dc=datacenter, pkg=package)    
    main.setNetworkSettings(is_public=isPublic, ibm_allocation=ibmAllocation, subnet=subnetId)
    
    main.setBasicAndHealthChecks(name=lbass_name, desc=description, 
                                 protocolConfigs=protocolConfigurations,
                                 health_checks=healthChecks)
    # Set server instances if you want to assign baremetals and virtual guests
    # during the order
    main.setServerInstances(server_instances=servers)
    main.orderLBaaS(True)

    main.addServerInstances(uuid, serverInstances)

    protocolConfigurations = [
        {
            "backendPort": 1350,
            "backendProtocol": "TCP",
            "frontendPort": 1450,
            "frontendProtocol": "TCP",
            "loadBalancingMethod": "WEIGHTED_RR",    # ROUNDROBIN, LEASTCONNECTION, WEIGHTED_RR
            "maxConn": 500,
            "sessionType": "SOURCE_IP"
        },
        {
            "backendPort": 1200,
            "backendProtocol": "HTTP",
            "frontendPort": 1150,
            "frontendProtocol": "HTTP",
            "loadBalancingMethod": "ROUNDROBIN",    # ROUNDROBIN, LEASTCONNECTION, WEIGHTED_RR
            "maxConn": 500,
            "sessionType": "SOURCE_IP"
        }
    ]
    main.addProtocols(uuid, protocolConfigurations)
    main.printDetails(uuid, servers=True, checks=True)
    listenerUuids = ["1d72f3cc-7903-484e-b5c0-507fd81ab852",
                     "a1908b08-1ef2-4fe4-bac3-f433589ba3cb"]
    main.removeProtocols(uuid, listeners=listenerUuids)

    # Use SoftLayer_Network_LBaaS_LoadBalancer::getMembers or
    # SoftLayer_Network_LBaaS_LoadBalancer::getLoadBalancer with masks
    # to retrive member uuid values
    memberUuids = ["ed25c408-4b26-4f22-ba1f-32b6af7e36da",
                   "ba9d6df6-c4e6-46a3-9a11-1e901de37932"]
    main.removeServerInstances(uuid, members=memberUuids)
    main.cancelLBaaS(lbaasUUID)
    
    # With following you can cancel it through the its name or id
    # main.cancelLBaaSById(lbaasId)
    # main.cancelLBaaSByName(lbaasName)