def __init__(self, plugin):
     self.openstack_connector = OpenStackInterface()
     # Build a list of all vTMs in the cluster
     self.vtms = [
         vTM(
             "https://%s:%s/api/tm/%s"
             % (server, cfg.CONF.vtm_settings.rest_port, cfg.CONF.vtm_settings.api_version),
             cfg.CONF.vtm_settings.username,
             cfg.CONF.vtm_settings.password,
         )
         for server in cfg.CONF.lbaas_settings.admin_servers
     ]
     LOG.error(
         _(
             "\nShared Brocade vTM LBaaS module initialized with %s " % len(self.vtms)
             + "cluster members.\nPlease restart the Neutron "
             "server if you manually add/remove any vTMs from the cluster."
         )
     )
class BrocadeAdxDeviceDriverV2(vTMDeviceDriverCommon):
    """
    Shared vTM Cluster Version
    """

    def __init__(self, plugin):
        self.openstack_connector = OpenStackInterface()
        # Build a list of all vTMs in the cluster
        self.vtms = [
            vTM(
                "https://%s:%s/api/tm/%s"
                % (server, cfg.CONF.vtm_settings.rest_port, cfg.CONF.vtm_settings.api_version),
                cfg.CONF.vtm_settings.username,
                cfg.CONF.vtm_settings.password,
            )
            for server in cfg.CONF.lbaas_settings.admin_servers
        ]
        LOG.error(
            _(
                "\nShared Brocade vTM LBaaS module initialized with %s " % len(self.vtms)
                + "cluster members.\nPlease restart the Neutron "
                "server if you manually add/remove any vTMs from the cluster."
            )
        )

    #################
    # LOADBALANCERS #
    #################

    def create_loadbalancer(self, lb):
        LOG.debug(_("\ncreate_loadbalancer(%s): called" % lb.id))
        self.update_loadbalancer(lb, None)
        LOG.debug(_("\ncreate_loadbalancer(%s): completed!" % lb.id))

    def update_loadbalancer(self, lb, old):
        """
        Creates or updates a TrafficIP group for the loadbalancer VIP address.
        The VIP is added to the allowed_address_pairs of the ports of each
        vTM cluster member to enable them to receive traffic.
        """
        LOG.debug(_("\nupdate_loadbalancer(%s): called" % lb.id))
        try:
            vtm = self._get_vtm()
            # Create a Traffic IP group for the loadbalancer's VIP address
            tip_group_nodes = self._get_tip_group_nodes(vtm)
            tip_config = {
                "properties": {
                    "basic": {
                        "ipaddresses": [lb.vip_address],
                        "machines": tip_group_nodes["machines"],
                        "slaves": tip_group_nodes["passive"],
                        "note": "%s (%s)" % (lb.name, lb.tenant_id),
                    }
                }
            }
            vtm.tip_group.create(lb.id, config=tip_config)
            # If applicable, add IP to each vTM's "allowed-address-pairs"
            if not old:
                self.openstack_connector.add_ip_to_ports(lb.vip_address, cfg.CONF.lbaas_settings.ports)
            # If applicable, update each vTM's "allowed-address-pairs"
            elif old.vip_address != lb.vip_address:
                self.openstack_connector.add_ip_to_ports(lb.vip_address, cfg.CONF.lbaas_settings.ports)
                self.openstack_connector.delete_ip_from_ports(old.vip_address, cfg.CONF.lbaas_settings.ports)
            LOG.debug(_("\nupdate_loadbalancer(%s): completed!" % lb.id))
        except Exception as e:
            LOG.error(_("\nError in update_loadbalancer(%s): %s" % (lb.id, e)))
            LOG.trace(_("\n%s" % format_exc()))
            raise LbaasException()

    def delete_loadbalancer(self, lb):
        """
        Deletes the TrafficIP group for the loadbalancer VIP address.
        The VIP is removed from the allowed_address_pairs of the ports of each
        vTM cluster member.
        """
        LOG.debug(_("\ndelete_loadbalancer(%s): called" % lb.id))
        try:
            vtm = self._get_vtm()
            # Delete the Traffic IP group for the loadbalancer's VIP address
            vtm.tip_group.delete(lb.id)
            # Delete IP from each vTM's "allowed-address-pairs"
            self.openstack_connector.delete_ip_from_ports(lb.vip_address, cfg.CONF.lbaas_settings.ports)
            LOG.debug(_("\ndelete_loadbalancer(%s): completed!" % lb.id))
        except Exception as e:
            LOG.error(_("\nError in delete_loadbalancer(%s): %s" % (lb.id, e)))
            LOG.trace(_("\n%s" % format_exc()))
            raise LbaasException()

    #############
    # LISTENERS #
    #############

    def update_listener(self, listener, old):
        """
        Creates or updates a Virtual Server bound to the listener port.
        The IP address is that of the specified "loadbalancer", i.e.
        TrafficIP.
        Connection limiting is implemented using a Rate Class and a
        corresponding TrafficScript request rule.
        """
        LOG.debug(_("\nupdate_listener(%s): called" % listener.id))
        try:
            vtm = self._get_vtm()
            listen_on_settings = {}
            listen_on_settings["listen_on_traffic_ips"] = [listener.loadbalancer.id]
            listen_on_settings["listen_on_any"] = False
            super(BrocadeAdxDeviceDriverV2, self).update_listener(
                listener, old, vtm, listen_on_settings, False, "%s (%s)" % (listener.name, listener.tenant_id)
            )
            LOG.debug(_("\nupdate_listener(%s): completed" % listener.id))
        except Exception as e:
            LOG.error(_("\nError in update_listener(%s): %s" % (listener.id, e)))
            LOG.trace(_("\n%s" % format_exc()))
            raise LbaasException()

    def delete_listener(self, listener):
        """
        Deletes the Virtual Server associated with the listener.
        Also cleans up any associated Rate Classes and TrafficScript rules.
        """
        LOG.debug(_("\ndelete_listener(%s): called" % listener.id))
        try:
            vtm = self._get_vtm()
            super(BrocadeAdxDeviceDriverV2, self).delete_listener(listener, vtm, False)
            LOG.debug(_("\ndelete_listener(%s): completed" % listener.id))
        except Exception as e:
            LOG.error(_("\nError in delete_listener(%s): %s" % (listener.id, e)))
            LOG.trace(_("\n%s" % format_exc()))
            raise LbaasException()

    #########
    # POOLS #
    #########

    def update_pool(self, pool, old):
        """
        Creates or updates a Pool of servers.
        Session persistence is implemented using Session Persistence Classes.
        If SOURCE_IP is selected as the loadbalancing algorithm, this will
        override any other session persistence applied.
        """
        LOG.debug(_("\nupdate_pool(%s): called" % pool.id))
        try:
            vtm = self._get_vtm()
            super(BrocadeAdxDeviceDriverV2, self).update_pool(pool, old, vtm, "%s (%s)" % (pool.name, pool.tenant_id))
            LOG.debug(_("\nupdate_pool(%s): completed!" % pool.id))
        except Exception as e:
            LOG.error(_("\nError in update_pool(%s): %s" % (pool.id, e)))
            LOG.trace(_("\n%s" % format_exc()))
            raise LbaasException()

    def delete_pool(self, pool):
        """
        Deletes the vTM Pool associated with the Neutron pool.
        Also cleans up any associated Session Persistence Classes.
        """
        LOG.debug(_("\ndelete_pool(%s): called" % pool.id))
        try:
            vtm = self._get_vtm()
            super(BrocadeAdxDeviceDriverV2, self).delete_pool(pool, vtm)
            LOG.debug(_("\ndelete_pool(%s): completed!" % pool.id))
        except Exception as e:
            LOG.error(_("\nError in delete_pool(%s): %s" % (pool.id, e)))
            LOG.trace(_("\n%s" % format_exc()))
            raise LbaasException()

    ############
    # MONITORS #
    ############

    def update_healthmonitor(self, monitor, old):
        """
        Creates or updates a Health Monitor.
        """
        LOG.debug(_("\nupdate_healthmonitor(%s): called" % monitor.id))
        try:
            vtm = self._get_vtm()
            super(BrocadeAdxDeviceDriverV2, self).update_healthmonitor(
                monitor, old, vtm, "%s (%s)" % (monitor.pool.name, monitor.tenant_id)
            )
            LOG.debug(_("\nupdate_healthmonitor(%s): completed!" % monitor.id))
        except Exception as e:
            LOG.error(_("\nError in update_healthmonitor(%s): %s" % (monitor.id, e)))
            LOG.trace(_("\n%s" % format_exc()))
            raise LbaasException()

    def delete_healthmonitor(self, monitor):
        LOG.debug(_("\ndelete_healthmonitor(%s): called" % monitor.id))
        try:
            vtm = self._get_vtm()
            super(BrocadeAdxDeviceDriverV2, self).delete_healthmonitor(monitor, vtm)
            LOG.debug(_("\ndelete_healthmonitor(%s): completed!" % monitor.id))
        except Exception as e:
            LOG.error(_("\nError in delete_healthmonitor(%s): %s" % (monitor.id, e)))
            LOG.trace(_("\n%s" % format_exc()))
            raise LbaasException()

    #########
    # STATS #
    #########

    def stats(self, loadbalancer):
        LOG.debug(_("\nstats(%s): called" % loadbalancer.id))
        try:
            vtm = self._get_vtm()
            return super(BrocadeAdxDeviceDriverV2, self).stats(vtm, loadbalancer.vip_address)
        except Exception as e:
            LOG.error(_("\nError in stats(%s): %s" % (loadbalancer.id, e)))
            LOG.trace(_("\n%s" % format_exc()))
            raise LbaasException()

    ########
    # MISC #
    ########

    def _get_tip_group_nodes(self, vtm):
        # Get a tally of how many TIP groups the machine is currently in...
        cluster_members = vtm.get_nodes_in_cluster()
        tip_count = {member: {"active": 0, "total": 0} for member in cluster_members}
        for tip_id in vtm.tip_groups.list():
            tip_group = vtm.tip_group.get(tip_id)
            for member in tip_group.machines:
                if member not in tip_group.slaves:
                    tip_count[member]["active"] += 1
                tip_count[member]["total"] += 1
        # Work out which machines are best to be used for the new TIP group:
        # Choose active member...
        active = sorted(cluster_members, key=lambda m: (tip_count[m]["active"], tip_count[m]["total"]))[0]
        # Choose passive members...
        passive_member_count = min(cfg.CONF.lbaas_settings.passive_vtms, len(cluster_members) - 1)
        if passive_member_count == 0:
            passive = []
        else:
            cluster_members.remove(active)
            passive = sorted(cluster_members, key=lambda m: (tip_count[m]["total"], tip_count[m]["active"]))[
                0:passive_member_count
            ]
        return {"machines": [active] + passive, "passive": passive}

    def _get_vtm(self):
        for _ in xrange(3):
            for vtm in self.vtms:
                try:
                    if not vtm.test_connectivity():
                        raise Exception("")
                    return vtm
                except:
                    pass
            sleep(3)
        raise Exception("Could not contact any vTMs in cluster")