Beispiel #1
0
    def get_interfaces(self, management_address):
        """Lists interfaces attached to the resource.

        This lists the interfaces attached to the resource from the POV
        of the resource iteslf.

        :returns: A list of interfaces
        """
        return akanda_client.get_interfaces(management_address, self.mgt_port)
Beispiel #2
0
    def get_interfaces(self, management_address):
        """Lists interfaces attached to the resource.

        This lists the interfaces attached to the resource from the POV
        of the resource iteslf.

        :returns: A list of interfaces
        """
        return akanda_client.get_interfaces(management_address,
                                            self.mgt_port)
Beispiel #3
0
    def test_get_interfaces(self):
        self.mock_get.return_value.status_code = 200
        self.mock_get.return_value.json.return_value = {
            'interfaces': 'the_interfaces'
        }

        self.assertEqual(akanda_client.get_interfaces('fe80::2', 5000),
                         'the_interfaces')
        self.mock_get.assert_called_once_with(
            'http://[fe80::2]:5000/v1/system/interfaces', timeout=30)
    def test_get_interfaces(self):
        self.mock_get.return_value.status_code = 200
        self.mock_get.return_value.json.return_value = {
            'interfaces': 'the_interfaces'
        }

        self.assertEqual(akanda_client.get_interfaces('fe80::2', 5000),
                         'the_interfaces')
        self.mock_get.assert_called_once_with(
            'http://[fe80::2]:5000/v1/system/interfaces',
            timeout=30
        )
Beispiel #5
0
    def configure(self, worker_context, failure_state=RESTART, attempts=None):
        self.log.debug('Begin router config')
        self.state = UP
        attempts = attempts or cfg.CONF.max_retries

        # FIXME: This might raise an error, which doesn't mean the
        # *router* is broken, but does mean we can't update it.
        # Change the exception to something the caller can catch
        # safely.
        self._ensure_cache(worker_context)
        if self.state == GONE:
            return

        addr = _get_management_address(self.router_obj)

        # FIXME: This should raise an explicit exception so the caller
        # knows that we could not talk to the router (versus the issue
        # above).
        interfaces = router_api.get_interfaces(
            addr, cfg.CONF.akanda_mgt_service_port)

        if not self._verify_interfaces(self.router_obj, interfaces):
            # FIXME: Need a REPLUG state when we support hot-plugging
            # interfaces.
            self.log.debug("Interfaces aren't plugged as expected.")
            self.state = REPLUG
            return

        # FIXME: Need to catch errors talking to neutron here.
        config = configuration.build_config(worker_context.neutron,
                                            self.router_obj, interfaces)
        self.log.debug('preparing to update config to %r', config)

        for i in xrange(attempts):
            try:
                router_api.update_config(addr,
                                         cfg.CONF.akanda_mgt_service_port,
                                         config)
            except Exception:
                if i == attempts - 1:
                    # Only log the traceback if we encounter it many times.
                    self.log.exception('failed to update config')
                else:
                    self.log.debug('failed to update config, attempt %d', i)
                time.sleep(cfg.CONF.retry_delay)
            else:
                self.state = CONFIGURED
                self.log.info('Router config updated')
                return
        else:
            # FIXME: We failed to configure the router too many times,
            # so restart it.
            self.state = failure_state
Beispiel #6
0
    def configure(self, worker_context, failure_state=RESTART, attempts=None):
        self.log.debug("Begin router config")
        self.state = UP
        attempts = attempts or cfg.CONF.max_retries

        # FIXME: This might raise an error, which doesn't mean the
        # *router* is broken, but does mean we can't update it.
        # Change the exception to something the caller can catch
        # safely.
        self._ensure_cache(worker_context)
        if self.state == GONE:
            return

        addr = _get_management_address(self.router_obj)

        # FIXME: This should raise an explicit exception so the caller
        # knows that we could not talk to the router (versus the issue
        # above).
        interfaces = router_api.get_interfaces(addr, cfg.CONF.akanda_mgt_service_port)

        if not self._verify_interfaces(self.router_obj, interfaces):
            # FIXME: Need a REPLUG state when we support hot-plugging
            # interfaces.
            self.log.debug("Interfaces aren't plugged as expected.")
            self.state = REPLUG
            return

        # FIXME: Need to catch errors talking to neutron here.
        config = configuration.build_config(worker_context.neutron, self.router_obj, interfaces)
        self.log.debug("preparing to update config to %r", config)

        for i in xrange(attempts):
            try:
                router_api.update_config(addr, cfg.CONF.akanda_mgt_service_port, config)
            except Exception:
                if i == attempts - 1:
                    # Only log the traceback if we encounter it many times.
                    self.log.exception("failed to update config")
                else:
                    self.log.debug("failed to update config, attempt %d", i)
                time.sleep(cfg.CONF.retry_delay)
            else:
                self.state = CONFIGURED
                self.log.info("Router config updated")
                return
        else:
            # FIXME: We failed to configure the router too many times,
            # so restart it.
            self.state = failure_state
Beispiel #7
0
    def test_get_interfaces(self):
        self.mock_get.return_value.status_code = 200
        self.mock_get.return_value.json.return_value = {"interfaces": "the_interfaces"}

        self.assertEqual(akanda_client.get_interfaces("fe80::2", 5000), "the_interfaces")
        self.mock_get.assert_called_once_with("http://[fe80::2]:5000/v1/system/interfaces", timeout=30)
Beispiel #8
0
    def replug(self, worker_context):
        self.log.debug('Attempting to replug...')
        self._ensure_provider_ports(self.router_obj, worker_context)

        addr = _get_management_address(self.router_obj)
        interfaces = router_api.get_interfaces(
            addr,
            cfg.CONF.akanda_mgt_service_port
        )
        actual_macs = set((iface['lladdr'] for iface in interfaces))

        expected_ports = dict(
            (p.mac_address, p) for p in self.router_obj.internal_ports
        )
        expected_macs = set(expected_ports.keys())
        expected_macs.add(self.router_obj.management_port.mac_address)
        expected_macs.add(self.router_obj.external_port.mac_address)

        ports_to_delete = []
        if expected_macs != actual_macs:
            instance = worker_context.nova_client.get_instance(self.router_obj)

            # For each port that doesn't have a mac address on the VM...
            for mac in expected_macs - actual_macs:
                port = expected_ports.get(mac)
                if port:
                    self.log.debug(
                        'New port %s, %s found, plugging...' % (port.id, mac)
                    )
                    instance.interface_attach(port.id, None, None)

            # For each *extra* mac address on the VM...
            for mac in actual_macs - expected_macs:
                interface_ports = map(
                    quantum.Port.from_dict,
                    worker_context.neutron.api_client.list_ports(
                        device_id=instance.id,
                        device_owner=quantum.DEVICE_OWNER_ROUTER_INT
                    )['ports']
                )
                for port in interface_ports:
                    if port.mac_address == mac:
                        # If we find a router-interface port attached to the
                        # device (meaning the interface has been removed
                        # from the neutron router, but not the VM), detach the
                        # port from the Nova instance and mark the orphaned
                        # port for deletion
                        self.log.debug(''.join([
                            'Port %s, %s is detached from ' % (port.id, mac),
                            'the neutron router, unplugging...'
                        ]))
                        instance.interface_detach(port.id)
                        ports_to_delete.append(port)

        # The action of attaching/detaching interfaces in Nova happens via the
        # message bus and is *not* blocking.  We need to wait a few seconds to
        # see if the list of tap devices on the appliance actually changed.  If
        # not, assume the hotplug failed, and reboot the VM.
        replug_seconds = cfg.CONF.hotplug_timeout
        while replug_seconds > 0:
            self.log.debug(
                "Waiting for interface attachments to take effect..."
            )
            interfaces = router_api.get_interfaces(
                addr,
                cfg.CONF.akanda_mgt_service_port
            )
            if self._verify_interfaces(self.router_obj, interfaces):
                # If the interfaces now match (hotplugging was successful), go
                # ahead and clean up any orphaned neutron ports that may have
                # been detached
                for port in ports_to_delete:
                    self.log.debug('Deleting orphaned port %s' % port.id)
                    worker_context.neutron.api_client.update_port(
                        port.id, {'port': {'device_owner': ''}}
                    )
                    worker_context.neutron.api_client.delete_port(port.id)
                return
            time.sleep(1)
            replug_seconds -= 1

        self.log.debug("Interfaces aren't plugged as expected, rebooting.")
        self.state = RESTART
Beispiel #9
0
    def replug(self, worker_context):
        self.log.debug('Attempting to replug...')
        self._ensure_provider_ports(self.router_obj, worker_context)

        addr = _get_management_address(self.router_obj)
        interfaces = router_api.get_interfaces(
            addr, cfg.CONF.akanda_mgt_service_port)
        actual_macs = set((iface['lladdr'] for iface in interfaces))

        expected_ports = dict(
            (p.mac_address, p) for p in self.router_obj.internal_ports)
        expected_macs = set(expected_ports.keys())
        expected_macs.add(self.router_obj.management_port.mac_address)
        expected_macs.add(self.router_obj.external_port.mac_address)

        ports_to_delete = []
        if expected_macs != actual_macs:
            instance = worker_context.nova_client.get_instance(self.router_obj)

            # For each port that doesn't have a mac address on the VM...
            for mac in expected_macs - actual_macs:
                port = expected_ports.get(mac)
                if port:
                    self.log.debug('New port %s, %s found, plugging...' %
                                   (port.id, mac))
                    instance.interface_attach(port.id, None, None)

            # For each *extra* mac address on the VM...
            for mac in actual_macs - expected_macs:
                interface_ports = map(
                    quantum.Port.from_dict,
                    worker_context.neutron.api_client.list_ports(
                        device_id=instance.id,
                        device_owner=quantum.DEVICE_OWNER_ROUTER_INT)['ports'])
                for port in interface_ports:
                    if port.mac_address == mac:
                        # If we find a router-interface port attached to the
                        # device (meaning the interface has been removed
                        # from the neutron router, but not the VM), detach the
                        # port from the Nova instance and mark the orphaned
                        # port for deletion
                        self.log.debug(''.join([
                            'Port %s, %s is detached from ' % (port.id, mac),
                            'the neutron router, unplugging...'
                        ]))
                        instance.interface_detach(port.id)
                        ports_to_delete.append(port)

        # The action of attaching/detaching interfaces in Nova happens via the
        # message bus and is *not* blocking.  We need to wait a few seconds to
        # see if the list of tap devices on the appliance actually changed.  If
        # not, assume the hotplug failed, and reboot the VM.
        replug_seconds = cfg.CONF.hotplug_timeout
        while replug_seconds > 0:
            self.log.debug(
                "Waiting for interface attachments to take effect...")
            interfaces = router_api.get_interfaces(
                addr, cfg.CONF.akanda_mgt_service_port)
            if self._verify_interfaces(self.router_obj, interfaces):
                # If the interfaces now match (hotplugging was successful), go
                # ahead and clean up any orphaned neutron ports that may have
                # been detached
                for port in ports_to_delete:
                    self.log.debug('Deleting orphaned port %s' % port.id)
                    worker_context.neutron.api_client.update_port(
                        port.id, {'port': {
                            'device_owner': ''
                        }})
                    worker_context.neutron.api_client.delete_port(port.id)
                return
            time.sleep(1)
            replug_seconds -= 1

        self.log.debug("Interfaces aren't plugged as expected, rebooting.")
        self.state = RESTART
    def replug(self, worker_context):
        self.log.debug("Attempting to replug...")
        self._ensure_provider_ports(self.router_obj, worker_context)

        interfaces = router_api.get_interfaces(self.instance_info.management_address, cfg.CONF.akanda_mgt_service_port)
        actual_macs = set((iface["lladdr"] for iface in interfaces))
        instance_macs = set(p.mac_address for p in self.instance_info.ports)
        instance_macs.add(self.instance_info.management_port.mac_address)

        if instance_macs != actual_macs:
            # our cached copy of the ports is wrong reboot and clean up
            self.log.warning(
                _LW("Instance macs(%s) do not match actual macs (%s). " "Instance cache appears out-of-sync"),
                instance_macs,
                actual_macs,
            )
            self.state = RESTART
            return

        instance_ports = {p.network_id: p for p in self.instance_info.ports}
        instance_networks = set(instance_ports.keys())

        logical_networks = set(p.network_id for p in self.router_obj.ports)

        if logical_networks != instance_networks:
            instance = worker_context.nova_client.get_instance_by_id(self.instance_info.id_)

            # For each port that doesn't have a mac address on the instance...
            for network_id in logical_networks - instance_networks:
                port = worker_context.neutron.create_vrrp_port(self.router_obj.id, network_id)
                self.log.debug("Net %s is missing from the router, plugging: %s", network_id, port.id)

                try:
                    instance.interface_attach(port.id, None, None)
                except:
                    self.log.exception(_LE("Interface attach failed"))
                    self.state = RESTART
                    return
                self.instance_info.ports.append(port)

            for network_id in instance_networks - logical_networks:
                port = instance_ports[network_id]
                self.log.debug("Net %s is detached from the router, unplugging: %s", network_id, port.id)

                try:
                    instance.interface_detach(port.id)
                except:
                    self.log.exception(_LE("Interface detach failed"))
                    self.state = RESTART
                    return

                self.instance_info.ports.remove(port)

        # The action of attaching/detaching interfaces in Nova happens via the
        # message bus and is *not* blocking.  We need to wait a few seconds to
        # see if the list of tap devices on the appliance actually changed.  If
        # not, assume the hotplug failed, and reboot the Instance.
        replug_seconds = cfg.CONF.hotplug_timeout
        while replug_seconds > 0:
            self.log.debug("Waiting for interface attachments to take effect...")
            interfaces = router_api.get_interfaces(
                self.instance_info.management_address, cfg.CONF.akanda_mgt_service_port
            )
            if self._verify_interfaces(self.router_obj, interfaces):
                # replugging was successful
                # TODO(mark) update port states
                return
            time.sleep(1)
            replug_seconds -= 1

        self.log.debug("Interfaces aren't plugged as expected, rebooting.")
        self.state = RESTART
    def configure(self, worker_context, failure_state=RESTART, attempts=None):
        self.log.debug("Begin router config")
        self.state = UP
        attempts = attempts or cfg.CONF.max_retries

        # FIXME: This might raise an error, which doesn't mean the
        # *router* is broken, but does mean we can't update it.
        # Change the exception to something the caller can catch
        # safely.
        self._ensure_cache(worker_context)
        if self.state == GONE:
            return

        # FIXME: This should raise an explicit exception so the caller

        # knows that we could not talk to the router (versus the issue
        # above).
        interfaces = router_api.get_interfaces(self.instance_info.management_address, cfg.CONF.akanda_mgt_service_port)

        if not self._verify_interfaces(self.router_obj, interfaces):
            # FIXME: Need a REPLUG state when we support hot-plugging
            # interfaces.
            self.log.debug("Interfaces aren't plugged as expected.")
            self.state = REPLUG
            return

        # TODO(mark): We're in the first phase of VRRP, so we need
        # map the interface to the network ID.
        # Eventually we'll send VRRP data and real interface data
        port_mac_to_net = {p.mac_address: p.network_id for p in self.instance_info.ports}
        # Add in the management port
        mgt_port = self.instance_info.management_port
        port_mac_to_net[mgt_port.mac_address] = mgt_port.network_id

        # this is a network to logical interface id
        iface_map = {port_mac_to_net[i["lladdr"]]: i["ifname"] for i in interfaces if i["lladdr"] in port_mac_to_net}

        # FIXME: Need to catch errors talking to neutron here.
        config = configuration.build_config(worker_context.neutron, self.router_obj, mgt_port, iface_map)
        self.log.debug("preparing to update config to %r", config)

        for i in xrange(attempts):
            try:
                router_api.update_config(
                    self.instance_info.management_address, cfg.CONF.akanda_mgt_service_port, config
                )
            except Exception:
                if i == attempts - 1:
                    # Only log the traceback if we encounter it many times.
                    self.log.exception(_LE("Failed to update config"))
                else:
                    self.log.debug("failed to update config, attempt %d", i)
                time.sleep(cfg.CONF.retry_delay)
            else:
                self.state = CONFIGURED
                self.log.info(_LI("Router config updated"))
                return
        else:
            # FIXME: We failed to configure the router too many times,
            # so restart it.
            self.state = failure_state