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)
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 )
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
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
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 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) 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