Ejemplo n.º 1
0
 def __init__(self, lbresource, extensions_enabled=False):
     self.lbresource = lbresource
     self.lbservice = lbresource.lbservice 
     self.connmanager = self.lbservice.connmanager
     self.logger = self.lbservice.logger 
     self.extensions_enabled = extensions_enabled
     self.db = NetScalerLBServiceDB(self.lbservice)
 def __init__(self, lbservice, pools_config):
     self.lbservice = lbservice
     self.logger = self.lbservice.logger
     self.vip_pools = {}
     """ We can prepopulate it with the known ones """
     self._configure_pool("PUBLIC", pools_config)
     self._configure_pool("SERVICENET", pools_config)
     self.db = NetScalerLBServiceDB(self.lbservice)
class NitroLoadBalancerAdapter(object):
    def __init__(self, lbresource, extensions_enabled=False):

        self.lbresource = lbresource
        self.lbservice = self.lbresource.lbservice
        self.connmanager = self.lbservice.connmanager
        self.logger = self.lbservice.logger
        self.extensions_enabled = extensions_enabled
        self.db = NetScalerLBServiceDB(self.lbservice)

    def _adjust_lbvserver_state_for_update(self, loadBalancerId, new_state):
        """ First, we'll get from Netscaler the current lbvserver state """
        """ XXX - In Netscaler, we haven't got IDs for resources so our assumption: loadBalancerId == loadBalancer.name """

        resource_type = "lbvserver"
        resource_name = NitroUtils.get_lbvservername_from_loadbalancerid(
            loadBalancerId)

        self.logger.debug(
            "adjusting the lbvserver state to submit for an update operation. For this we need to get the existing lbvserver"
        )
        current_state = self.nitrowrapper.get_netscaler_entity(
            resource_type, resource_name)

        if "servicetype" in new_state.keys():
            if new_state["servicetype"] != current_state["servicetype"]:
                """ XXX - Netscaler doesn't support changing the servicetype of an lbvserver. """
                raise NotImplementedException(
                    "Sorry, changing the protocol of the loadBalancer in the current implementation is not supported."
                )

            else:
                """ Just remove it since it is the same """
                del new_state["servicetype"]

        if "port" in new_state.keys():
            if type(current_state["port"]) is int:
                new_state["port"] = int(new_state["port"])

            if new_state["port"] != current_state["port"]:
                """ XXX - Netscaler doesn't support changing the port of an lbvserver. """
                raise NotImplementedException(
                    "Sorry, changing the port of the loadBalancer in the current implementation is not supported."
                )

            else:
                del new_state["port"]

        if "lbmethod" in new_state.keys():
            if new_state["lbmethod"] == current_state["lbmethod"]:
                del new_state["lbmethod"]

        if self.extensions_enabled:
            """ XXX - Also check for extended properties in the new_state dictionary """


        self.logger.debug("Adjusted state for Updating lbvserver %s is: %s" %
                          (resource_name, str(new_state)))

        return new_state

    def _get_new_loadBalancer_object(self):

        return NetScalerLoadBalancerState(self.lbresource)

    def _get_loadbalancer_object_from_lbvserver_entity(self, loadBalancerId,
                                                       dict_obj):

        lbvserver_name = str(dict_obj["name"])

        expected_name = NitroUtils.get_lbvservername_from_loadbalancerid(
            loadBalancerId)

        if lbvserver_name != expected_name:
            raise ImplementationError("resource returned not expected...")

        loadBalancer = self._get_new_loadBalancer_object()

        loadBalancer.name = NitroUtils.get_loadBalancer_name(loadBalancerId)

        servicetype = str(dict_obj["servicetype"])

        loadBalancer.protocol = NitroUtils.get_protocol_from_servicetype(
            servicetype)

        port = str(dict_obj["port"])
        loadBalancer.port = port

        lbmethod = str(dict_obj["lbmethod"])
        loadBalancer.algorithm = NitroUtils.get_algorithm_from_lbmethod(
            lbmethod)

        effectivestate = str(dict_obj["effectivestate"])
        loadBalancer.status = NitroUtils.get_loadbalancerstatus_from_state(
            effectivestate)

        i = 1

        loadBalancer.virtualIps = []

        virtualIP = LBVirtualIPState(self.lbresource)

        ipaddress = str(dict_obj["ipv46"])

        virtualIP.address = ipaddress

        self.logger.debug("VIP for loadbalancer %s is %s" %
                          (loadBalancer.name, ipaddress))

        loadBalancer.virtualIps.append(virtualIP)
        """ Get Session Persistence data """
        persistencetype = str(dict_obj["persistencetype"])

        self.logger.debug("persistencetype of lbvserver is %s" %
                          persistencetype)

        if persistencetype != 'NONE':
            if self.extensions_enabled:
                sessionPersistence = NetScalerLBSessionPersistenceState(
                    self.lbresource)
            else:
                sessionPersistence = LBSessionPersistenceState(self.lbresource)

            persistencevalue = NitroUtils.get_persistencetype_from_nspersistencetype(
                persistencetype)

            if persistencevalue:
                sessionPersistence.persistenceType = persistencevalue

            if self.extensions_enabled:
                timeout = str(dict_obj["timeout"])
                if timeout != None:
                    sessionPersistence.timeout = timeout

            loadBalancer.sessionPersistence = sessionPersistence
        """ Get Connection Logging data """
        """
        if self.extensions_enabled:
    	    loadBalancer.connectionLogging = NetScalerLBConnectionLoggingState(self.lbresource)
        else:
    	    loadBalancer.connectionLogging = LBConnectionLoggingState(self.lbresource)


        appflow = str(dict_obj["appflowlog"])

        if appflow == "ENABLED":
            loadBalancer.connectionLogging.enabled="true"
        else:
            loadBalancer.connectionLogging.enabled="false"

        loadBalancer.connectionThrottle = LBConnectionThrottleState(self.lbresource)

        loadBalancer.connectionThrottle.minConnections = "10"
        loadBalancer.connectionThrottle.maxConnections = "100"
        loadBalancer.connectionThrottle.maxConnectionRate = "50"
        loadBalancer.connectionThrottle.rateInterval = "60"
        """

        if self.lbservice.cluster:
            loadBalancer.cluster = {}
            loadBalancer.cluster["name"] = self.lbservice.cluster
        else:
            loadBalancer.cluster = {}
            loadBalancer.cluster["name"] = "none"

        if self.extensions_enabled:

            sc = str(dict_obj["sc"])

            if sc == "ON":
                loadBalancer.sureConnect = "true"
            else:
                loadBalancer.sureConnect = "false"
            """
            if appflow == "ENABLED":
                loadBalancer.connectionLogging.type="appflow"
            """

        return loadBalancer

    def _extract_loadbalancer(self, results, loadBalancerId):

        lbvserver = NitroTasks.extract_lbvserver_object_from_task_list(
            results, loadBalancerId)

        self.logger.debug("lbvserver object is: %s" % str(lbvserver))

        loadBalancer = self._get_loadbalancer_object_from_lbvserver_entity(
            loadBalancerId, lbvserver)

        return loadBalancer

    def _extract_nodes(self, results, loadBalancerId, loadBalancer):

        servicebindings = NitroTasks.extract_servicebindings_from_task_list(
            results)

        if servicebindings:
            task_list = self.nodeadapter.get_netscaler_getservices_tasks(
                loadBalancerId, loadBalancer, servicebindings)

            results = self.nitrowrapper.process_netscaler_task_list(task_list)

            services = NitroTasks.extract_services_from_task_list(
                results, servicebindings)
        else:
            services = None

        if services:
            return self.nodeadapter.get_netscaler_nodes_from_services_and_servicebindings(
                loadBalancer.id, loadBalancer, services, servicebindings)
        else:
            return None

    def _extract_healthmonitor(self, results, loadBalancerId, loadBalancer):

        monitor_name = NitroUtils.get_monitorname_from_loadBalancerId(
            loadBalancerId)

        monitor = NitroTasks.extract_monitor_from_task_list(
            results, monitor_name)

        if monitor:
            healthMonitor = self.monitoradapter.get_netscaler_healthmonitor_from_monitor(
                loadBalancerId, loadBalancer, monitor)

            return healthMonitor
        else:
            return None

    def _set_netscaler_lbvserver_vipaddress(self, lbvserver, virtualIps):

        if not virtualIps:
            return

        virtualIp = virtualIps[0]

        lbvserver["ipv46"] = virtualIp.address

        return lbvserver

    def _set_netscaler_lbvserver_persistencetype(self, lbvserver,
                                                 sessionPersistence):

        if "persistenceType" in sessionPersistence:
            lbvserver[
                "persistencetype"] = NitroUtils.get_nspersistencetype_from_persistencetype(
                    sessionPersistence.persistenceType)

        if self.extensions_enabled:
            if "timeout" in sessionPersistence:
                lbvserver["timeout"] = sessionPersistence.timeout

    def _process_loadBalancer_extensions(self, loadBalancer, key, lbvserver):

        if not self.extensions_enabled:
            return False

        if key == "sureConnect":
            sureConnect = loadBalancer.sureConnect
            lbvserver["sc"] = NitroUtils.get_nssureconnect_from_sureconnect(
                sureConnect)
            return True

        return False

    def _get_netscaler_servicebindings_of_loadBalancer(self, loadBalancerId):

        task = self.nodeadapter.get_netscaler_getservicebindings_task(
            loadBalancerId, None)

        task_list.append(task)

        results = self.nitrowrapper.process_netscaler_task_list(task_list)

        servicebindings = NitroTasks.extract_servicebindings_from_task_list(
            results)

        return servicebindings

    def _get_netscaler_servicenames_of_loadBalancer(self, loadBalancerId):
        """ We need to get the servicebindings to know the names of the services from this loadBalancer """

        servicebindings = NitroUtils.get_servicebindings_of_loadBalancer(
            self, loadBalancerId)

        servicenames = NitroUtils.get_servicenames_from_servicebindings(
            servicebindings)

        return servicenames

    def _get_lbvserver_state(self, lbvserver_name, loadBalancer):

        lbvserver = {}

        lbvserver["name"] = lbvserver_name

        for key in loadBalancer:
            self.logger.debug("Examining key \"%s\" of loadBalancer payload" %
                              key)

            if key == "_properties":
                continue

            if key == "protocol":
                lbvserver[
                    "servicetype"] = NitroUtils.get_servicetype_from_protocol(
                        loadBalancer.protocol)
                """ XXX - We also need to modify the protocol of all the services of this lbvserver!! """
                continue

            if key == "port":
                lbvserver["port"] = loadBalancer.port
                continue

            if key == "algorithm":
                algorithm = loadBalancer.algorithm
                lbvserver["lbmethod"] = NitroUtils.get_lbmethod_from_algorithm(
                    algorithm)
                continue

            if key == "virtualIps":
                virtualIps = loadBalancer.virtualIps
                self._set_netscaler_lbvserver_vipaddress(lbvserver, virtualIps)
                continue

            if key == "sessionPersistence":
                sessionPersistence = loadBalancer.sessionPersistence
                self._set_netscaler_lbvserver_persistencetype(
                    lbvserver, sessionPersistence)
                continue

            if self._process_loadBalancer_extensions(loadBalancer, key,
                                                     lbvserver):
                continue

        return lbvserver

    def _get_netscaler_addloadbalancer_task(self, loadBalancerId,
                                            loadBalancer):

        task_list = []

        if not "name" in loadBalancer:
            raise ImplementationError(
                "We should have caught this during validation phase.")

        lbvserver_name = NitroUtils.get_lbvservername_from_loadbalancerid(
            loadBalancerId)

        task = {}
        task["type"] = "lbvserver"
        task["name"] = lbvserver_name
        task["operation"] = "ADD"

        lbvserver = self._get_lbvserver_state(lbvserver_name, loadBalancer)
        task["state"] = lbvserver
        task_list.append(task)

        return task

    def _get_netscaler_updateloadbalancer_task(self,
                                               loadBalancerId,
                                               loadBalancer,
                                               newLoadBalancerId=None):

        task_list = []

        self.logger.debug("About to get lbvserver state")

        if newLoadBalancerId:
            lbvserver_name = NitroUtils.get_lbvservername_from_loadbalancerid(
                newLoadBalancerId)
        else:
            lbvserver_name = NitroUtils.get_lbvservername_from_loadbalancerid(
                loadBalancerId)

        lbvserver = self._get_lbvserver_state(lbvserver_name, loadBalancer)

        lbvserver = self._adjust_lbvserver_state_for_update(
            loadBalancerId, lbvserver)

        if not lbvserver:
            return None

        if len(lbvserver.keys()) <= 1:
            """ There is no field to update on the lbvserver, so no need for an update task """
            return None

        task = {}
        task["type"] = "lbvserver"
        task["name"] = lbvserver_name
        task["operation"] = "UPDATE"
        task["state"] = lbvserver

        return task

    def _get_netscaler_renameloadbalancer_task(self, loadBalancerId,
                                               newLoadBalancerId,
                                               loadBalancer):

        if not all([loadBalancerId, newLoadBalancerId, loadBalancer
                    ]) or not "name" in loadBalancer:
            raise ImplementationError("programming error")

        existing_name = NitroUtils.get_lbvservername_from_loadbalancerid(
            loadBalancerId)
        new_name = NitroUtils.get_lbvservername_from_loadbalancerid(
            newLoadBalancerId)

        task = {}
        task["name"] = existing_name
        task["type"] = "lbvserver"
        task["operation"] = "RENAME"

        obj = {}
        obj["name"] = existing_name
        obj["newname"] = new_name

        task["state"] = obj

        return task

    def _get_netscaler_updatenode_protocol_task(self, loadBalancerId,
                                                loadBalancer, node,
                                                newLoadBalancerId):

        servicename = NitroUtils.get_servicename_from_nodeid(
            loadBalancerId, node.id)

        if not "protocol" in loadBalancer:
            return None

        lbprotocol = loadBalancer.protocol

        servicetype = NitroUtils.get_servicetype_from_protocol(lbprotocol)
        servicetype = NitroUtils.get_decrypted_version_of_servicetype(
            servicetype)

        service = {}
        service["name"] = servicename
        service["protocol"] = servicetype

        task = {}
        task["name"] = servicename
        task["type"] = "service"
        task["operation"] = "UPDATE"
        task["state"] = service

        return task

    def _get_netscaler_updatenode_weight_tasks(self, loadBalancerId,
                                               loadBalancer, node,
                                               newLoadBalancerId):

        if not "algorithm" in loadBalancer:
            return None

        if loadBalancer.algorithm.startswith("WEIGHTED_"):
            return None

        newnode = self.lbresource.GetLBNode().GetStateObject()
        newnode.id = node.id
        newnode.weight = 1

        task_list = []

        task = self.nodeadapter.get_netscaler_removeservicebinding_task(
            loadBalancerId, loadBalancer, node.id)
        task_list.append(task)

        if newLoadBalancerId:
            task = self.nodeadapter.get_netscaler_addservicebinding_task(
                newLoadBalancerId, loadBalancer, node.id, newnode)
        else:
            task = self.nodeadapter.get_netscaler_addservicebinding_task(
                loadBalancerId, loadBalancer, node.id, newnode)

        task_list.append(task)

        return task_list

    def _get_netscaler_updatenode_tasks(self, loadBalancerId, loadBalancer,
                                        node, newLoadBalancerId):

        self.logger.debug("Creating update tasks for node %s" % node.id)

        task_list = []
        task = self._get_netscaler_updatenode_protocol_task(
            loadBalancerId, loadBalancer, node, newLoadBalancerId)

        if task:
            task_list.append(task)

        self.logger.debug("Created an update task for node %s protocol" %
                          node.id)

        tasks = self._get_netscaler_updatenode_weight_tasks(
            loadBalancerId, loadBalancer, node, newLoadBalancerId)

        if tasks:
            task_list.extend(tasks)

        self.logger.debug("Created an update task for node %s weight" %
                          node.id)

        return task_list

    def _get_netscaler_updatenodes_tasks(self,
                                         loadBalancerId,
                                         loadBalancer,
                                         newLoadBalancerId=None):

        task_list = []
        """ Let's check if there is a reason we need to update the services of the loadBalancer """
        """ Currently, only a change in the protocol or algorithm property of the loadBalancer requires adjusting the 
            protocol(servicetype) property of the services """

        if not "protocol" in loadBalancer and not "algorithm" in loadBalancer:
            return task_list
        """servicenames = self._get_netscaler_servicenames_of_loadBalancer(loadBalancerId)"""

        nodes = self.nodeadapter._list_LBNodesInternal(loadBalancerId,
                                                       loadBalancer)

        self.logger.debug("Retrieved nodes of loadbalancer")

        for node in nodes:
            tasks = self._get_netscaler_updatenode_tasks(
                loadBalancerId, loadBalancer, node, newLoadBalancerId)
            task_list.extend(tasks)

        return task_list

    def _get_netscaler_removeloadbalancer_task(self, loadBalancerId):

        lbvserver_name = NitroUtils.get_lbvservername_from_loadbalancerid(
            loadBalancerId)

        task = {}
        task["type"] = "lbvserver"
        task["name"] = lbvserver_name
        task["operation"] = "REMOVE"
        task["state"] = None

        return task

    def _get_netscaler_getnodes_tasks(self, loadBalancerId):

        task_list = []

        servicebindings_task = self.nodeadapter.get_netscaler_getservicebindings_task(
            loadBalancerId, None)

        if servicebindings_task:
            task_list.append(servicebindings_task)

        return task_list

    def _get_netscaler_addnodes_tasks(self, loadBalancerId, loadBalancer):

        tasks = []

        if "nodes" in loadBalancer:
            nodes = loadBalancer.nodes

            tasks = self.nodeadapter.get_netscaler_addnodes_tasks(
                loadBalancerId, loadBalancer, nodes)

        return tasks

    def _get_netscaler_removenodes_tasks(self, loadBalancerId, loadBalancer):

        node_tasks = self.nodeadapter.get_netscaler_removeboundservices_tasks(
            loadBalancerId, loadBalancer)

        return node_tasks

    def _get_netscaler_gethealthmonitor_tasks(self, loadBalancerId):

        task_list = self.monitoradapter.get_netscaler_gethealthmonitor_tasks(
            loadBalancerId, None)

        return task_list

    def _get_netscaler_addhealthmonitor_tasks(self,
                                              loadBalancerId,
                                              loadBalancer,
                                              newLoadBalancerId=None):

        task_list = []

        if not "healthMonitor" in loadBalancer:
            return task_list

        healthMonitor = loadBalancer.healthMonitor

        monitor_tasks = self.monitoradapter.get_netscaler_loadbalancer_addhealthmonitor_tasks(
            loadBalancerId, loadBalancer, healthMonitor)

        return monitor_tasks

    def _get_netscaler_removehealthmonitor_tasks(self, loadBalancerId,
                                                 loadBalancer):

        self.logger.debug(
            "Getting tasks to remove health monitor from loadbalancer")

        task_list = self.monitoradapter.get_netscaler_removehealthmonitor_tasks(
            loadBalancerId, loadBalancer)

        self.logger.debug("task list to remove health monitor: %s" %
                          repr(task_list))

        return task_list

    def _get_netscaler_getconnectionlogging_tasks(self, loadBalancerId):

        pass

    def _get_netscaler_addconnectionlogging_tasks(self,
                                                  loadBalancerId,
                                                  loadBalancer,
                                                  newLoadBalancerId=None):

        task_list = []

        if not "connectionLogging" in loadBalancer:
            return task_list

        raise NotImplementedException(
            "connectionLogging property is not currently supported for loadBalancer."
        )

    def _get_netscaler_updateconnectionlogging_tasks(self,
                                                     loadBalancerId,
                                                     loadBalancer,
                                                     newLoadBalancerId=None):

        pass

    def _get_netscaler_removeconnectionlogging_tasks(self, loadBalancerId):

        pass

    def _get_netscaler_getconnectionthrottle_tasks(self, loadBalancerId):

        pass

    def _get_netscaler_addconnectionthrottle_tasks(self,
                                                   loadBalancerId,
                                                   loadBalancer,
                                                   newLoadBalancerId=None):

        task_list = []

        if not "connectionThrottle" in loadBalancer:
            return task_list

        raise NotImplementedException(
            "connectionThrottle property is not currently supported for loadBalancer."
        )

    def _get_netscaler_updateconnectionthrottle_tasks(self,
                                                      loadBalancerId,
                                                      loadBalancer,
                                                      newLoadBalancerId=None):

        pass

    def _get_netscaler_removeconnectionthrottle_tasks(self, loadBalancerId):

        pass

    def _get_netscaler_tasks_for_get_op(self, loadBalancerId):

        task_list = []

        if not loadBalancerId:
            raise ImplementationErrorException("Programming error")

        task = NitroTasks.get_netscaler_getlbvserver_task(loadBalancerId)
        task_list.append(task)

        tasks = self._get_netscaler_getnodes_tasks(loadBalancerId)
        task_list.extend(tasks)

        tasks = self._get_netscaler_gethealthmonitor_tasks(loadBalancerId)
        """ We don't care if there is no healthmonitor set on the load balancer """

        if tasks:
            for task in tasks:
                task["ignore_notfound"] = True

        task_list.extend(tasks)
        """ XXX - We should get all info about the loadbalancer, e.g. connection logging, etc. """
        """ 
        logging_tasks = self._get_netscaler_getlogging_tasks(loadBalancer, lbvserver)
        task_list.extend(logging_tasks)
           
        throttle_tasks = self._get_netscaler_getthrottle_tasks(loadBalancer, lbvserver)
        task_list.extend(throttle_tasks)
        """

        return task_list

    def _get_netscaler_tasks_for_add_op(self, loadBalancerId, loadBalancer):

        task_list = []

        if not all([loadBalancerId, loadBalancer]):
            raise ImplementationErrorException("Programming error")

        task = self._get_netscaler_addloadbalancer_task(
            loadBalancerId, loadBalancer)

        task_list.append(task)
        lbvserver = task["state"]

        nodes_tasks = self._get_netscaler_addnodes_tasks(
            loadBalancerId, loadBalancer)
        task_list.extend(nodes_tasks)

        monitor_tasks = self._get_netscaler_addhealthmonitor_tasks(
            loadBalancerId, loadBalancer)
        task_list.extend(monitor_tasks)

        logging_tasks = self._get_netscaler_addconnectionlogging_tasks(
            loadBalancerId, loadBalancer)
        task_list.extend(logging_tasks)

        throttle_tasks = self._get_netscaler_addconnectionthrottle_tasks(
            loadBalancerId, loadBalancer)
        task_list.extend(throttle_tasks)

        return task_list

    def _get_netscaler_tasks_for_update_op(self, loadBalancerId,
                                           updatedLoadBalancer):

        self.logger.debug("Assembling required tasks for update operation")

        task_list = []

        if not all([loadBalancerId, updatedLoadBalancer]):
            raise ImplementationErrorException("Programming error")

        newLoadBalancerId = None

        if "name" in updatedLoadBalancer:

            existing_name = NitroUtils.get_loadBalancer_name(loadBalancerId)

            if updatedLoadBalancer.name != existing_name:
                self.logger.debug("We need to queue a name change task")
                """ Because the internal ID depends on the name, a name change results in a new ID """
                newLoadBalancerId = NitroUtils.get_new_internal_loadBalancerId(
                    loadBalancerId, updatedLoadBalancer)

                namechange_task = self._get_netscaler_renameloadbalancer_task(
                    loadBalancerId, newLoadBalancerId, updatedLoadBalancer)
                task_list.append(namechange_task)

        self.logger.debug("Adding an lbvserver update task")
        task = self._get_netscaler_updateloadbalancer_task(
            loadBalancerId, updatedLoadBalancer, newLoadBalancerId)
        task_list.append(task)
        """ We also may need to change the services and other affected Netscaler resources """
        tasks = self._get_netscaler_updatenodes_tasks(loadBalancerId,
                                                      updatedLoadBalancer,
                                                      newLoadBalancerId)
        task_list.extend(tasks)

        return task_list

    def _get_netscaler_tasks_for_remove_op(self, loadBalancerId, loadBalancer):

        task_list = []

        if not loadBalancerId:
            raise ImplementationErrorException("Programming error")

        self.logger.debug(
            "Getting all tasks to remove resources of loadbalancer")

        task = self._get_netscaler_removeloadbalancer_task(loadBalancerId)
        task_list.append(task)

        tasks = self._get_netscaler_removenodes_tasks(loadBalancerId,
                                                      loadBalancer)
        task_list.extend(tasks)

        tasks = self._get_netscaler_removehealthmonitor_tasks(
            loadBalancerId, loadBalancer)
        task_list.extend(tasks)
        """ XXX - We should also remove other resources. """
        """ 
        logging_tasks = self._get_netscaler_removeconnectionlogging_tasks(loadBalancer, lbvserver)
        task_list.extend(logging_tasks)
           
        throttle_tasks = self._get_netscaler_removeconnectionthrottle_tasks(loadBalancer, lbvserver)
        task_list.extend(throttle_tasks)
        """

        return task_list

    def _get_netscaler_tasks(self,
                             operation,
                             loadBalancerId,
                             loadBalancer=None):

        if operation == "GET":
            return self._get_netscaler_tasks_for_get_op(loadBalancerId)

        if operation == "ADD":
            return self._get_netscaler_tasks_for_add_op(
                loadBalancerId, loadBalancer)

        if operation == "UPDATE":
            return self._get_netscaler_tasks_for_update_op(
                loadBalancerId, loadBalancer)

        if operation == "REMOVE":
            return self._get_netscaler_tasks_for_remove_op(
                loadBalancerId, loadBalancer)
        """ We should never get here """
        raise ImplementationError("programming error")

    def _get_loadBalancer_from_results(self, results):
        return None

    def _allocate_loadBalancer_vip(self, virtualIP, tenant_id, loadBalancerId):

        if getattr(virtualIP, "type") != None:
            viptype = virtualIP.type
            virtualIP.address = self.lbservice.vippool_manager.allocate_virtualIP(
                viptype, tenant_id, loadBalancerId)
            self.logger.debug("VIP allocated is: %s" % virtualIP.address)
            return

        if getattr(virtualIP, "id") != None:
            vipid = virtualIP.id
            virtualIP.address = self.lbservice.vippool_manager.share_virtualIP(
                vipid, tenant_id, loadBalancerId)
            self.logger.debug("VIP retrieved by id is: %s" % virtualIP.address)
            return

    def _allocate_loadBalancer_vips(self, loadBalancer, tenant_id,
                                    loadBalancerId):

        if not loadBalancer.virtualIps:
            raise BadRequestException("validation fault",
                                      "object is not valid",
                                      "No virtual IP field specified.")

        if len(loadBalancer.virtualIps) > 1:
            raise NotImplementedException(
                "Not Implemented. " +
                "Sorry, this loadBalancer service currently allow only for one virtual Ip per loadBalancer"
            )

        for virtualIP in loadBalancer.virtualIps:
            self._allocate_loadBalancer_vip(virtualIP, tenant_id,
                                            loadBalancerId)

    def _get_LoadBalancerInternal(self, loadBalancerId):

        task_list = self._get_netscaler_tasks("GET", loadBalancerId)

        self.logger.debug("get task list: " + str(task_list))

        results = self.nitrowrapper.process_netscaler_task_list(task_list)

        self.logger.debug("results from get task list: " + str(results))

        loadBalancer = self._extract_loadbalancer(results, loadBalancerId)

        loadBalancer.id = loadBalancerId

        loadBalancer.nodes = self._extract_nodes(results, loadBalancerId,
                                                 loadBalancer)

        loadBalancer.healthMonitor = self._extract_healthmonitor(
            results, loadBalancerId, loadBalancer)
        """ 
        " XXX - Still TODO "
        loadBalancer.connectionLogging = self._extract_connectionlogging(results, loadBalancerId, loadBalancer) 

        loadBalancer.connectionThrottle = self._extract_connectionthrottle(results, loadBalancerId, loadBalancer) 

        """

        return loadBalancer

    def _add_LoadBalancerInternal(self, loadBalancerId, newLoadBalancer):

        task_list = self._get_netscaler_tasks("ADD", loadBalancerId,
                                              newLoadBalancer)

        self.logger.debug("add task list: " + str(task_list))

        if task_list:
            self.nitrowrapper.process_netscaler_task_list(task_list)

    def _update_LoadBalancerInternal(self, loadBalancerId,
                                     updatedLoadBalancer):

        task_list = self._get_netscaler_tasks("UPDATE", loadBalancerId,
                                              updatedLoadBalancer)

        self.logger.debug("update task list: " + str(task_list))

        if task_list:
            self.nitrowrapper.process_netscaler_task_list(task_list)

    def _remove_LoadBalancerInternal(self, loadBalancerId, loadBalancer):

        task_list = self._get_netscaler_tasks("REMOVE", loadBalancerId,
                                              loadBalancer)

        self.logger.debug("remove task list: " + str(task_list))

        if task_list:
            self.nitrowrapper.process_netscaler_task_list(task_list)

    def _setup_nitrowrapper(self, tenant_id, loadBalancerId=None):

        connection = self.connmanager.get_nitro_connection(
            tenant_id, loadBalancerId)
        self.nitrowrapper = NitroCallsWrapper.get_nitrowrapper(
            connection, self.logger)

        self.monitoradapter = NitroLBHealthMonitorAdapter(
            self.lbresource, self.extensions_enabled)
        self.monitoradapter.nitrowrapper = self.nitrowrapper
        self.monitoradapter.usermonitor = connection.monitor_script

        self.nodeadapter = NitroLBNodeAdapter(self.lbresource,
                                              self.extensions_enabled)
        self.nodeadapter.nitrowrapper = self.nitrowrapper
        self.nodeadapter.monitoradapter = self.monitoradapter

    def get_LoadBalancer(self, loadBalancerId, tenant_id):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        dbLoadBalancer = self._get_new_loadBalancer_object()

        dbLoadBalancer = self.db.fill_loadBalancer_object(
            tenant_id, loadBalancerId, dbLoadBalancer)

        if not dbLoadBalancer:
            raise ItemNotFoundException(
                "No loadBalancer with id %s was found" % loadBalancerId)

        if dbLoadBalancer.status == "DELETED":
            dict_obj = dbLoadBalancer.get_as_dictionary()
            del dict_obj["protocol"]
            del dict_obj["algorithm"]

            return dbLoadBalancer

        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(
            tenant_id, loadBalancerId, dbLoadBalancer)

        loadBalancer = self._get_LoadBalancerInternal(internal_loadBalancerId)

        loadBalancer.id = dbLoadBalancer.id
        """ XXX- We should check whether the values in the DB are different from those returned from NetScaler
                 and change the DB to match what is on NetScaler (NetScaler is the authoritative source)
        """

        loadBalancer.protocol = dbLoadBalancer.protocol
        loadBalancer.algorithm = dbLoadBalancer.algorithm
        loadBalancer.created = dbLoadBalancer.created
        loadBalancer.updated = dbLoadBalancer.updated

        self.db.fill_loadBalancer_vips_info(tenant_id, loadBalancer.id,
                                            loadBalancer)

        return loadBalancer

    def add_LoadBalancer(self, newLoadBalancer, tenant_id):

        self.logger.debug("adding loadbalancer %s" % repr(newLoadBalancer))

        existing_ids = self.db.get_tenant_loadBalancers(tenant_id)
        loadBalancerId = str(NitroUtils.generate_loadbalancerid(existing_ids))

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        self._allocate_loadBalancer_vips(newLoadBalancer, tenant_id,
                                         loadBalancerId)

        internalLoadBalancer = newLoadBalancer
        internalLoadBalancerId = NitroUtils.get_internal_loadBalancerId(
            tenant_id, loadBalancerId, internalLoadBalancer)

        self._add_LoadBalancerInternal(internalLoadBalancerId,
                                       internalLoadBalancer)

        loadBalancer = self._get_LoadBalancerInternal(internalLoadBalancerId)

        loadBalancer.id = loadBalancerId
        loadBalancer.protocol = internalLoadBalancer.protocol
        loadBalancer.algorithm = internalLoadBalancer.algorithm

        self.db.create_loadBalancer(tenant_id, loadBalancer)

        self.db.fill_loadBalancer_vips_info(tenant_id, loadBalancer.id,
                                            loadBalancer)

        return loadBalancer

    def add_LoadBalancers(self, loadBalancers, tenant_id):

        response_loadBalancers = []

        for loadBalancer in loadBalancers:
            resp_item = self.add_LoadBalancer(loadBalancer, tenant_id)
            response_loadBalancers.append(resp_item)

        return response_loadBalancers

    def update_LoadBalancer(self, loadBalancerId, updatedLoadBalancer,
                            tenant_id):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        dbLoadBalancer = self._get_new_loadBalancer_object()

        dbLoadBalancer = self.db.fill_loadBalancer_object(
            tenant_id, loadBalancerId, dbLoadBalancer)

        if not dbLoadBalancer:
            raise ItemNotFoundException(
                "No loadBalancer with id %s was found" % loadBalancerId)

        if dbLoadBalancer.status == "DELETED":
            raise ImmutableEntityException(
                "loadBalancer with id %s was deleted and cannot be updated" %
                loadBalancerId)

        internalLoadBalancer = updatedLoadBalancer

        internalLoadBalancerId = NitroUtils.get_internal_loadBalancerId(
            tenant_id, loadBalancerId, dbLoadBalancer)

        self._update_LoadBalancerInternal(internalLoadBalancerId,
                                          internalLoadBalancer)

        dbLoadBalancer = self.db.update_loadBalancer(tenant_id, loadBalancerId,
                                                     internalLoadBalancer)

        internalLoadBalancerId = NitroUtils.get_internal_loadBalancerId(
            tenant_id, loadBalancerId, dbLoadBalancer)

        loadBalancer = self._get_LoadBalancerInternal(internalLoadBalancerId)

        loadBalancer.id = dbLoadBalancer.id
        loadBalancer.protocol = dbLoadBalancer.protocol
        loadBalancer.algorithm = dbLoadBalancer.algorithm
        loadBalancer.created = dbLoadBalancer.created
        loadBalancer.updated = dbLoadBalancer.updated

        self.db.fill_loadBalancer_vips_info(tenant_id, loadBalancer.id,
                                            loadBalancer)

        self.logger.debug(
            "Successfully completed update loadbalancer operation on the adapter"
        )

        return loadBalancer

    def remove_LoadBalancer(self, loadBalancerId, tenant_id):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        loadBalancer = self.get_LoadBalancer(loadBalancerId, tenant_id)

        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(
            tenant_id, loadBalancerId, loadBalancer)

        self._remove_LoadBalancerInternal(internal_loadBalancerId,
                                          loadBalancer)

        self.db.remove_loadBalancer(tenant_id, loadBalancerId)

    def list_LoadBalancers(self, tenant_id):

        self._setup_nitrowrapper(tenant_id)

        resource_type = "lbvserver"

        loadBalancers = []
        """ First get active load-balancers """
        loadBalancerIds = self.db.get_tenant_activeloadBalancers(tenant_id)

        self.logger.debug("active loadBalancer list is: %s" %
                          str(loadBalancerIds))

        for loadBalancerId in loadBalancerIds:
            loadBalancer = self.get_LoadBalancer(loadBalancerId, tenant_id)
            loadBalancers.append(loadBalancer)
        """ Get deleted load-balancers """
        loadBalancerIds = self.db.get_tenant_deletedloadBalancers(tenant_id)

        self.logger.debug("Deleted loadBalancer list is: %s" %
                          str(loadBalancerIds))

        for loadBalancerId in loadBalancerIds:
            loadBalancer = self.get_LoadBalancer(loadBalancerId, tenant_id)
            loadBalancers.append(loadBalancer)

        return loadBalancers
class NetScalerVirtualIPPool(object):
    def __init__(self, lbservice, pools_config):
        self.lbservice = lbservice
        self.logger = self.lbservice.logger
        self.vip_pools = {}
        """ We can prepopulate it with the known ones """
        self._configure_pool("PUBLIC", pools_config)
        self._configure_pool("SERVICENET", pools_config)
        self.db = NetScalerLBServiceDB(self.lbservice)

    def _configure_pool(self, viptype, pools_config):

        if not pools_config:
            raise LoadBalancerFaultException(
                "Error in configuring Virtual IPs of type %s" % viptype)

        for vip_pool in pools_config:
            if vip_pool["name"].lower() == viptype.lower():
                pool_config = vip_pool["config"]
                break
        else:
            raise BadRequestException(
                "Validation fault", "The object is not valid",
                "VirtualIP type specified for load balancer is not a valid type"
            )

        ips_str = pool_config["ips"]
        ips = ips_str.split(",")

        ips = [ip.strip() for ip in ips]
        """ validate IP addresses and ranges found in configuration """
        for ip in ips:
            try:
                IP(ip)
            except ValueError as msg:
                self.logger.debug(
                    "Virtual IP range %s specified in configuration file is invalid:%s"
                    % (ip, msg))
                raise ServiceUnavailableException(
                    "Invalid service configuration.Please contact support")

        self.vip_pools[viptype] = ips

    def _is_range(self, vip):

        if "-" in vip or "/" in vip:
            return True

        return False

    def _get_next_address(self, address_str):

        next_address = IP(IP(address_str).int() + 1)

        return next_address.strNormal()

    def _get_free_vip_in_range2(self, start_address, end_address,
                                allocated_vips):

        comps = start_address.split(".")

        if len(comps) != 4:
            raise LoadBalancerFaultException(
                "Bad configuration for LB service. Malformed IP address")

        address = start_address
        free_address = None

        while address != end_address:
            if address in allocated_vips:
                address = self._get_next_address(address)
                if address == None:
                    return None

                continue

            return address

        if end_address in allocated_vips:
            return None
        else:
            return end_address

    def _get_free_vip_in_range(self, vip_range, allocated_vips):

        try:
            make_net = 0

            if "/" in vip_range:
                make_net = 1
                ip_list = IP(vip_range, make_net=make_net)

        except ValueError:
            self.logger.debug(
                "Virtual IP range %s specified in configuration file is invalid:"
                % vip_range)
            raise ServiceUnavailableException(
                "Invalid service configuration.Please contact support")

        if "-" in vip_range:
            start_address, end_address = vip_range.split('-')
        else:
            network_address = ip_list.net()
            broadcast_address = ip_list.broadcast()
            """ netmask or CIDR network notation """
            start_address = IP(network_address.int() + 1)
            end_address = IP(broadcast_address.int() - 1)

        self.logger.debug("VIP range: %s" % vip_range)
        self.logger.debug("       start address: %s" % str(start_address))
        self.logger.debug("       end address: %s" % str(end_address))

        address = self._get_free_vip_in_range2(str(start_address),
                                               str(end_address),
                                               allocated_vips)

        return address

    def get_virtualIP(self, vipid, tenant_id):
        return self.db.get_vip_from_id(vipid, tenant_id)

    def get_virtualIP_type(self, vip):
        return self.db.get_vip_type(vip)

    def allocate_virtualIP(self, viptype, tenant_id, loadBalancerId):

        if not viptype in self.vip_pools.keys():
            self._configure_pool(viptype)

        all_vips = self.vip_pools[viptype]

        allocated_vips = self.db.get_allocated_vips(viptype)
        allocated_addresses = [vip["address"] for vip in allocated_vips]
        allocated_vipids = [vip["id"] for vip in allocated_vips]

        for vip in all_vips:
            """ Check if this is an individual entry or a range """
            if self._is_range(vip):
                self.logger.debug("already allocated vips are: %s" %
                                  str(allocated_addresses))
                free_vip = self._get_free_vip_in_range(vip,
                                                       allocated_addresses)
                if free_vip:
                    break

                continue
            else:
                if vip in allocated_addresses:
                    continue

                free_vip = vip
                break
        else:
            raise OutOfVirtualIpsException(
                "Out of virtual IPs. Please contact support so they can make available more virtual IPs for use."
            )

        vip_id = NitroUtils.generate_vipid(allocated_vipids)

        version = IP(free_vip).version()

        if version == 4:
            vip_version = "IPV4"
        elif version == 6:
            vip_version = "IPV6"
        else:
            self.logger.debug(
                "Allocated IP address %s doesn't seem to be IPV4 or IPV6 !!" %
                free_vip)
            raise ServiceUnavailableException(
                "Internal error.Please contact support")

        self.db.allocate_vip(vip_id, free_vip, viptype, vip_version, tenant_id,
                             loadBalancerId)

        self.logger.debug(
            "Allocated VIP: address=%s ipVersion=%s  type=%s id=%s" %
            (free_vip, vip_version, viptype, vip_id))

        return free_vip

    def share_virtualIP(self, vipid, tenant_id, loadBalancerId):
        """ Let's first check if an IP address with this ID exists """
        address = self.get_virtualIP(vipid, tenant_id)

        if not address:
            raise LoadBalancerFaultException(
                "No virtual IP address with id %s could be found" % vipid)

        self.db.share_vip(vipid, tenant_id, loadBalancerId)

        return address

    def release_virtualIP(self, ipaddress, viptype):
        self.db.release_vip(ipaddress, viptype)
Ejemplo n.º 5
0
class NitroLBNodeAdapter(object):

    def __init__(self, lbresource, extensions_enabled=False):
        self.lbresource = lbresource
        self.lbservice = lbresource.lbservice 
        self.connmanager = self.lbservice.connmanager
        self.logger = self.lbservice.logger 
        self.extensions_enabled = extensions_enabled
        self.db = NetScalerLBServiceDB(self.lbservice)



    def _get_new_loadBalancer_object(self):

        return NetScalerLoadBalancerState(self.lbresource)


    def _get_netscaler_service_from_node(self, loadBalancerId, loadBalancer, nodeId, node):

        service_obj = {}
     

        for key in node:
 
            self.logger.debug("Examining key \"%s\" of node object" % key)

            if key == "_properties":
                continue

            if key == "id":
                service_obj["name"] = NitroUtils.get_servicename_from_nodeid(loadBalancerId, nodeId)
                continue

            if key == "address":
                service_obj["ip"] = node.address
                continue 
            
            if key == "port":
                service_obj["port"] = node.port
                continue                


        if loadBalancer:
            servicetype = NitroUtils.get_servicetype_from_protocol(loadBalancer.protocol)
            servicetype = NitroUtils.get_decrypted_version_of_servicetype(servicetype)
            service_obj["servicetype"] = servicetype 


        return service_obj
        


    def _get_netscaler_servicebinding_from_node(self, loadBalancerId, loadBalancer, nodeId, node):

        service_binding_obj = {}

        lbvserver_name = NitroUtils.get_lbvservername_from_loadbalancerid(loadBalancerId)
        service_name =  NitroUtils.get_servicename_from_nodeid(loadBalancerId, nodeId)

        service_binding_obj["name"] = lbvserver_name
        service_binding_obj["servicename"] = service_name


        if "weight" in node and node.weight != 1 :
            if not loadBalancer.algorithm.startswith("WEIGHTED_"):
                raise BadRequestException("validation fault", "invalid object", 
                                          "LB algorithm in use doesn't allow for weighted nodes")

            service_binding_obj["weight"] = node["weight"]

        return service_binding_obj
        

    def _get_netscaler_servicename_from_servicebinding(self, loadBalancerId, loadBalancer, binding):
    
        servicename = binding["servicename"]

        return servicename


             
    def _get_netscaler_node_from_service_and_servicebinding(self, loadBalancerId, loadBalancer, service, servicebinding):

        node = LBNodeState(self.lbresource)

        service_name = service["name"] 

        node.id = NitroUtils.get_nodeid_from_servicename(loadBalancerId, service_name)

        if "ipaddress" in service and service["ipaddress"]:
            ipaddress = service["ipaddress"]
            ipaddress = ipaddress.encode('ascii', 'ignore')
            node.address = ipaddress


        if "port" in service and service["port"]:
            port = service["port"]
            port = str(port)
            node.port = port 

                     
        if "svrstate" in service and service["svrstate"]:
            svrstate =  service["svrstate"]
            
            node.condition = NitroUtils.get_condition_from_svrstate(svrstate)
            node.status = NitroUtils.get_nodestatus_from_svrstate(svrstate)


        if "weight" in servicebinding and servicebinding["weight"]:
            weight = servicebinding["weight"]
            node.weight = str(weight)


        self.logger.debug("returned node is: %s" % node)

        return node



    def _get_netscaler_node_currentcondition(self, loadBalancerId, loadBalancer, nodeId):

        service_name = NitroUtils.get_servicename_from_nodeid(loadBalancerId, nodeId)

        service = self.nitrowrapper.get_netscaler_entity("service", service_name)

        svrstate = service["svrstate"]

        current_condition = NitroUtils.get_condition_from_svrstate(svrstate)

        return current_condition

 

    def _get_netscaler_getservice_task(self, loadBalancerId, loadBalancer, nodeId):
                                                           
        service_name = NitroUtils.get_servicename_from_nodeid(loadBalancerId, nodeId)
                                                
        task = {}
        task["type"] = "service"
        task["name"] = service_name
        task["operation"] = "GET"
        task["state"] = None

        return task


    def _get_netscaler_getservicebinding_task(self, loadBalancerId, loadBalancer, nodeId):

        self.logger.debug("loadBalancer Id is: %s" % loadBalancerId)
        self.logger.debug("node Id is: %s" % nodeId)

        lbvserver_name = NitroUtils.get_lbvservername_from_loadbalancerid(loadBalancerId) 
        service_name = NitroUtils.get_servicename_from_nodeid(loadBalancerId, nodeId)

        args = "servicename:" + service_name
                                                                                                        
        task = {}

        task["type"] = "lbvserver_service_binding"
        task["name"] = lbvserver_name + "?filter=" + args
        task["operation"] = "GET"
        task["state"] = None

        return task



    def _get_netscaler_addservice_task(self, loadBalancerId, loadBalancer, nodeId, node):

        service = self._get_netscaler_service_from_node(loadBalancerId, loadBalancer, nodeId, node)  

        task = {}
        task["type"] = "service"
        task["name"] = service["name"]
        task["operation"] = "ADD"
        task["state"] = service

        return task


    def _get_netscaler_updateservice_task(self, loadBalancerId, loadBalancer, nodeId, node):

        service = self._get_netscaler_service_from_node(loadBalancerId, loadBalancer, nodeId, node)

        if not service:
            return None


        """ For update operations, we cannot update the servicetype of a service, so we need to remove it 
            here. Earlier checks should have been made to throw an exception if the user tries to change 
            the protocol of the loadbalancer. """

        del service["servicetype"]

        if len(service.keys()) == 1:
           """ There is nothing to update on the service entity """
           return None
  

        task = {}
        task["type"] = "service"
        task["name"] = service["name"]
        task["operation"] = "UPDATE"
        task["state"] = service

        return task  


    def _get_netscaler_updateservice_tasks(self, loadBalancerId, loadBalancer, nodeId, node):   

        task_list = []

        task = self._get_netscaler_updateservice_task(loadBalancerId, loadBalancer, nodeId, node)
 
        if task:
            task_list.append(task)
 
        task = self._get_netscaler_updateservicestatus_task(loadBalancerId, loadBalancer, nodeId, node)

        if task: 
            task_list.append(task) 
        
        return task_list



    def _get_netscaler_removeservice_task(self, loadBalancerId, loadBalancer, nodeId):
                 
        servicename = NitroUtils.get_servicename_from_nodeid(loadBalancerId, nodeId)
                                                                                          
        task = {}
        task["type"] = "service"
        task["name"] = servicename
        task["operation"] = "REMOVE"
        task["state"] = None

        return task



    def _get_netscaler_removeservices_tasks(self, loadBalancerId, loadBalancer, nodeIds):
                                                           
        task_list = []

        for nodeId in nodeIds:
            task = self._get_netscaler_removeservice_task(loadBalancerId, loadBalancer, nodeId)
            task_list.append(task)
                              
        return task_list




    def get_netscaler_addservicebinding_task(self, loadBalancerId, loadBalancer, nodeId, node):

        service_binding_obj = self._get_netscaler_servicebinding_from_node(loadBalancerId, loadBalancer, nodeId, node)
                                                                                                           
        task = {}
        task["type"] = "lbvserver_service_binding"
        task["name"] = service_binding_obj["name"]
        task["state"] = service_binding_obj
        task["operation"] = "ADD"

        return task


    def _get_netscaler_addmonitorbinding_task(self, loadBalancerId, loadBalancer, node):

        if not "healthMonitor" in loadBalancer or not loadBalancer.healthMonitor:
            return None

        task = self.monitoradapter.get_netscaler_addmonitorbinding_task(loadBalancerId, node, loadBalancer.healthMonitor)

        return task


    def get_netscaler_updateservicebinding_tasks(self, loadBalancerId, loadBalancer, nodeId, node):   

        """ the only reason to update a binding is if the weight property on a node is updated """

        if not "weight" in node:
            return None

        """ In order to update an service binding, one has to remove the 
            existing one and add a new one """ 
             
        task_list = []

        task = self.get_netscaler_removeservicebinding_task(loadBalancerId, loadBalancer, nodeId)
        task_list.append(task)
 
        task = self.get_netscaler_addservicebinding_task(loadBalancerId, loadBalancer, nodeId, node)
        task_list.append(task)
 
        return task_list


    def get_netscaler_removeservicebinding_task(self, loadBalancerId, loadBalancer, nodeId):

        lbvserver_name = NitroUtils.get_lbvservername_from_loadbalancerid(loadBalancerId)
        servicename = NitroUtils.get_servicename_from_nodeid(loadBalancerId, nodeId)

        args = "servicename:" +  servicename
        args += ","
        args += "name:" + lbvserver_name

        task = {}
        task["type"] = "lbvserver_service_binding"
        task["name"] = lbvserver_name + "?args=" + args
        task["operation"] = "REMOVE"
        task["state"] = None

        return task


    def _get_netscaler_removeservicebindings_tasks(self, loadBalancerId, loadBalancer, nodeIds):
 
        task_list = []

        for nodeId in nodeIds:
            
            task = self.get_netscaler_removeservicebinding_task(loadBalancerId, loadBalancer, nodeId)
            task_list.append(task)

        return task_list 



    def _get_netscaler_addservicestatus_task(self, loadBalancerId, loadBalancer, nodeId, node):

        if not "condition" in node:
            return None

        service_name = NitroUtils.get_servicename_from_nodeid(loadBalancerId, nodeId)

        service = {}
        service["name"] = service_name

        task = {}
        task["type"] = "service"
        task["name"] = service_name
        task["state"] = service

        if node.condition == "ENABLED":
            task["operation"] = "ENABLE"
        else:
            task["operation"] = "DISABLE" 

        return task



    def _get_netscaler_updateservicestatus_task(self, loadBalancerId, loadBalancer, nodeId, node):

        if not "condition" in node:
            return None

        """ We shouldn't modify a service status if it's already in the desired state.
            This makes the logic of "undoing" an enable/disable operation easier, because we 
            can go ahead and disable/enable the service, without having to worry about cases
            like "what if we were enabling a service that was already enabled, in which case,
            we shouldn't disable it on the undo, we should leave it as it is.". This below 
            avoids processing these noop tasks
        """

        current_condition = self._get_netscaler_node_currentcondition(loadBalancerId, loadBalancer, nodeId)

        if current_condition == node.condition:
            return None

        return self._get_netscaler_addservicestatus_task(loadBalancerId, loadBalancer, nodeId, node)



    def _get_netscaler_updatenode_tasks(self, loadBalancerId, loadBalancer, nodeId, node):

        task_list = []
 
        tasks = self._get_netscaler_updateservice_tasks(loadBalancerId, loadBalancer, nodeId, node)

        if tasks:
            task_list.extend(tasks)

        tasks = self.get_netscaler_updateservicebinding_tasks(loadBalancerId, loadBalancer, nodeId, node)

        if tasks:
            task_list.extend(tasks)


        return task_list
  



    def _get_netscaler_tasks_for_list_op(self, loadBalancerId, loadBalancer):

        task_list = []

        if not loadBalancerId:
            raise ImplementationErrorException("Programming error")
 

        task = self.get_netscaler_getservicebindings_task(loadBalancerId, loadBalancer)
        task_list.append(task)

        return task_list



    def _get_netscaler_tasks_for_get_op(self, loadBalancerId, loadBalancer, nodeId):

        task_list = []

        task = self._get_netscaler_getservicebinding_task(loadBalancerId, loadBalancer, nodeId)
        task_list.append(task)

        task = self._get_netscaler_getservice_task(loadBalancerId, loadBalancer, nodeId)
        task_list.append(task)

        return task_list



    def _get_netscaler_tasks_for_addmulti_op(self, loadBalancerId, loadBalancer, nodes):

        existing_nodes = self._list_LBNodesInternal(loadBalancerId, loadBalancer)

        existing_ids = []

        for node in existing_nodes:
            existing_ids.append(node.id)

        service_tasks = self.get_netscaler_addnodes_tasks(loadBalancerId, loadBalancer, nodes, existing_ids)

        return service_tasks



    def _get_netscaler_tasks_for_add_op(self, loadBalancerId, loadBalancer, node):

        nodes = [] 
        nodes.append(node)
         
        return self._get_netscaler_tasks_for_addmulti_op(loadBalancerId, loadBalancer, nodes)



    def _get_netscaler_tasks_for_update_op(self, loadBalancerId, loadBalancer, nodeId, node):
    
        if not node:
            raise ImplementationErrorException("Programming error")

        task_list = self._get_netscaler_updatenode_tasks(loadBalancerId, loadBalancer, nodeId, node)

        return task_list


    def _get_netscaler_tasks_for_remove_op(self, loadBalancerId, loadBalancer, nodeId):

        task_list = []

        if not all([loadBalancerId, loadBalancer, nodeId]):
            raise ImplementationErrorException("Programming error")
  
        task = self.get_netscaler_removeservicebinding_task(loadBalancerId, loadBalancer, nodeId)
        task_list.append(task)
 
        task = self._get_netscaler_removeservice_task(loadBalancerId, loadBalancer, nodeId)
        task_list.append(task)

        return task_list



    def _get_netscaler_tasks(self, operation, loadBalancerId, loadBalancer, nodeId=None, node=None):

        if operation == "LIST":
            return self._get_netscaler_tasks_for_list_op(loadBalancerId, loadBalancer)

        if operation == "GET":
            return self._get_netscaler_tasks_for_get_op(loadBalancerId, loadBalancer, nodeId)

        if operation == "ADD":
            return self._get_netscaler_tasks_for_add_op(loadBalancerId, loadBalancer, node)

        if operation == "ADDMULTI":
            nodes = node
            return self._get_netscaler_tasks_for_addmulti_op(loadBalancerId, loadBalancer, nodes)

        if operation == "UPDATE":
            return self._get_netscaler_tasks_for_update_op(loadBalancerId, loadBalancer, nodeId, node)
        
        if operation == "REMOVE":
            return self._get_netscaler_tasks_for_remove_op(loadBalancerId, loadBalancer, nodeId)

        """ We should never get here """ 
        raise ImplementationError("programming error")
   

 
    def _build_lbnodes(self, loadBalancerId, loadBalancer, services, servicebindings):
        
        nodes = self.get_netscaler_nodes_from_services_and_servicebindings(loadBalancerId, loadBalancer, services, servicebindings)

        self.logger.debug("nodes of loadBalancer are: %s" % str(nodes))
 
        return nodes


    def _build_lbnode(self, loadBalancerId, loadBalancer, nodeId, service, servicebinding):


        node = self._get_netscaler_node_from_service_and_servicebinding(loadBalancerId, loadBalancer, service, servicebinding)

        if node.id != nodeId:
            raise ImplementationError("programming error")

        self.logger.debug("node %s has the following state: %s" % (nodeId, str(node)))
 
        return node


    def get_netscaler_nodes_from_services_and_servicebindings(self, loadBalancerId, loadBalancer, services, servicebindings):

        nodes = []

        if not services:
            return nodes

        for service in services:
            servicebinding = NitroUtils.get_servicebinding_for_service(service, servicebindings)

            if servicebinding:
                node = self._get_netscaler_node_from_service_and_servicebinding(loadBalancerId, loadBalancer, service, servicebinding)
                nodes.append(node)

        if not nodes:
            return None

        return nodes 
       


    def get_netscaler_addnodes_tasks(self, loadBalancerId, loadBalancer, nodes, existing_ids=[]):


        """ 
        Preconditions: 
                  A. The lbvserver object must have the following attributes
                     for this method to work:
                         1. name 
                         2. service_type

                  B. The lb_algorithm must contain a non empty value
        """
                        

        total_nodes = len(existing_ids) + len(nodes)

        self.logger.debug("existing node ids are: %s" % str(existing_ids))

        if total_nodes > MAX_NODES_PER_LOADBALANCER:
            raise OverLimitException("Number of nodes exceeds maximum of %s allowed per load balancer" % MAX_NODES_PER_LOADBALANCER)
 
        task_list = []

        for node in nodes:

            if not "id" in node:
                operation = "ADD"
                nodeid = NitroUtils.generate_nodeid(existing_ids)
                existing_ids.append(nodeid) 
                node.id = str(nodeid)

            else:
                operation = "UPDATE"

            task = self._get_netscaler_addservice_task(loadBalancerId, loadBalancer, node.id, node)
            task["operation"] = operation
            task_list.append(task)    
              
            task = self.get_netscaler_addservicebinding_task(loadBalancerId, loadBalancer, node.id, node)
            task_list.append(task)

            service_name = NitroUtils.get_servicename_from_nodeid(loadBalancerId, node.id)

            if operation == "ADD": 
                task = self._get_netscaler_addservicestatus_task(loadBalancerId, loadBalancer, node.id, node)
                task = self._get_netscaler_addmonitorbinding_task(loadBalancerId, loadBalancer, node)

            if operation == "UPDATE":
                task = self._get_netscaler_updateservicestatus_task(loadBalancerId, loadBalancer, node.id, node)

            if task:
                task_list.append(task) 
        

        return task_list  



    def get_netscaler_removeboundservices_tasks(self, loadBalancerId, loadBalancer):

       """I need first to get the list of service names"""

       task_list = []

       task = self.get_netscaler_getservicebindings_task(loadBalancerId, loadBalancer) 
       task_list.append(task)  

       completed_tasks = self.nitrowrapper.process_netscaler_task_list(task_list)

       servicebindings = NitroTasks.extract_servicebindings_from_task_list(completed_tasks)

       if not servicebindings:
           return task_list

       """ We form a new task list to remove the service bindings and the services """

       nodeIds = []

       for binding in servicebindings:
           servicename = self._get_netscaler_servicename_from_servicebinding(loadBalancerId, loadBalancer, binding) 
           nodeId = NitroUtils.get_nodeid_from_servicename(loadBalancerId, servicename)
           nodeIds.append(nodeId)

 
       task_list = []

       tasks = self._get_netscaler_removeservicebindings_tasks(loadBalancerId, loadBalancer, nodeIds)
       task_list.extend(tasks)

       tasks = self._get_netscaler_removeservices_tasks(loadBalancerId, loadBalancer, nodeIds)
       task_list.extend(tasks)
 
       return task_list



    def get_netscaler_getservices_tasks(self, loadBalancerId, loadBalancer, servicebindings):
                                                           
        task_list = []

        for binding in servicebindings:
            servicename = binding["servicename"]
            nodeId = NitroUtils.get_nodeid_from_servicename(loadBalancerId, servicename)
            task = self._get_netscaler_getservice_task(loadBalancerId, loadBalancer, nodeId)
            task_list.append(task)
                              
        return task_list


    def get_netscaler_getservicebindings_task(self, loadBalancerId, loadBalancer):
                 
        lbvserver_name = NitroUtils.get_lbvservername_from_loadbalancerid(loadBalancerId)
                                                                                          
        task = {}
        task["type"] = "lbvserver_service_binding"
        task["name"] = lbvserver_name
        task["operation"] = "GET"
        task["state"] = None

        return task


    def _setup_nitrowrapper(self, tenant_id, loadBalancerId=None):

        connection = self.connmanager.get_nitro_connection(tenant_id, loadBalancerId)

        self.nitrowrapper = NitroCallsWrapper.get_nitrowrapper(connection, self.logger)

        self.lbadapter = NitroLoadBalancerAdapter.NitroLoadBalancerAdapter(self.lbresource, self.extensions_enabled)
        self.monitoradapter = NitroLBHealthMonitorAdapter.NitroLBHealthMonitorAdapter(self.lbresource, self.extensions_enabled)

    def _list_LBNodesInternal(self, loadBalancerId, loadBalancer):

        task_list = self._get_netscaler_tasks("LIST", loadBalancerId, loadBalancer)

        self.logger.debug("LIST nodes tasklist: " + str(task_list))

        results = self.nitrowrapper.process_netscaler_task_list(task_list) 

        servicebindings = NitroTasks.extract_servicebindings_from_task_list(results)
 
        if servicebindings:
            task_list = self.get_netscaler_getservices_tasks(loadBalancerId, loadBalancer, servicebindings)

            results = self.nitrowrapper.process_netscaler_task_list(task_list) 

            services = NitroTasks.extract_services_from_task_list(results, servicebindings)
        else:
            services = None

        lbnodes = self._build_lbnodes(loadBalancerId, loadBalancer, services, servicebindings)

        return lbnodes 


    def _get_LBNodeInternal(self, loadBalancerId, loadBalancer, nodeId):

        task_list = self._get_netscaler_tasks("GET", loadBalancerId, loadBalancer, nodeId)

        self.logger.debug("GET nodes tasklist: " + str(task_list))

        results = self.nitrowrapper.process_netscaler_task_list(task_list) 

        servicebindings = NitroTasks.extract_servicebindings_from_task_list(results)        

        """ we are expecting only one binding back """
        if not servicebindings or len(servicebindings) != 1:
            self.logger.debug("servicebindings list returned: %s " % str(servicebindings))
            raise ImplementationErrorException("programming error ")

        servicebinding = servicebindings[0]  

        self.logger.debug("servicebinding retrieved: " + str(servicebinding))

        service = NitroTasks.extract_service_from_task_list(results, servicebinding) 

        lbnode = self._build_lbnode(loadBalancerId, loadBalancer, nodeId, service, servicebinding)

        return lbnode


    def _update_LBNodeInternal(self, loadBalancerId, loadBalancer, nodeId, node):

        task_list = self._get_netscaler_tasks("UPDATE", loadBalancerId, loadBalancer, nodeId, node)
                                                    
        remove_task = None
            
        for task in task_list:
            resource_name = task["name"]
            resource_type = task["type"]
            resource_state = task["state"]

            if resource_type == "service" and task["operation"] == "UPDATE":
                """ If there is no field to update in this UPDATE operation, then there
                    is no point of dispatching a useless update task"""
                if len(resource_state.keys()) <= 1:
                    remove_task = task

                break                      


        if remove_task:
           task_list.remove(remove_task)                                                                                  
        
        self.logger.debug("update task list: " + str(task_list))
          
        if task_list: 
            self.nitrowrapper.process_netscaler_task_list(task_list) 

        return self._get_LBNodeInternal(loadBalancerId, loadBalancer, nodeId)



    def _remove_LBNodeInternal(self, loadBalancerId, loadBalancer, nodeId):

        task_list = self._get_netscaler_tasks("REMOVE", loadBalancerId, loadBalancer, nodeId)

        self.logger.debug("remove task list: " + str(task_list))

        self.nitrowrapper.process_netscaler_task_list(task_list)


    def list_LBNodes(self, tenant_id, loadBalancerId):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        loadBalancer = self.lbadapter.get_LoadBalancer(loadBalancerId, tenant_id)

        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(tenant_id, loadBalancerId, loadBalancer)

        return self._list_LBNodesInternal(internal_loadBalancerId, loadBalancer)



    def get_LBNode(self, tenant_id, loadBalancerId, nodeId):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        loadBalancer = self.lbadapter.get_LoadBalancer(loadBalancerId, tenant_id)

        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(tenant_id, loadBalancerId, loadBalancer)

        lbnode = self._get_LBNodeInternal(internal_loadBalancerId, loadBalancer, nodeId)

        return lbnode 



    def add_LBNode(self, lbnode, tenant_id, loadBalancerId):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        loadBalancer = self.lbadapter.get_LoadBalancer(loadBalancerId, tenant_id)


        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(tenant_id, loadBalancerId, loadBalancer)

        task_list = self._get_netscaler_tasks("ADD", internal_loadBalancerId, loadBalancer, node=lbnode)
        
        self.logger.debug("add task list: " + str(task_list))

        self.nitrowrapper.process_netscaler_task_list(task_list) 

        added_node = self.get_LBNode(tenant_id, loadBalancerId, lbnode.id)

        self.db.update_loadBalancer_updatetime(tenant_id, loadBalancerId)

        return added_node



    def add_LBNodes(self, lbnodes, tenant_id, loadBalancerId):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        self.logger.debug("Adding nodes to loadbalancer %s" % loadBalancerId)

        loadBalancer = self.lbadapter.get_LoadBalancer(loadBalancerId, tenant_id)

        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(tenant_id, loadBalancerId, loadBalancer)

        task_list = self._get_netscaler_tasks("ADDMULTI", internal_loadBalancerId, loadBalancer, node=lbnodes)

        self.nitrowrapper.process_netscaler_task_list(task_list) 

        added_nodes = []

        for lbnode in lbnodes:
            node = self._get_LBNodeInternal(internal_loadBalancerId, loadBalancer, lbnode.id)
            added_nodes.append(node)     

        self.db.update_loadBalancer_updatetime(tenant_id, loadBalancerId)

        return added_nodes



    def update_LBNode(self, loadBalancerId, nodeId, lbnode, tenant_id):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)


        """"
        dbLoadBalancer = self._get_new_loadBalancer_object()

        dbLoadBalancer = self.db.fill_loadBalancer_object(tenant_id, loadBalancerId, dbLoadBalancer)

        if not dbLoadBalancer:
            raise ItemNotFoundException("No loadBalancer with id %s was found" % loadBalancerId)
        
        if dbLoadBalancer.status == "DELETED":
            raise ImmutableEntityException("loadBalancer with id %s was deleted and cannot be updated" % loadBalancerId)
        """

        loadBalancer = self.lbadapter.get_LoadBalancer(loadBalancerId, tenant_id)


        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(tenant_id, loadBalancerId, loadBalancer)

        lbnode.id = nodeId
 
        lbnode = self._update_LBNodeInternal(internal_loadBalancerId, loadBalancer, nodeId, lbnode)

        self.db.update_loadBalancer_updatetime(tenant_id, loadBalancerId)
 
        return lbnode
        


    def remove_LBNode(self, loadBalancerId, nodeId, tenant_id):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        loadBalancer = self.lbadapter.get_LoadBalancer(loadBalancerId, tenant_id)

        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(tenant_id, loadBalancerId, loadBalancer)

        self._remove_LBNodeInternal(internal_loadBalancerId, loadBalancer, nodeId)

        self.db.update_loadBalancer_updatetime(tenant_id, loadBalancerId)
class NitroLBHealthMonitorAdapter(object):
    def __init__(self, lbresource, extensions_enabled=False):
        self.lbresource = lbresource
        self.lbservice = lbresource.lbservice
        self.connmanager = self.lbservice.connmanager
        self.logger = self.lbservice.logger
        self.extensions_enabled = extensions_enabled
        self.db = NetScalerLBServiceDB(self.lbservice)

    def get_netscaler_healthmonitor_from_monitor(self, loadBalancerId,
                                                 loadBalancer, monitor):

        healthMonitor = LBHealthMonitorState(self.lbresource)

        if monitor["type"].upper() == "TCP":
            healthMonitor.type = "CONNECT"
        elif monitor["type"].upper() == "USER":
            if monitor["scriptname"] != self.usermonitor:
                self.logger.debug(
                    "Not the user script expected ! : scriptname=%s" %
                    monitor["scriptname"])
                raise ImplementationErrorException("programming error")

        if "interval" in monitor and monitor["interval"]:
            delay = monitor["interval"]
            delay = str(delay)
            healthMonitor.delay = delay

        if "resptimeout" in monitor and monitor["resptimeout"]:
            timeout = monitor["resptimeout"]
            timeout = str(timeout)
            healthMonitor.timeout = timeout

        if "retries" in monitor and monitor["retries"]:
            attemptsBeforeDeactivation = monitor["retries"]
            attemptsBeforeDeactivation = str(attemptsBeforeDeactivation)
            healthMonitor.attemptsBeforeDeactivation = attemptsBeforeDeactivation

        if monitor["type"].upper() == "TCP":
            return healthMonitor

        if "scriptargs" in monitor and monitor["scriptargs"]:
            scriptargs = monitor["scriptargs"]
            scriptargs = scriptargs.encode('ascii', 'ignore')

            request_comps = scriptargs.split(';')

            if len(request_comps) < 1:
                self.logger.debug(
                    "Unexpected erroneous monitor scriptargs: %s" %
                    repr(scriptargs))
                raise ImplementationErrorException("programming error")

            for comp in request_comps:

                comp_name, comp_value = comp.split('=')
                comp_value = comp_value.strip('\'')

                if comp_name == 'type':
                    healthMonitor.type = comp_value
                    continue

                if comp_name == 'path':
                    healthMonitor.path = comp_value
                    continue

                if comp_name == 'statusRegex':
                    healthMonitor.statusRegex = comp_value
                    continue

                if comp_name == 'bodyRegex':
                    healthMonitor.bodyRegex = comp_value
                    continue

                self.logger.debug(
                    "Unexpected erroneous monitor scriptargs: %s" %
                    repr(scriptargs))
                raise ImplementationErrorException("programming error")
        """ the path must exist in the scriptargs"""

        if not "path" in healthMonitor or not healthMonitor.path:
            self.logger.debug("Unexpected erroneous monitor scriptargs: %s" %
                              repr(scriptargs))
            raise ImplementationErrorException("programming error")

        return healthMonitor

    def _adjust_healthmonitor_state(self, newHealthMonitor,
                                    existingHealthMonitor):

        if not "type" in newHealthMonitor and "type" in existingHealthMonitor:
            newHealthMonitor.type = existingHealthMonitor.type

        if not "delay" in newHealthMonitor and "delay" in existingHealthMonitor:
            newHealthMonitor.delay = existingHealthMonitor.delay

        if not "timeout" in newHealthMonitor and "timeout" in existingHealthMonitor:
            newHealthMonitor.timeout = existingHealthMonitor.timeout

        if (not "attemptsBeforeDeactivation" in newHealthMonitor
                and "attemptsBeforeDeactivation" in existingHealthMonitor):
            newHealthMonitor.attemptsBeforeDeactivation = existingHealthMonitor.attemptsBeforeDeactivation

        if newHealthMonitor.type.upper() == "CONNECT":
            return

        if not existingHealthMonitor.type.upper().startswith("HTTP"):
            return

        if not "path" in newHealthMonitor and "path" in existingHealthMonitor:
            newHealthMonitor.path = existingHealthMonitor.path

        if not "statusRegex" in newHealthMonitor and "statusRegex" in existingHealthMonitor:
            newHealthMonitor.statusRegex = existingHealthMonitor.statusRegex

        if not "bodyRegex" in newHealthMonitor and "bodyRegex" in existingHealthMonitor:
            newHealthMonitor.bodyRegex = existingHealthMonitor.bodyRegex

        if "delay" in newHealthMonitor and "timeout" in newHealthMonitor:
            delay = int(newHealthMonitor.delay)
            timeout = int(newHealthMonitor.timeout)

            if delay <= timeout:
                self.logger.debug(
                    "Validation error: healthMonitor delay must be greater than timeout"
                )
                raise BadRequestException(
                    "Validation fault", "invalid object",
                    "healthMonitor delay attribute must have a value greater than that of timeout attribute"
                )
        self.logger.debug("adjusted health monitor: %s" %
                          repr(newHealthMonitor))

    def get_netscaler_gethealthmonitor_tasks(self, loadBalancerId,
                                             loadBalancer):

        task_list = []

        monitor_name = NitroUtils.get_monitorname_from_loadBalancerId(
            loadBalancerId)

        task = {}
        task["type"] = "lbmonitor"
        task["name"] = monitor_name
        task["operation"] = "GET"
        task["state"] = None

        task_list.append(task)

        return task_list

    def _get_netscaler_monitorbinding_from_healthmonitor(
            self, loadBalancerId, node, healthMonitor):

        monitor_name = NitroUtils.get_monitorname_from_loadBalancerId(
            loadBalancerId)
        service_name = NitroUtils.get_servicename_from_nodeid(
            loadBalancerId, node.id)

        monitor_binding = {}
        monitor_binding["monitorname"] = monitor_name
        monitor_binding["servicename"] = service_name

        return monitor_binding

    def _get_netscaler_loadBalancer_monitor(self, loadBalancerId,
                                            loadBalancer):

        resource_type = "lbmonitor"
        monitor = None

        monitor_name = NitroUtils.get_monitorname_from_loadBalancerId(
            loadBalancerId)

        try:
            monitor = self.nitrowrapper.get_netscaler_entity(
                resource_type, monitor_name)
        except ItemNotFoundException:
            return None

        return monitor

    def _get_netscaler_monitor_from_healthmonitor(self, loadBalancerId,
                                                  loadBalancer, healthMonitor):

        self.logger.debug(
            "obtaining a netscaler monitor from an OpenStack healthmonitor: %s"
            % str(healthMonitor))

        if not "type" in healthMonitor or not healthMonitor.type:
            self.logger.debug("healthMonitor type attribute missing")
            raise BadRequestException("validation fault", "invalid object",
                                      "healthMonitor type attribute missing")

        if not healthMonitor.type.upper() in ["CONNECT", "HTTP", "HTTPS"]:
            self.logger.debug(
                "healthMonitor type attribute has an invalid value")
            raise BadRequestException(
                "validation fault", "invalid object",
                "healthMonitor type attribute has an invalid value")

        monitor = {}

        monitor[
            "monitorname"] = NitroUtils.get_monitorname_from_loadBalancerId(
                loadBalancerId)

        if healthMonitor.type.upper() in ["HTTP", "HTTPS"]:
            monitor["type"] = "USER"
            monitor["scriptname"] = self.usermonitor
        else:
            monitor["type"] = "TCP"

        if "delay" in healthMonitor:
            monitor["interval"] = int(healthMonitor.delay)

        if "timeout" in healthMonitor:
            monitor["resptimeout"] = int(healthMonitor.timeout)

        if "attemptsBeforeDeactivation" in healthMonitor:
            monitor["retries"] = int(healthMonitor.attemptsBeforeDeactivation)

        if monitor["type"] == "TCP":
            return monitor

        if not "path" in healthMonitor or not healthMonitor.path:
            self.logger.debug(
                "healthMonitor of type HTTP or HTTPS has the path attribute missing"
            )
            raise BadRequestException(
                "validation fault", "invalid object",
                "healthMonitor of type HTTP or HTTPS has the path attribute missing"
            )

        type_expr = '='.join(["type", healthMonitor.type])
        scriptargs = type_expr

        path = '\'' + healthMonitor.path + '\''
        path_expr = '='.join(["path", path])
        scriptargs = ';'.join([scriptargs, path_expr])

        if not "statusRegex" in healthMonitor or not healthMonitor.statusRegex:
            healthMonitor.statusRegex = "[1-5]{3}"

        statusRegex = '\'' + healthMonitor.statusRegex + "\'"
        status_expr = '='.join(["statusRegex", statusRegex])
        scriptargs = ';'.join([scriptargs, status_expr])

        if not "bodyRegex" in healthMonitor or not healthMonitor.bodyRegex:
            healthMonitor.bodyRegex = ".*"

        bodyRegex = '\'' + healthMonitor.bodyRegex + "\'"
        body_expr = '='.join(["bodyRegex", bodyRegex])
        scriptargs = ';'.join([scriptargs, body_expr])

        monitor["scriptargs"] = scriptargs

        self.logger.debug("Netscaler monitor built from healthmonitor: %s" %
                          str(monitor))

        return monitor

    def _get_netscaler_addmonitor_task(self, loadBalancerId, loadBalancer,
                                       healthMonitor):

        monitor_name = NitroUtils.get_monitorname_from_loadBalancerId(
            loadBalancerId)

        state = self._get_netscaler_monitor_from_healthmonitor(
            loadBalancerId, loadBalancer, healthMonitor)

        task = {}
        task["type"] = "lbmonitor"
        task["name"] = monitor_name
        task["operation"] = "ADD"
        task["state"] = state

        return task

    def _get_netscaler_removemonitor_task(self, loadBalancerId, monitor):

        self.logger.debug("Building task to remove monitor")

        task = {}
        task["type"] = "lbmonitor"
        task["name"] = monitor["monitorname"] + "?args=" + "type:" + monitor[
            "type"]
        task["operation"] = "REMOVE"
        task["state"] = None

        return task

    def get_netscaler_addmonitorbinding_task(self, loadBalancerId, node,
                                             healthMonitor):

        binding = self._get_netscaler_monitorbinding_from_healthmonitor(
            loadBalancerId, node, healthMonitor)

        task = {}
        task["type"] = "lbmonitor_service_binding"
        task["name"] = binding["monitorname"]
        task["state"] = binding
        task["operation"] = "ADD"

        return task

    def _get_netscaler_addmonitorbinding_tasks(self, loadBalancerId,
                                               loadBalancer, healthMonitor):

        task_list = []

        if not "nodes" in loadBalancer or not loadBalancer.nodes:
            return task_list

        for node in loadBalancer.nodes:
            task = self.get_netscaler_addmonitorbinding_task(
                loadBalancerId, node, healthMonitor)
            task_list.append(task)

        return task_list

    def get_netscaler_removemonitorbinding_task(self, loadBalancerId,
                                                loadBalancer, nodeId):

        monitor_name = NitroUtils.get_monitorname_from_loadBalancerId(
            loadBalancerId)
        servicename = NitroUtils.get_servicename_from_nodeid(
            loadBalancerId, nodeId)

        args = "servicename:" + servicename

        task = {}
        task["type"] = "lbmonitor_service_binding"
        task["name"] = monitor_name + "?args=" + args
        task["operation"] = "REMOVE"
        task["state"] = None

        return task

    def _get_netscaler_removemonitorbinding_tasks(self, loadBalancerId,
                                                  loadBalancer):

        task_list = []

        for node in loadBalancer.nodes:
            task = self.get_netscaler_removemonitorbinding_task(
                loadBalancerId, loadBalancer, node.id)
            task_list.append(task)

        return task_list

    def _get_netscaler_updatemonitor_task(self, loadBalancerId, loadBalancer,
                                          healthMonitor):

        monitor_name = NitroUtils.get_monitorname_from_loadBalancerId(
            loadBalancerId)

        state = self._get_netscaler_monitor_from_healthmonitor(
            loadBalancerId, loadBalancer, healthMonitor)

        if len(state.keys()) < 3:
            return None

        task = {}
        task["type"] = "lbmonitor"
        task["name"] = monitor_name
        task["operation"] = "UPDATE"
        task["state"] = state

        return task

    """ This version doesn't bind the monitor to nodes """

    def get_netscaler_loadbalancer_addhealthmonitor_tasks(
            self, loadBalancerId, loadBalancer, healthMonitor):

        task_list = []
        """ This loadBalancer hasn't got monitors. We need to add this """
        task = self._get_netscaler_addmonitor_task(loadBalancerId,
                                                   loadBalancer, healthMonitor)

        if task:
            task_list.append(task)

        return task_list

    def get_netscaler_addhealthmonitor_tasks(self, loadBalancerId,
                                             loadBalancer, healthMonitor):

        task_list = []
        """ This loadBalancer hasn't got monitors. We need to add this """
        task = self._get_netscaler_addmonitor_task(loadBalancerId,
                                                   loadBalancer, healthMonitor)

        if task:
            task_list.append(task)
        """ We also need to add tasks that bind this monitor to all existing nodes """
        if loadBalancer.nodes:
            for node in loadBalancer.nodes:
                task = self.get_netscaler_addmonitorbinding_task(
                    loadBalancerId, node, healthMonitor)

                if task:
                    task_list.append(task)

        return task_list

    def _get_netscaler_updatemonitor_tasks(self, loadBalancerId, loadBalancer,
                                           healthMonitor):

        task_list = []

        monitor_name = NitroUtils.get_monitorname_from_loadBalancerId(
            loadBalancerId)

        if "healthMonitor" in loadBalancer and loadBalancer.healthMonitor:
            existingType = loadBalancer.healthMonitor.type.upper()

            if "type" in healthMonitor:
                newType = healthMonitor.type.upper()
            else:
                healthMonitor.type = loadBalancer.healthMonitor.type
                newType = existingType

            if newType != existingType:
                """ The monitor type is different, we need to remove the existing health monitor 
                    and add a new one """

                tasks = self.get_netscaler_removehealthmonitor_tasks(
                    loadBalancerId, loadBalancer)

                if tasks:
                    task_list.extend(tasks)
                    self._adjust_healthmonitor_state(
                        healthMonitor, loadBalancer.healthMonitor)
                    tasks = self.get_netscaler_addhealthmonitor_tasks(
                        loadBalancerId, loadBalancer, healthMonitor)

                    if tasks:
                        task_list.extend(tasks)
            else:
                self._adjust_healthmonitor_state(healthMonitor,
                                                 loadBalancer.healthMonitor)
                task = self._get_netscaler_updatemonitor_task(
                    loadBalancerId, loadBalancer, healthMonitor)

                if task:
                    task_list.append(task)

        return task_list

    def get_netscaler_updatehealthmonitor_tasks(self, loadBalancerId,
                                                loadBalancer, healthMonitor):

        task_list = []
        """ Let's first find out if there is already a monitor for this load balancer """

        monitor = self._get_netscaler_loadBalancer_monitor(
            loadBalancerId, loadBalancer)

        if monitor == None:
            return self.get_netscaler_addhealthmonitor_tasks(
                loadBalancerId, loadBalancer, healthMonitor)

        if not monitor["type"] == "TCP" and not monitor["type"] == "USER":
            self.logger.debug("We expect only TCP or USER monitor. Found: %s" %
                              monitor["type"])
            raise ImplementationErrorException("programming error")
        """ This loadBalancer has already got a health monitor. """
        tasks = self._get_netscaler_updatemonitor_tasks(
            loadBalancerId, loadBalancer, healthMonitor)

        if tasks:
            task_list.extend(tasks)

        return task_list

    def get_netscaler_removehealthmonitor_tasks(self, loadBalancerId,
                                                loadBalancer):

        task_list = []
        """ Let's first find out if there are already monitors for this load balancer """

        monitor = self._get_netscaler_loadBalancer_monitor(
            loadBalancerId, loadBalancer)

        if monitor:

            if loadBalancer and loadBalancer.nodes:
                """ Tasks to remove the monitor bindings to the nodes of the load balancer """
                tasks = self._get_netscaler_removemonitorbinding_tasks(
                    loadBalancerId, loadBalancer)

                if tasks:
                    task_list.extend(tasks)

            self.logger.debug("Adding task to remove monitor")
            """ A task to remove the monitor itself now """
            task = self._get_netscaler_removemonitor_task(
                loadBalancerId, monitor)

            if task:
                task_list.append(task)

        return task_list

    def _get_netscaler_tasks_for_get_op(self, loadBalancerId, loadBalancer):

        task_list = []

        tasks = self.get_netscaler_gethealthmonitor_tasks(
            loadBalancerId, loadBalancer)

        task_list.extend(tasks)

        return task_list

    def _validate_healthmonitor(self, loadBalancerId, loadBalancer,
                                healthMonitor):

        if "timeout" in healthMonitor and "delay" in healthMonitor:
            delay_val = int(healthMonitor.delay)
            timeout_val = int(healthMonitor.timeout)

            if timeout_val > delay_val:
                raise LoadBalancerFaultException(
                    "health monitor timeout value must be less than delay value"
                )

    def _get_netscaler_tasks_for_update_op(self, loadBalancerId, loadBalancer,
                                           healthMonitor):

        if not healthMonitor:
            raise ImplementationErrorException("Programming error")

        self._validate_healthmonitor(loadBalancerId, loadBalancer,
                                     healthMonitor)

        task_list = self.get_netscaler_updatehealthmonitor_tasks(
            loadBalancerId, loadBalancer, healthMonitor)

        return task_list

    def _get_netscaler_tasks_for_remove_op(self, loadBalancerId, loadBalancer):

        task_list = []

        if not all([loadBalancerId, loadBalancer]):
            raise ImplementationErrorException("Programming error")

        tasks = self.get_netscaler_removehealthmonitor_tasks(
            loadBalancerId, loadBalancer)
        task_list.extend(tasks)

        return task_list

    def _get_netscaler_tasks(self,
                             operation,
                             loadBalancerId,
                             loadBalancer,
                             healthMonitor=None):

        if operation == "GET":
            return self._get_netscaler_tasks_for_get_op(
                loadBalancerId, loadBalancer)

        if operation == "UPDATE":
            return self._get_netscaler_tasks_for_update_op(
                loadBalancerId, loadBalancer, healthMonitor)

        if operation == "REMOVE":
            return self._get_netscaler_tasks_for_remove_op(
                loadBalancerId, loadBalancer)
        """ We should never get here """
        raise ImplementationError("programming error")

    def _get_LBHealthMonitorInternal(self, loadBalancerId, loadBalancer):

        task_list = self._get_netscaler_tasks("GET", loadBalancerId,
                                              loadBalancer)

        self.logger.debug("GET nodes tasklist: " + str(task_list))

        results = self.nitrowrapper.process_netscaler_task_list(task_list)

        monitor_name = NitroUtils.get_monitorname_from_loadBalancerId(
            loadBalancerId)

        monitor = NitroTasks.extract_monitor_from_task_list(
            results, monitor_name)

        healthMonitor = self.get_netscaler_healthmonitor_from_monitor(
            loadBalancerId, loadBalancer, monitor)

        return healthMonitor

    def _update_LBHealthMonitorInternal(self, loadBalancerId, loadBalancer,
                                        healthMonitor):

        task_list = self._get_netscaler_tasks("UPDATE", loadBalancerId,
                                              loadBalancer, healthMonitor)

        self.logger.debug("update task list: " + str(task_list))

        if task_list:
            self.nitrowrapper.process_netscaler_task_list(task_list)

        return self._get_LBHealthMonitorInternal(loadBalancerId, loadBalancer)

    def _remove_LBHealthMonitorInternal(self, loadBalancerId, loadBalancer):

        task_list = self._get_netscaler_tasks("REMOVE", loadBalancerId,
                                              loadBalancer)

        self.logger.debug("remove task list: " + str(task_list))

        self.nitrowrapper.process_netscaler_task_list(task_list)

    def _setup_nitrowrapper(self, tenant_id, loadBalancerId=None):

        connection = self.connmanager.get_nitro_connection(
            tenant_id, loadBalancerId)
        self.usermonitor = connection.monitor_script
        self.nitrowrapper = NitroCallsWrapper.get_nitrowrapper(
            connection, self.logger)
        self.lbadapter = NitroLoadBalancerAdapter.NitroLoadBalancerAdapter(
            self.lbresource, self.extensions_enabled)

    def get_LBHealthMonitor(self, tenant_id, loadBalancerId):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        loadBalancer = self.lbadapter.get_LoadBalancer(loadBalancerId,
                                                       tenant_id)

        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(
            tenant_id, loadBalancerId, loadBalancer)

        healthMonitor = self._get_LBHealthMonitorInternal(
            internal_loadBalancerId, loadBalancer)

        return healthMonitor

    def update_LBHealthMonitor(self, tenant_id, loadBalancerId, healthMonitor):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        loadBalancer = self.lbadapter.get_LoadBalancer(loadBalancerId,
                                                       tenant_id)

        if loadBalancer.status == "DELETED":
            raise ItemNotFoundException("loadbalancer %s not found" %
                                        loadBalancerId)

        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(
            tenant_id, loadBalancerId, loadBalancer)

        healthMonitor = self._update_LBHealthMonitorInternal(
            internal_loadBalancerId, loadBalancer, healthMonitor)

        self.db.update_loadBalancer_updatetime(tenant_id, loadBalancerId)

        return healthMonitor

    def remove_LBHealthMonitor(self, tenant_id, loadBalancerId):

        self._setup_nitrowrapper(tenant_id, loadBalancerId)

        loadBalancer = self.lbadapter.get_LoadBalancer(loadBalancerId,
                                                       tenant_id)

        internal_loadBalancerId = NitroUtils.get_internal_loadBalancerId(
            tenant_id, loadBalancerId, loadBalancer)

        self._remove_LBHealthMonitorInternal(internal_loadBalancerId,
                                             loadBalancer)

        self.db.update_loadBalancer_updatetime(tenant_id, loadBalancerId)