def teardown(pod_namespace, pod_name, docker_id): client = ContrailClient() manager = LxcManager() short_id = docker_id[0:11] api = ContrailVRouterApi() _, podName = getDockerPod(docker_id) vmi = vrouter_interface_by_name(podName) if vmi is not None: api.delete_port(vmi) manager.clear_interfaces(short_id) Shell.run('ip netns delete %s' % short_id)
class ContrailInterfaceDriver(interface.LinuxInterfaceDriver): """ Opencontrail VIF driver for neutron.""" @classmethod def _parse_class_args(cls, cfg_parser): cfg_parser.read(CONTRAIL_CFG_FILE) cls._api_server_ip = _read_cfg(cfg_parser, 'APISERVER', 'api_server_ip', '127.0.0.1') cls._api_server_port = _read_cfg(cfg_parser, 'APISERVER', 'api_server_port', '8082') cls._api_server_use_ssl = _read_cfg(cfg_parser, 'APISERVER', 'use_ssl', False) cls._auth_token_url = _read_cfg(cfg_parser, 'APISERVER', 'auth_token_url', None) def __init__(self, conf): super(ContrailInterfaceDriver, self).__init__(conf) self._port_dict = {} self._client = self._connect_to_api_server() self._vrouter_client = ContrailVRouterApi() timer = loopingcall.FixedIntervalLoopingCall(self._keep_alive) timer.start(interval=2) def _connect_to_api_server(self): cfg_parser = ConfigParser.ConfigParser() ContrailInterfaceDriver._parse_class_args(cfg_parser) try: client = VncApi(api_server_host=self._api_server_ip, api_server_port=self._api_server_port, api_server_use_ssl=self._api_server_use_ssl, auth_token_url=self._auth_token_url) return client except: pass def _keep_alive(self): self._vrouter_client.periodic_connection_check() def _delete_port(self, port_id): self._vrouter_client.delete_port(port_id) def _instance_locate(self, port_obj): """ lookup the instance associated with the port object. Create the vm instance if port object is not associated with a vm instance """ if port_obj.get_virtual_machine_refs() is not None: try: vm_uuid = port_obj.get_virtual_machine_refs()[0]['uuid'] instance_obj = self._client.virtual_machine_read(id=vm_uuid) return instance_obj except NoIdError: pass vm_uuid = str(uuid.uuid4()) instance_obj = VirtualMachine(vm_uuid) instance_obj.uuid = vm_uuid self._client.virtual_machine_create(instance_obj) port_obj.set_virtual_machine(instance_obj) self._client.virtual_machine_interface_update(port_obj) return instance_obj def _add_port_to_agent(self, port_id, net_id, iface_name, mac_address): port_obj = self._client.virtual_machine_interface_read(id=port_id) if port_obj is None: LOG.debug(_("Invalid port_id : %s"), port_id) return ips = port_obj.get_instance_ip_back_refs() ip_addr = '0.0.0.0' # get the ip address of the port if associated if ips and len(ips): ip_uuid = ips[0]['uuid'] ip = self._client.instance_ip_read(id=ip_uuid) ip_addr = ip.get_instance_ip_address() net_obj = self._client.virtual_network_read(id=net_id) if net_obj is None: LOG.debug(_("Invalid net_id : %s"), net_id) return # get the instance object the port is attached to instance_obj = self._instance_locate(port_obj) if instance_obj is None: return kwargs = {} kwargs['ip_address'] = ip_addr kwargs['network_uuid'] = net_id kwargs['vm_project_uuid'] = net_obj.parent_uuid self._vrouter_client.add_port(instance_obj.uuid, port_id, iface_name, mac_address, **kwargs) def plug(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None): if not ip_lib.device_exists(device_name, self.root_helper, namespace): ip = ip_lib.IPWrapper(self.root_helper) tap_name = device_name.replace(prefix or 'veth', 'veth') # Create ns_dev in a namespace if one is configured. root_dev, ns_dev = ip.add_veth(tap_name, device_name, namespace2=namespace) ns_dev.link.set_address(mac_address) namespace_obj = ip.ensure_namespace(namespace) namespace_obj.add_device_to_namespace(ns_dev) ns_dev.link.set_up() root_dev.link.set_up() self._add_port_to_agent(port_id, network_id, tap_name, mac_address) self._port_dict[tap_name] = port_id else: LOG.warn(_("Device %s already exists"), device_name) def unplug(self, device_name, bridge=None, namespace=None, prefix=None): tap_name = device_name.replace(prefix or 'veth', 'veth') if tap_name in self._port_dict: self._delete_port(self._port_dict[tap_name]) del self._port_dict[tap_name] device = ip_lib.IPDevice(device_name, self.root_helper, namespace) device.link.delete() LOG.debug(_("Unplugged interface '%s'"), device_name) ip_lib.IPWrapper(self.root_helper, namespace).garbage_collect_namespace()
def interface_unregister(vmi_uuid): api = ContrailVRouterApi() api.delete_port(vmi_uuid)
class OpenContrailVIFDriver(object): def __init__(self): self._vrouter_client = ContrailVRouterApi(doconnect=True) def plug(self, instance, vif): if_local_name = 'veth%s' % vif['id'][:8] if_remote_name = 'ns%s' % vif['id'][:8] # Device already exists so return. if linux_net.device_exists(if_local_name): return undo_mgr = utils.UndoManager() try: utils.execute('ip', 'link', 'add', if_local_name, 'type', 'veth', 'peer', 'name', if_remote_name, run_as_root=True) undo_mgr.undo_with(lambda: utils.execute( 'ip', 'link', 'delete', if_local_name, run_as_root=True)) utils.execute('ip', 'link', 'set', if_remote_name, 'address', vif['address'], run_as_root=True) except Exception: LOG.exception("Failed to configure network") msg = _('Failed to setup the network, rolling back') undo_mgr.rollback_and_reraise(msg=msg, instance=instance) def attach(self, instance, vif, container_id): if_local_name = 'veth%s' % vif['id'][:8] if_remote_name = 'ns%s' % vif['id'][:8] undo_mgr = utils.UndoManager() ipv4_address = '0.0.0.0' ipv6_address = None if 'subnets' in vif['network']: subnets = vif['network']['subnets'] for subnet in subnets: ips = subnet['ips'][0] if (ips['version'] == 4): if ips['address'] is not None: ipv4_address = ips['address'] if (ips['version'] == 6): if ips['address'] is not None: ipv6_address = ips['address'] params = { 'ip_address': ipv4_address, 'vn_id': vif['network']['id'], 'display_name': instance['display_name'], 'hostname': instance['hostname'], 'host': instance['host'], 'vm_project_id': instance['project_id'], 'port_type': 'NovaVMPort', 'ip6_address': ipv6_address, } try: utils.execute('ip', 'link', 'set', if_remote_name, 'netns', container_id, run_as_root=True) result = self._vrouter_client.add_port( instance['uuid'], vif['id'], if_local_name, vif['address'], **params) if not result: # follow the exception path raise RuntimeError('add_port returned %s' % str(result)) utils.execute('ip', 'link', 'set', if_local_name, 'up', run_as_root=True) except Exception: LOG.exception("Failed to attach the network") msg = _('Failed to attach the network, rolling back') undo_mgr.rollback_and_reraise(msg=msg, instance=instance) # TODO(NetNS): attempt DHCP client; fallback to manual config if the # container doesn't have an working dhcpclient utils.execute('ip', 'netns', 'exec', container_id, 'dhclient', if_remote_name, run_as_root=True) def unplug(self, instance, vif): try: self._vrouter_client.delete_port(vif['id']) except Exception: LOG.exception(_("Delete port failed"), instance=instance) if_local_name = 'veth%s' % vif['id'][:8] utils.execute('ip', 'link', 'delete', if_local_name, run_as_root=True)
class VRouterVIFDriver(LibvirtBaseVIFDriver): """VIF driver for VRouter when running Neutron.""" PORT_TYPE = 'NovaVMPort' def __init__(self, get_connection): super(VRouterVIFDriver, self).__init__(get_connection) self._vrouter_client = ContrailVRouterApi() timer = loopingcall.FixedIntervalLoopingCall(self._keep_alive) timer.start(interval=2) def _keep_alive(self): self._vrouter_client.periodic_connection_check() @staticmethod def _get_br_name(dev): """Returns the bridge name for a tap device. This is lxc related stuff. To work around the fact, that libvirt does not support direct passthrough of devices to LXC.""" return 'br%s' % dev[3:] def _create_bridge(self, dev, instance): """Creating a bridge and returning its name""" br_name = self._get_br_name(dev) try: linux_net.LinuxBridgeInterfaceDriver.ensure_bridge(br_name, dev) linux_net._execute('ip', 'link', 'set', br_name, 'promisc', 'on', run_as_root=True) except processutils.ProcessExecutionError: LOG.exception(_LE("Failed while plugging vif"), instance=instance) return br_name def get_config(self, instance, vif, image_meta, inst_type): conf = super(VRouterVIFDriver, self).get_config(instance, vif, image_meta, inst_type) dev = self.get_vif_devname(vif) if cfg.CONF.libvirt.virt_type == 'lxc': # for lxc we need to pass a bridge to libvirt br_name = self._get_br_name(dev) designer.set_vif_host_backend_bridge_config(conf, br_name) else: designer.set_vif_host_backend_ethernet_config(conf, dev) designer.set_vif_bandwidth_config(conf, inst_type) return conf def plug(self, instance, vif): dev = self.get_vif_devname(vif) try: linux_net.create_tap_dev(dev) except processutils.ProcessExecutionError: LOG.exception(_LE("Failed while plugging vif"), instance=instance) if cfg.CONF.libvirt.virt_type == 'lxc': dev = self._create_bridge(dev, instance) kwargs = { 'ip_address': vif['network']['subnets'][0]['ips'][0]['address'], 'vn_id': vif['network']['id'], 'display_name': instance['display_name'], 'hostname': instance['hostname'], 'host': instance['host'], 'vm_project_id': instance['project_id'], 'port_type': self.PORT_TYPE, } try: result = self._vrouter_client.add_port(instance['uuid'], vif['id'], dev, vif['address'], **kwargs) if not result: LOG.exception(_LE("Failed while plugging vif"), instance=instance) except TApplicationException: LOG.exception(_LE("Failed while plugging vif"), instance=instance) def unplug(self, instance, vif): dev = self.get_vif_devname(vif) try: self._vrouter_client.delete_port(vif['id']) #delegate the deletion of tap device to a deffered thread worker_thread = threading.Thread(target=self.delete_device, \ name='contrailvif', args=(dev,)) worker_thread.start() except (TApplicationException, processutils.ProcessExecutionError,\ RuntimeError): LOG.exception(_LE("Failed while unplugging vif"), instance=instance) def delete_device(self, dev): time.sleep(2) LOG.debug(dev) if cfg.CONF.libvirt.virt_type == 'lxc': linux_net.LinuxBridgeInterfaceDriver.remove_bridge( self._get_br_name(dev)) linux_net.delete_net_dev(dev)
class NetnsManager(object): SNAT_RT_TABLES_ID = 42 DEV_NAME_LEN = 14 NETNS_PREFIX = 'vrouter-' LEFT_DEV_PREFIX = 'int-' RIGH_DEV_PREFIX = 'gw-' TAP_PREFIX = 'veth' PORT_TYPE = 'NameSpacePort' LBAAS_PROCESS = 'haproxy' def __init__(self, vm_uuid, nic_left, nic_right, other_nics=None, root_helper='sudo', cfg_file=None, update=False, pool_id=None, gw_ip=None, namespace_name=None): self.vm_uuid = vm_uuid if namespace_name is None: self.namespace = self.NETNS_PREFIX + self.vm_uuid else: self.namespace = namespace_name if pool_id: self.namespace = self.namespace + ":" + pool_id self.nic_left = nic_left self.nic_right = nic_right self.root_helper = root_helper self.nics = other_nics or [] if self.nic_left: self.nic_left['name'] = (self.LEFT_DEV_PREFIX + self.nic_left['uuid'])[:self.DEV_NAME_LEN] self.nics.append(self.nic_left) if self.nic_right: self.nic_right['name'] = (self.RIGH_DEV_PREFIX + self.nic_right['uuid'])[:self.DEV_NAME_LEN] self.nics.append(self.nic_right) self.ip_ns = ip_lib.IPWrapper(root_helper=self.root_helper, namespace=self.namespace) self.vrouter_client = ContrailVRouterApi() self.cfg_file = cfg_file self.update = update self.gw_ip = gw_ip def _get_tap_name(self, uuid_str): return (self.TAP_PREFIX + uuid_str)[:self.DEV_NAME_LEN] def is_netns_already_exists(self): return self.ip_ns.netns.exists(self.namespace) def create(self): ip = ip_lib.IPWrapper(self.root_helper) ip.ensure_namespace(self.namespace) for nic in self.nics: self._create_interfaces(ip, nic) def set_snat(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Need to create the network namespace before set ' 'up the SNAT') self.ip_ns.netns.execute(['sysctl', '-w', 'net.ipv4.ip_forward=1']) self.ip_ns.netns.execute(['iptables', '-t', 'nat', '-F']) self.ip_ns.netns.execute(['iptables', '-t', 'nat', '-A', 'POSTROUTING', '-s', '0.0.0.0/0', '-o', self.nic_right['name'], '-j', 'MASQUERADE']) self.ip_ns.netns.execute(['ip', 'route', 'replace', 'default', 'dev', self.nic_right['name']]) self.ip_ns.netns.execute(['ip', 'route', 'replace', 'default', 'dev', self.nic_left['name'], 'table', self.SNAT_RT_TABLES_ID]) try: self.ip_ns.netns.execute(['ip', 'rule', 'del', 'iif', str(self.nic_right['name']), 'table', self.SNAT_RT_TABLES_ID]) except RuntimeError: pass self.ip_ns.netns.execute(['ip', 'rule', 'add', 'iif', str(self.nic_right['name']), 'table', self.SNAT_RT_TABLES_ID]) def _get_lbaas_pid(self): cmd = """ps aux | grep \'%(process)s -f %(file)s\' | grep -v grep """ % {'process':self.LBAAS_PROCESS, 'file':self.cfg_file} try: s = subprocess.check_output(cmd, shell=True) except subprocess.CalledProcessError: return None words = s.split() pid = int(words[1]) return pid def set_lbaas(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Need to create the network namespace before set ' 'up the lbaas') pid_file = self.cfg_file + ".pid" pid = self._get_lbaas_pid() if (self.update is False): if pid is not None: self.release_lbaas() self.ip_ns.netns.execute([self.LBAAS_PROCESS, '-f', self.cfg_file, '-D', '-p', pid_file]) self.ip_ns.netns.execute(['route', 'add', 'default', 'gw', self.gw_ip]) else: if pid is not None: self.ip_ns.netns.execute([self.LBAAS_PROCESS, '-f', self.cfg_file, '-D', '-p', pid_file, '-sf', pid]) else: print ("No old Haproxy process to Update for %s" %(self.cfg_file), file=sys.stderr) try: self.ip_ns.netns.execute(['route', 'add', 'default', 'gw', self.gw_ip]) except RuntimeError: pass def release_lbaas(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Need to create the network namespace before ' 'relasing lbaas') pid = self._get_lbaas_pid() if pid is not None: cmd = """kill -9 %(pid)s""" % {'pid':pid} try: s = subprocess.check_output(cmd, shell=True) print ("Haproxy process with pid %d config file %s killed" %(pid, self.cfg_file), file=sys.stderr) except subprocess.CalledProcessError: print ("SIGKILL Error for pid %d %s" %(pid, self.cfg_file), file=sys.stderr) try: self.ip_ns.netns.execute(['route', 'del', 'default']) except RuntimeError: pass def destroy(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Namespace %s does not exist' % self.namespace) for device in self.ip_ns.get_devices(exclude_loopback=True): ip_lib.IPDevice(device.name, self.root_helper, self.namespace).link.delete() self.ip_ns.netns.delete(self.namespace) def plug_namespace_interface(self): for nic in self.nics: self._add_port_to_agent(nic, display_name='NetNS %s %s interface' % (self.vm_uuid, nic['name'])) def unplug_namespace_interface(self): for nic in self.nics: self._delete_port_to_agent(nic) def _create_interfaces(self, ip, nic): if ip_lib.device_exists(nic['name'], self.root_helper, namespace=self.namespace): ip_lib.IPDevice(nic['name'], self.root_helper, self.namespace).link.delete() root_dev, ns_dev = ip.add_veth(self._get_tap_name(nic['uuid']), nic['name'], namespace2=self.namespace) if nic['mac']: ns_dev.link.set_address(str(nic['mac'])) ns_dev.link.set_up() root_dev.link.set_up() if nic['ip']: ip = nic['ip'] ns_dev.addr.flush() ns_dev.addr.add(ip.version, str(ip), str(ip.broadcast)) else: #TODO(ethuleau): start DHCP client raise NotImplementedError # disable reverse path filtering self.ip_ns.netns.execute(['sysctl', '-w', 'net.ipv4.conf.%s.rp_filter=2' % nic['name']] ) def _add_port_to_agent(self, nic, display_name=None): kwargs = {} kwargs['port_type'] = self.PORT_TYPE kwargs['ip_address'] = str(nic['ip'].ip) if display_name: kwargs['display_name'] = display_name if not (self.vrouter_client.add_port(self.vm_uuid, nic['uuid'], self._get_tap_name(nic['uuid']), str(nic['mac']), **kwargs)): raise ValueError('Cannot add interface %s on the vrouter' % nic['uuid']) def _delete_port_to_agent(self, nic): self.vrouter_client.delete_port(nic['uuid'])
class OpenContrailVIFDriver(object): def __init__(self): self._vrouter_semaphore = eventlet.semaphore.Semaphore() self._vrouter_client = ContrailVRouterApi( doconnect=True, semaphore=self._vrouter_semaphore) timer = loopingcall.FixedIntervalLoopingCall(self._keep_alive) timer.start(interval=2) def _keep_alive(self): self._vrouter_client.periodic_connection_check() def plug(self, instance, vif): vif_type = vif['type'] LOG.debug('Plug vif_type=%(vif_type)s instance=%(instance)s ' 'vif=%(vif)s', {'vif_type': vif_type, 'instance': instance, 'vif': vif}) if_local_name = 'veth%s' % vif['id'][:8] if_remote_name = 'ns%s' % vif['id'][:8] # Device already exists so return. if linux_net.device_exists(if_local_name): return undo_mgr = utils.UndoManager() try: utils.execute('ip', 'link', 'add', if_local_name, 'type', 'veth', 'peer', 'name', if_remote_name, run_as_root=True) undo_mgr.undo_with(lambda: utils.execute( 'ip', 'link', 'delete', if_local_name, run_as_root=True)) utils.execute('ip', 'link', 'set', if_remote_name, 'address', vif['address'], run_as_root=True) except Exception: LOG.exception("Failed to configure network") msg = _('Failed to setup the network, rolling back') undo_mgr.rollback_and_reraise(msg=msg, instance=instance) def attach(self, instance, vif, container_id): vif_type = vif['type'] LOG.debug('Attach vif_type=%(vif_type)s instance=%(instance)s ' 'vif=%(vif)s', {'vif_type': vif_type, 'instance': instance, 'vif': vif}) if_local_name = 'veth%s' % vif['id'][:8] if_remote_name = 'ns%s' % vif['id'][:8] undo_mgr = utils.UndoManager() undo_mgr.undo_with(lambda: utils.execute( 'ip', 'link', 'delete', if_local_name, run_as_root=True)) ipv4_address, ipv4_netmask, ipv4_gateway = self._retrieve_ip_address( vif, 4) ipv6_address, ipv6_netmask, ipv6_gateway = self._retrieve_ip_address( vif, 6) ipv4_address = ipv4_address or '0.0.0.0' params = { 'ip_address': ipv4_address, 'vn_id': vif['network']['id'], 'display_name': instance['display_name'], 'hostname': instance['hostname'], 'host': instance['host'], 'vm_project_id': instance['project_id'], 'port_type': 'NovaVMPort', 'ip6_address': ipv6_address, } try: utils.execute('ip', 'link', 'set', if_remote_name, 'netns', container_id, run_as_root=True) result = self._vrouter_client.add_port( instance['uuid'], vif['id'], if_local_name, vif['address'], **params) if not result: # follow the exception path raise RuntimeError('add_port returned %s' % str(result)) utils.execute('ip', 'link', 'set', if_local_name, 'up', run_as_root=True) except Exception: LOG.exception("Failed to attach the network") msg = _('Failed to attach the network, rolling back') undo_mgr.rollback_and_reraise(msg=msg, instance=instance) try: utils.execute('ip', 'netns', 'exec', container_id, 'ip', 'link', 'set', if_remote_name, 'address', vif['address'], run_as_root=True) if ipv6_address: ip = ipv6_address + "/" + ipv6_netmask gateway = ipv6_gateway utils.execute('ip', 'netns', 'exec', container_id, 'ifconfig', if_remote_name, 'inet6', 'add', ip, run_as_root=True) utils.execute('ip', 'netns', 'exec', container_id, 'ip', '-6', 'route', 'replace', 'default', 'via', gateway, 'dev', if_remote_name, run_as_root=True) if ipv4_address != '0.0.0.0': ip = ipv4_address + "/" + ipv4_netmask gateway = ipv4_gateway utils.execute('ip', 'netns', 'exec', container_id, 'ifconfig', if_remote_name, ip, run_as_root=True) utils.execute('ip', 'netns', 'exec', container_id, 'ip', 'route', 'replace', 'default', 'via', gateway, 'dev', if_remote_name, run_as_root=True) utils.execute('ip', 'netns', 'exec', container_id, 'ip', 'link', 'set', if_remote_name, 'up', run_as_root=True) except Exception: LOG.exception("Failed to attach vif", instance=instance) def _retrieve_ip_address(self, vif, version): address = None netmask = None gateway = None if 'subnets' in vif['network']: subnets = vif['network']['subnets'] for subnet in subnets: ips = subnet['ips'][0] if (ips['version'] == version): if ips['address'] is not None: address = ips['address'] netmask = subnet['cidr'].split('/')[1] gateway = subnet['gateway']['address'] return address, netmask, gateway def unplug(self, instance, vif): vif_type = vif['type'] if_local_name = 'veth%s' % vif['id'][:8] LOG.debug('Unplug vif_type=%(vif_type)s instance=%(instance)s ' 'vif=%(vif)s', {'vif_type': vif_type, 'instance': instance, 'vif': vif}) try: self._vrouter_client.delete_port(vif['id']) if linux_net.device_exists(if_local_name): utils.execute('ip', 'link', 'delete', if_local_name, run_as_root=True) except Exception: LOG.exception("Delete port failed", instance=instance)
class VRouterApiTest(unittest.TestCase): def setUp(self): self._api = ContrailVRouterApi() def test_create_port(self): mock_client = mock.Mock() self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = mock_client self._api.add_port(str(uuid.uuid1()), str(uuid.uuid1()), 'tapX', 'aa:bb:cc:ee:ff:00') self.assertTrue(mock_client.AddPort.called) def test_delete_port(self): mock_client = mock.Mock() self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = mock_client vm_uuid = uuid.uuid1() vif_uuid = uuid.uuid1() self._api.add_port(str(vm_uuid), str(vif_uuid), 'tapX', 'aa:bb:cc:ee:ff:00') self.assertTrue(mock_client.AddPort.called) self.assertTrue(self._api._ports[vif_uuid]) self._api.delete_port(str(vif_uuid)) self.assertTrue(mock_client.DeletePort.called) def test_resynchronize(self): self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = None vm_uuid = str(uuid.uuid1()) vif_uuid = str(uuid.uuid1()) port1 = ttypes.Port(self._api._uuid_string_to_hex(vif_uuid), self._api._uuid_string_to_hex(vm_uuid), 'tapX', '0.0.0.0', [0] * 16, 'aa:bb:cc:ee:ff:00') self._api.add_port(vm_uuid, vif_uuid, 'tapX', 'aa:bb:cc:ee:ff:00') mock_client = mock.Mock() self._api._rpc_client_instance.return_value = mock_client self._api.periodic_connection_check() mock_client.AddPort.assert_called_with([port1]) def test_resynchronize_multi_ports(self): self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = None vm_uuid = str(uuid.uuid1()) vif_uuid = str(uuid.uuid1()) port1 = ttypes.Port(self._api._uuid_string_to_hex(vif_uuid), self._api._uuid_string_to_hex(vm_uuid), 'tapX', '0.0.0.0', [0] * 16, 'aa:bb:cc:ee:ff:00') self._api.add_port(vm_uuid, vif_uuid, 'tapX', 'aa:bb:cc:ee:ff:00') vm_uuid = str(uuid.uuid1()) vif_uuid = str(uuid.uuid1()) port2 = ttypes.Port(self._api._uuid_string_to_hex(vif_uuid), self._api._uuid_string_to_hex(vm_uuid), 'tapY', '0.0.0.0', [0] * 16, '11:22:33:44:55:66') self._api.add_port(vm_uuid, vif_uuid, 'tapY', '11:22:33:44:55:66') mock_client = mock.Mock() self._api._rpc_client_instance.return_value = mock_client self._api.connect() self._api._resynchronize() mock_client.AddPort.assert_called_with([port1, port2]) def test_additional_arguments(self): mock_client = mock.Mock() self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = mock_client vif_uuid = uuid.uuid1() network_uuid = uuid.uuid1() project_id = uuid.uuid1().hex self._api.add_port(str(uuid.uuid1()), str(vif_uuid), 'tapX', 'aa:bb:cc:ee:ff:00', network_uuid=str(network_uuid), vm_project_id=project_id) self.assertTrue(mock_client.AddPort.called) port = self._api._ports[vif_uuid] self.assertEqual(self._api._uuid_to_hex(network_uuid), port.vn_id) self.assertEqual(self._api._uuid_string_to_hex(project_id), port.vm_project_id)
class NetnsManager(object): SNAT_RT_TABLES_ID = 42 DEV_NAME_LEN = 14 NETNS_PREFIX = 'vrouter-' LEFT_DEV_PREFIX = 'int-' RIGH_DEV_PREFIX = 'gw-' TAP_PREFIX = 'veth' PORT_TYPE = 'NameSpacePort' LBAAS_PROCESS = 'haproxy' def __init__(self, vm_uuid, nic_left, nic_right, other_nics=None, root_helper='sudo', cfg_file=None, update=False, pool_id=None, gw_ip=None, namespace_name=None): self.vm_uuid = vm_uuid if namespace_name is None: self.namespace = self.NETNS_PREFIX + self.vm_uuid else: self.namespace = namespace_name if pool_id: self.namespace = self.namespace + ":" + pool_id self.nic_left = nic_left self.nic_right = nic_right self.root_helper = root_helper self.nics = other_nics or [] if self.nic_left: self.nic_left['name'] = (self.LEFT_DEV_PREFIX + self.nic_left['uuid'])[:self.DEV_NAME_LEN] self.nics.append(self.nic_left) if self.nic_right: self.nic_right['name'] = (self.RIGH_DEV_PREFIX + self.nic_right['uuid'])[:self.DEV_NAME_LEN] self.nics.append(self.nic_right) self.ip_ns = ip_lib.IPWrapper(root_helper=self.root_helper, namespace=self.namespace) self.vrouter_client = ContrailVRouterApi() self.cfg_file = cfg_file self.update = update self.gw_ip = gw_ip def _get_tap_name(self, uuid_str): return (self.TAP_PREFIX + uuid_str)[:self.DEV_NAME_LEN] def is_netns_already_exists(self): return self.ip_ns.netns.exists(self.namespace) def create(self): ip = ip_lib.IPWrapper(self.root_helper) ip.ensure_namespace(self.namespace) for nic in self.nics: self._create_interfaces(ip, nic) def set_snat(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Need to create the network namespace before set ' 'up the SNAT') self.ip_ns.netns.execute(['sysctl', '-w', 'net.ipv4.ip_forward=1']) self.ip_ns.netns.execute(['iptables', '-t', 'nat', '-F']) self.ip_ns.netns.execute(['iptables', '-t', 'nat', '-A', 'POSTROUTING', '-s', '0.0.0.0/0', '-o', self.nic_right['name'], '-j', 'MASQUERADE']) self.ip_ns.netns.execute(['ip', 'route', 'replace', 'default', 'dev', self.nic_right['name']]) self.ip_ns.netns.execute(['ip', 'route', 'replace', 'default', 'dev', self.nic_left['name'], 'table', self.SNAT_RT_TABLES_ID]) try: self.ip_ns.netns.execute(['ip', 'rule', 'del', 'iif', str(self.nic_right['name']), 'table', self.SNAT_RT_TABLES_ID]) except RuntimeError: pass self.ip_ns.netns.execute(['ip', 'rule', 'add', 'iif', str(self.nic_right['name']), 'table', self.SNAT_RT_TABLES_ID]) self.ip_ns.netns.execute(['ip', 'route', 'del', 'default', 'table', self.SNAT_RT_TABLES_ID]) self.ip_ns.netns.execute(['ip', 'route', 'add', 'default', 'table', self.SNAT_RT_TABLES_ID, 'via', self.gw_ip, 'dev', str(self.nic_left['name'])]) def _get_lbaas_pid(self): cmd = """ps aux | grep \'%(process)s -f %(file)s\' | grep -v grep """ % {'process':self.LBAAS_PROCESS, 'file':self.cfg_file} try: if "check_output" not in dir(subprocess): s = _check_output(cmd) else: s = subprocess.check_output(cmd, shell=True) except subprocess.CalledProcessError: return None words = s.split() pid = int(words[1]) return pid def set_lbaas(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Need to create the network namespace before set ' 'up the lbaas') pid_file = self.cfg_file + ".pid" pid = self._get_lbaas_pid() if (self.update is False): if pid is not None: self.release_lbaas() self.ip_ns.netns.execute([self.LBAAS_PROCESS, '-f', self.cfg_file, '-D', '-p', pid_file]) self.ip_ns.netns.execute(['route', 'add', 'default', 'gw', self.gw_ip]) else: if pid is not None: self.ip_ns.netns.execute([self.LBAAS_PROCESS, '-f', self.cfg_file, '-D', '-p', pid_file, '-sf', pid]) else: self.ip_ns.netns.execute([self.LBAAS_PROCESS, '-f', self.cfg_file, '-D', '-p', pid_file]) try: self.ip_ns.netns.execute(['route', 'add', 'default', 'gw', self.gw_ip]) except RuntimeError: pass def release_lbaas(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Need to create the network namespace before ' 'relasing lbaas') pid = self._get_lbaas_pid() if pid is not None: cmd = """kill -9 %(pid)s""" % {'pid':pid} try: if "check_output" not in dir(subprocess): s = _check_output(cmd) else: s = subprocess.check_output(cmd, shell=True) print ("Haproxy process with pid %d config file %s killed" %(pid, self.cfg_file), file=sys.stderr) except subprocess.CalledProcessError: print ("SIGKILL Error for pid %d %s" %(pid, self.cfg_file), file=sys.stderr) try: self.ip_ns.netns.execute(['route', 'del', 'default']) except RuntimeError: pass def destroy(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Namespace %s does not exist' % self.namespace) for device in self.ip_ns.get_devices(exclude_loopback=True): ip_lib.IPDevice(device.name, self.root_helper, self.namespace).link.delete() self.ip_ns.netns.delete(self.namespace) def plug_namespace_interface(self): for nic in self.nics: self._add_port_to_agent(nic, display_name='NetNS %s %s interface' % (self.vm_uuid, nic['name'])) def unplug_namespace_interface(self): for nic in self.nics: self._delete_port_to_agent(nic) def _create_interfaces(self, ip, nic): if ip_lib.device_exists(nic['name'], self.root_helper, namespace=self.namespace): ip_lib.IPDevice(nic['name'], self.root_helper, self.namespace).link.delete() root_dev, ns_dev = ip.add_veth(self._get_tap_name(nic['uuid']), nic['name'], namespace2=self.namespace) if nic['mac']: ns_dev.link.set_address(str(nic['mac'])) ns_dev.link.set_up() root_dev.link.set_up() if nic['ip']: ip = nic['ip'] ns_dev.addr.flush() ns_dev.addr.add(ip.version, str(ip), str(ip.broadcast)) else: #TODO(ethuleau): start DHCP client raise NotImplementedError # disable reverse path filtering self.ip_ns.netns.execute(['sysctl', '-w', 'net.ipv4.conf.%s.rp_filter=2' % nic['name']] ) def _add_port_to_agent(self, nic, display_name=None): kwargs = {} kwargs['port_type'] = self.PORT_TYPE kwargs['ip_address'] = str(nic['ip'].ip) if display_name: kwargs['display_name'] = display_name if not (self.vrouter_client.add_port(self.vm_uuid, nic['uuid'], self._get_tap_name(nic['uuid']), str(nic['mac']), **kwargs)): raise ValueError('Cannot add interface %s on the vrouter' % nic['uuid']) def _delete_port_to_agent(self, nic): self.vrouter_client.delete_port(nic['uuid'])
class VRouterAPIClient(object): """ A client for Contrail VRouter Agent REST API. """ def __init__(self): self.vrouter_api = ContrailVRouterApi() self.vrouter_host = 'http://localhost' self.vrouter_port = '9091' self.port_files_path = '/var/lib/contrail/ports/' def add_port(self, vmi_model): """ Add port to VRouter Agent. """ try: ip_address = vmi_model.ip_address if vmi_model.vnc_instance_ip: ip_address = vmi_model.vnc_instance_ip.instance_ip_address parameters = dict( vm_uuid_str=vmi_model.vm_model.uuid, vif_uuid_str=vmi_model.uuid, interface_name=vmi_model.uuid, mac_address=vmi_model.vcenter_port.mac_address, ip_address=ip_address, vn_id=vmi_model.vn_model.uuid, display_name=vmi_model.vm_model.name, vlan=vmi_model.vcenter_port.vlan_id, rx_vlan=vmi_model.vcenter_port.vlan_id, port_type=2, # vrouter-port-control accepts only project's uuid without dashes vm_project_id=vmi_model.vn_model.vnc_vn.parent_uuid.replace('-', ''), ) self.vrouter_api.add_port(**parameters) logger.info('Added port to vRouter with parameters: %s', parameters) except Exception as e: logger.error('There was a problem with vRouter API Client: %s', e) def delete_port(self, vmi_uuid): """ Delete port from VRouter Agent. """ try: self.vrouter_api.delete_port(vmi_uuid) logger.info('Removed port from vRouter with uuid: %s', vmi_uuid) except Exception as e: logger.error('There was a problem with vRouter API Client: %s', e) def enable_port(self, vmi_uuid): try: self.vrouter_api.enable_port(vmi_uuid) logger.info('Enabled vRouter port with uuid: %s', vmi_uuid) except Exception as e: logger.error('There was a problem with vRouter API Client: %s', e) def disable_port(self, vmi_uuid): try: self.vrouter_api.disable_port(vmi_uuid) logger.info('Disabled vRouter port with uuid: %s', vmi_uuid) except Exception as e: logger.error('There was a problem with vRouter API Client: %s', e) def read_port(self, vmi_uuid): try: request_url = '{host}:{port}/port/{uuid}'.format(host=self.vrouter_host, port=self.vrouter_port, uuid=vmi_uuid) response = requests.get(request_url) if response.status_code == requests.codes.ok: port_properties = json.loads(response.content) logger.info('Read vRouter port with uuid: %s, port properties: %s', vmi_uuid, port_properties) return port_properties except Exception as e: logger.error('There was a problem with vRouter API Client: %s', e) logger.info('Unable to read vRouter port with uuid: %s', vmi_uuid) return None def get_all_port_uuids(self): if not os.path.exists(self.port_files_path): return () port_uuids = [] for file_name in os.listdir(self.port_files_path): if os.path.isfile(os.path.join(self.port_files_path, file_name)): port_uuids.append(file_name) return port_uuids
class ConfigCNI(ConfigHandle): def __init__(self, **kwargs): if self.vnc_handle: pass else: super(ConfigCNI, self).__init__(api_server_host=kwargs['api_server'], tenant=kwargs['tenant']) self.vmi = ConfigVMI() self.vm = ConfigVM() self.vn = ConfigVN() self.vrouter = ContrailVRouterApi() def create(self, name, vn): self.name = name cmd = 'docker inspect -f "{{.State.Pid}}" %s' % (name) pid = self.shell_cmd(cmd) pid = pid.rstrip('\n') self.shell_cmd('mkdir -p /var/run/netns') self.shell_cmd('sudo ln -sf /proc/%s/ns/net /var/run/netns/%s' % (pid, name)) vm_name = '{}-{}'.format(socket.gethostname(), name) proj_obj = self.get_project(name=self.tenant) vn_obj = self.vn.read(name=vn) try: vm_obj = self.vm.read(vm_name) except: self.vm.create(vm_name) vm_obj = self.vm.read(vm_name) ifl = self.get_vethid(name) ifl = 1 if not ifl else int(ifl) + 1 (veth,cni) = ("veth-{}-{}".format(name,str(ifl)),\ "cni-{}-{}".format(name,str(ifl))) vmi_name = "{}-{}-{}".format(socket.gethostname(), name, ifl) try: vmi_obj = self.vmi.read(vmi_name) except: self.vmi.create(vn, vm_name, vmi_name) vmi_obj = self.vmi.read(vmi_name) if vmi_obj: mac = vmi_obj.virtual_machine_interface_mac_addresses.mac_address[ 0] self.shell_cmd('sudo ip link add %s type veth peer name %s' \ %(cni,veth)) self.shell_cmd('sudo ifconfig %s hw ether %s'\ %(veth,mac)) self.shell_cmd('sudo ip link set %s netns %s' \ %(veth, name)) self.shell_cmd('sudo ip netns exec %s ip link set %s up' \ %(name,veth)) self.shell_cmd('sudo ip link set %s up' \ %(cni)) self.register_cni(cni, vm_obj, vmi_obj, vn_obj, proj_obj) def delete(self, name): vm_name = '%s-%s' % (socket.gethostname(), name) ifl = self.get_vethid(name) vmi_name = '{}-{}-{}'.format(socket.gethostname(), name, ifl) if ifl: cni = "cni-{}-{}".format(name, str(ifl)) else: print "No more interfaces are left inside the container instance" sys.exit(1) vmi_obj = self.vmi.read(vmi_name) self.unregister_cni(vmi_obj) self.vmi.delete(vmi_name) self.shell_cmd('sudo ip link delete %s' \ %(cni)) if int(ifl) == 1: print "Deleting the VM object" self.vm.delete(vm_name) return def list(self, name): import re from operator import attrgetter, itemgetter vm_name = '%s-%s' % (socket.gethostname(), name) vm_obj = self.vm.read(vm_name) vm_name = vm_obj.display_name try: print "{:24}{:24}{:24}{:32}{:24}".\ format('Docker Instance','Vrouter Interface',\ 'Container interface','Virtual Machine Interface','Network Segment') print "-" * 124 vmi_objs = vm_obj.get_virtual_machine_interface_back_refs() vmi_objs.sort(key=lambda vmi: vmi['to'][2]) for p in vmi_objs: vmi_obj = self.vnc_handle.virtual_machine_interface_read( fq_name=p['to']) vmi_name = vmi_obj.display_name ifl = re.search(r'-([0-9]+)$', vmi_name).group(1) cni_name = 'cni-{}-{}'.format(name, ifl) veth_name = 'veth-{}-{}'.format(name, ifl) vn_name = vmi_obj.virtual_network_refs[0]['to'][2] print "{:24}{:24}{:24}{:32}{:24}".format( name, veth_name, cni_name, vmi_name, vn_name) except: return return def register_cni(self, cni, vm_obj, vmi_obj, vn_obj, proj_obj): mac = vmi_obj.virtual_machine_interface_mac_addresses.mac_address[0] self.vrouter.add_port(vm_obj.uuid, vmi_obj.uuid, cni, mac, port_type='NovaVMPort', vm_project_id=proj_obj.uuid, vn_id=vn_obj.uuid) return def unregister_cni(self, vmi_obj): self.vrouter.delete_port(vmi_obj.uuid) return def get_vethid(self, name): cmd = "ip netns exec %s ip link | grep veth | \ awk '{print $2}' | awk -F ':' '{print $1}' | \ awk -F 'veth-%s-' '{print $2}' | tail -n 1 | \ awk -F '@' {'print $1}'" % (name, name) ifl = self.shell_cmd(cmd) return ifl.rstrip('\n')
class VRouterApiTest(unittest.TestCase): def setUp(self): self._api = ContrailVRouterApi() def test_create_port(self): mock_client = mock.Mock() self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = mock_client self._api.add_port(str(uuid.uuid1()), str(uuid.uuid1()), 'tapX', 'aa:bb:cc:ee:ff:00') self.assertTrue(mock_client.AddPort.called) def test_delete_port(self): mock_client = mock.Mock() self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = mock_client vm_uuid = uuid.uuid1() vif_uuid = uuid.uuid1() self._api.add_port(str(vm_uuid), str(vif_uuid), 'tapX', 'aa:bb:cc:ee:ff:00') self.assertTrue(mock_client.AddPort.called) self.assertTrue(self._api._ports[vif_uuid]) self._api.delete_port(str(vif_uuid)) self.assertTrue(mock_client.DeletePort.called) def test_resynchronize(self): self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = None vm_uuid = str(uuid.uuid1()) vif_uuid = str(uuid.uuid1()) self._api.add_port(vm_uuid, vif_uuid, 'tapX', 'aa:bb:cc:ee:ff:00') mock_client = mock.Mock() self._api._rpc_client_instance.return_value = mock_client self._api.periodic_connection_check() self.assertTrue(mock_client.AddPort.called) def test_additional_arguments(self): mock_client = mock.Mock() self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = mock_client vif_uuid = uuid.uuid1() network_uuid = uuid.uuid1() project_uuid = uuid.uuid1() self._api.add_port(str(uuid.uuid1()), str(vif_uuid), 'tapX', 'aa:bb:cc:ee:ff:00', network_uuid=str(network_uuid), vm_project_uuid=str(project_uuid)) self.assertTrue(mock_client.AddPort.called) port = self._api._ports[vif_uuid] self.assertEqual(self._api._uuid_to_hex(network_uuid), port.vn_id) self.assertEqual(self._api._uuid_to_hex(project_uuid), port.vm_project_uuid)
class ContrailInterfaceDriver(interface.LinuxInterfaceDriver): """ Opencontrail VIF driver for neutron.""" @classmethod def _parse_class_args(cls, cfg_parser): cfg_parser.read(CONTRAIL_CFG_FILE) cls._api_server_ip = _read_cfg(cfg_parser, 'APISERVER', 'api_server_ip', '127.0.0.1') cls._api_server_port = _read_cfg(cfg_parser, 'APISERVER', 'api_server_port', '8082') cls._api_server_use_ssl = _read_cfg(cfg_parser, 'APISERVER', 'use_ssl', False) def __init__(self, conf): super(ContrailInterfaceDriver, self).__init__(conf) self._port_dict = {} self._client = self._connect_to_api_server() self._vrouter_client = ContrailVRouterApi() timer = loopingcall.FixedIntervalLoopingCall(self._keep_alive) timer.start(interval=2) def _connect_to_api_server(self): cfg_parser = ConfigParser.ConfigParser() ContrailInterfaceDriver._parse_class_args(cfg_parser) try: client = VncApi(api_server_host=self._api_server_ip, api_server_port=self._api_server_port, api_server_use_ssl=self._api_server_use_ssl) return client except: pass def _keep_alive(self): self._vrouter_client.periodic_connection_check() def _delete_port(self, port_id): self._vrouter_client.delete_port(port_id) def _instance_locate(self, port_obj): """ lookup the instance associated with the port object. Create the vm instance if port object is not associated with a vm instance """ if port_obj.get_virtual_machine_refs() is not None: try: vm_uuid = port_obj.get_virtual_machine_refs()[0]['uuid'] instance_obj = self._client.virtual_machine_read(id=vm_uuid) return instance_obj except NoIdError: pass vm_uuid = str(uuid.uuid4()) instance_obj = VirtualMachine(vm_uuid) instance_obj.uuid = vm_uuid self._client.virtual_machine_create(instance_obj) port_obj.set_virtual_machine(instance_obj) self._client.virtual_machine_interface_update(port_obj) return instance_obj def _add_port_to_agent(self, port_id, net_id, iface_name, mac_address): port_obj = self._client.virtual_machine_interface_read(id=port_id) if port_obj is None: LOG.debug(_("Invalid port_id : %s"), port_id) return ips = port_obj.get_instance_ip_back_refs() ip_addr = '0.0.0.0' # get the ip address of the port if associated if ips and len(ips): ip_uuid = ips[0]['uuid'] ip = self._client.instance_ip_read(id=ip_uuid) ip_addr = ip.get_instance_ip_address() net_obj = self._client.virtual_network_read(id=net_id) if net_obj is None: LOG.debug(_("Invalid net_id : %s"), net_id) return # get the instance object the port is attached to instance_obj = self._instance_locate(port_obj) if instance_obj is None: return kwargs = {} kwargs['ip_address'] = ip_addr kwargs['network_uuid'] = net_id kwargs['vm_project_uuid'] = net_obj.parent_uuid self._vrouter_client.add_port(instance_obj.uuid, port_id, iface_name, mac_address, **kwargs) def plug(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None): if not ip_lib.device_exists(device_name, self.root_helper, namespace): ip = ip_lib.IPWrapper(self.root_helper) tap_name = device_name.replace(prefix or 'veth', 'veth') # Create ns_dev in a namespace if one is configured. root_dev, ns_dev = ip.add_veth(tap_name, device_name, namespace2=namespace) ns_dev.link.set_address(mac_address) namespace_obj = ip.ensure_namespace(namespace) namespace_obj.add_device_to_namespace(ns_dev) ns_dev.link.set_up() root_dev.link.set_up() self._add_port_to_agent(port_id, network_id, tap_name, mac_address) self._port_dict[tap_name] = port_id else: LOG.warn(_("Device %s already exists"), device_name) def unplug(self, device_name, bridge=None, namespace=None, prefix=None): tap_name = device_name.replace(prefix or 'veth', 'veth') if tap_name in self._port_dict: self._delete_port(self._port_dict[tap_name]) del self._port_dict[tap_name] device = ip_lib.IPDevice(device_name, self.root_helper, namespace) device.link.delete() LOG.debug(_("Unplugged interface '%s'"), device_name) ip_lib.IPWrapper( self.root_helper, namespace).garbage_collect_namespace()
class OpenContrailVIFDriver(object): def __init__(self): self._vrouter_semaphore = eventlet.semaphore.Semaphore() self._vrouter_client = ContrailVRouterApi( doconnect=True, semaphore=self._vrouter_semaphore) timer = loopingcall.FixedIntervalLoopingCall(self._keep_alive) timer.start(interval=2) def _keep_alive(self): self._vrouter_client.periodic_connection_check() def plug(self, instance, vif): vif_type = vif['type'] LOG.debug( 'Plug vif_type=%(vif_type)s instance=%(instance)s ' 'vif=%(vif)s', { 'vif_type': vif_type, 'instance': instance, 'vif': vif }) if_local_name = 'veth%s' % vif['id'][:8] if_remote_name = 'ns%s' % vif['id'][:8] # Device already exists so return. if linux_net.device_exists(if_local_name): return undo_mgr = utils.UndoManager() try: utils.execute('ip', 'link', 'add', if_local_name, 'type', 'veth', 'peer', 'name', if_remote_name, run_as_root=True) undo_mgr.undo_with(lambda: utils.execute( 'ip', 'link', 'delete', if_local_name, run_as_root=True)) utils.execute('ip', 'link', 'set', if_remote_name, 'address', vif['address'], run_as_root=True) except Exception: LOG.exception("Failed to configure network") msg = _('Failed to setup the network, rolling back') undo_mgr.rollback_and_reraise(msg=msg, instance=instance) def attach(self, instance, vif, container_id): vif_type = vif['type'] LOG.debug( 'Attach vif_type=%(vif_type)s instance=%(instance)s ' 'vif=%(vif)s', { 'vif_type': vif_type, 'instance': instance, 'vif': vif }) if_local_name = 'veth%s' % vif['id'][:8] if_remote_name = 'ns%s' % vif['id'][:8] undo_mgr = utils.UndoManager() undo_mgr.undo_with(lambda: utils.execute( 'ip', 'link', 'delete', if_local_name, run_as_root=True)) ipv4_address = '0.0.0.0' ipv6_address = None if 'subnets' in vif['network']: subnets = vif['network']['subnets'] for subnet in subnets: ips = subnet['ips'][0] if (ips['version'] == 4): if ips['address'] is not None: ipv4_address = ips['address'] ipv4_netmask = subnet['cidr'].split('/')[1] ipv4_gateway = subnet['gateway']['address'] if (ips['version'] == 6): if ips['address'] is not None: ipv6_address = ips['address'] ipv6_netmask = subnet['cidr'].split('/')[1] ipv6_gateway = subnet['gateway']['address'] params = { 'ip_address': ipv4_address, 'vn_id': vif['network']['id'], 'display_name': instance['display_name'], 'hostname': instance['hostname'], 'host': instance['host'], 'vm_project_id': instance['project_id'], 'port_type': 'NovaVMPort', 'ip6_address': ipv6_address, } try: utils.execute('ip', 'link', 'set', if_remote_name, 'netns', container_id, run_as_root=True) result = self._vrouter_client.add_port(instance['uuid'], vif['id'], if_local_name, vif['address'], **params) if not result: # follow the exception path raise RuntimeError('add_port returned %s' % str(result)) utils.execute('ip', 'link', 'set', if_local_name, 'up', run_as_root=True) except Exception: LOG.exception("Failed to attach the network") msg = _('Failed to attach the network, rolling back') undo_mgr.rollback_and_reraise(msg=msg, instance=instance) try: utils.execute('ip', 'netns', 'exec', container_id, 'ip', 'link', 'set', if_remote_name, 'address', vif['address'], run_as_root=True) if ipv6_address: ip = ipv6_address + "/" + ipv6_netmask gateway = ipv6_gateway utils.execute('ip', 'netns', 'exec', container_id, 'ifconfig', if_remote_name, 'inet6', 'add', ip, run_as_root=True) utils.execute('ip', 'netns', 'exec', container_id, 'ip', '-6', 'route', 'replace', 'default', 'via', gateway, 'dev', if_remote_name, run_as_root=True) if ipv4_address != '0.0.0.0': ip = ipv4_address + "/" + ipv4_netmask gateway = ipv4_gateway utils.execute('ip', 'netns', 'exec', container_id, 'ifconfig', if_remote_name, ip, run_as_root=True) utils.execute('ip', 'netns', 'exec', container_id, 'ip', 'route', 'replace', 'default', 'via', gateway, 'dev', if_remote_name, run_as_root=True) utils.execute('ip', 'netns', 'exec', container_id, 'ip', 'link', 'set', if_remote_name, 'up', run_as_root=True) except Exception: LOG.exception(_("Failed to attach vif"), instance=instance) def unplug(self, instance, vif): vif_type = vif['type'] if_local_name = 'veth%s' % vif['id'][:8] LOG.debug( 'Unplug vif_type=%(vif_type)s instance=%(instance)s ' 'vif=%(vif)s', { 'vif_type': vif_type, 'instance': instance, 'vif': vif }) try: self._vrouter_client.delete_port(vif['id']) if linux_net.device_exists(if_local_name): utils.execute('ip', 'link', 'delete', if_local_name, run_as_root=True) except Exception: LOG.exception(_("Delete port failed"), instance=instance)
class VRouterVIFDriver(LibVirtVIFDriver): """VIF driver for VRouter when running Neutron.""" PORT_TYPE = 'NovaVMPort' def __init__(self, get_connection): super(VRouterVIFDriver, self).__init__(get_connection) self._vrouter_semaphore = eventlet.semaphore.Semaphore() self._vrouter_client = ContrailVRouterApi(doconnect=True, semaphore=self._vrouter_semaphore) timer = loopingcall.FixedIntervalLoopingCall(self._keep_alive) timer.start(interval=2) def _keep_alive(self): self._vrouter_client.periodic_connection_check() @staticmethod def _get_br_name(dev): """Returns the bridge name for a tap device. This is lxc related stuff. To work around the fact, that libvirt does not support direct passthrough of devices to LXC.""" return 'br%s' % dev[3:] def _create_bridge(self, dev, instance): """Creating a bridge and returning its name""" br_name = self._get_br_name(dev) try: linux_net.LinuxBridgeInterfaceDriver.ensure_bridge(br_name, dev) linux_net._execute('ip', 'link', 'set', br_name, 'promisc', 'on', run_as_root=True) except processutils.ProcessExecutionError: LOG.exception(_LE("Failed while plugging vif"), instance=instance) return br_name def get_config(self, instance, vif, image_meta, inst_type, virt_type=None): try: conf = super(VRouterVIFDriver, self).get_config(instance, vif, image_meta, inst_type) except TypeError: conf = super(VRouterVIFDriver, self).get_base_config(instance, vif, image_meta, inst_type, virt_type) dev = self.get_vif_devname(vif) if not virt_type: try: virt_type = cfg.CONF.libvirt.virt_type except cfg.NoSuchOptError: virt_type = cfg.CONF.libvirt_type if virt_type == 'lxc': # for lxc we need to pass a bridge to libvirt br_name = self._get_br_name(dev) designer.set_vif_host_backend_bridge_config(conf, br_name) else: if CONF.contrail.use_userspace_vhost: dev = path.join(CONF.contrail.userspace_vhost_socket_dir, 'uvh_vif_' + dev) designer.set_vif_host_backend_vhostuser_config(conf, 'client', dev) else: designer.set_vif_host_backend_ethernet_config(conf, dev) designer.set_vif_bandwidth_config(conf, inst_type) return conf def plug(self, instance, vif): try: dev = self.get_vif_devname(vif) try: if not CONF.contrail.use_userspace_vhost: linux_net.create_tap_dev(dev) except processutils.ProcessExecutionError: LOG.exception(_LE("Failed while plugging vif"), instance=instance) try: virt_type = cfg.CONF.libvirt.virt_type except cfg.NoSuchOptError: virt_type = cfg.CONF.libvirt_type if virt_type == 'lxc': dev = self._create_bridge(dev, instance) ipv4_address = '0.0.0.0' ipv6_address = None subnets = vif['network']['subnets'] for subnet in subnets: ips = subnet['ips'][0] if (ips['version'] == 4): if ips['address'] is not None: ipv4_address = ips['address'] if (ips['version'] == 6): if ips['address'] is not None: ipv6_address = ips['address'] kwargs = { 'ip_address': ipv4_address, 'vn_id': vif['network']['id'], 'display_name': instance['display_name'], 'hostname': instance['hostname'], 'host': instance['host'], 'vm_project_id': instance['project_id'], 'port_type': self.PORT_TYPE, 'ip6_address': ipv6_address, } try: result = self._vrouter_client.add_port(instance['uuid'], vif['id'], dev, vif['address'], **kwargs) if not result: LOG.exception(_LE("Failed while plugging vif"), instance=instance) except TApplicationException: LOG.exception(_LE("Failed while plugging vif"), instance=instance) except Exception as e: from pprint import pformat LOG.error(_("Error in plug: %s locals: %s instance %s" %(str(e), pformat(locals()), pformat(instance) if isinstance(instance, dict) else pformat(instance.__dict__)))) def unplug(self, instance, vif): try: dev = self.get_vif_devname(vif) if isinstance(instance, dict): task_state = instance['task_state'] else: task_state = instance._task_state try: self._vrouter_client.delete_port(vif['id']) if task_state == 'rebuilding': self.delete_device(dev) else: # delegate the deletion of tap device to a deffered thread worker_thread = threading.Thread( target=self.delete_device, name='contrailvif', args=(dev,), kwargs={'timeout': 2}) worker_thread.start() except (TApplicationException, processutils.ProcessExecutionError, RuntimeError): LOG.exception(_LE("Failed while unplugging vif"), instance=instance) except Exception as e: from pprint import pformat LOG.error(_("Error in unplug: %s locals: %s instance %s" %(str(e), pformat(locals()), pformat(instance) if isinstance(instance, dict) else pformat(instance.__dict__)))) def delete_device(self, dev, timeout=None): if timeout is not None: time.sleep(timeout) LOG.debug(dev) try: virt_type = cfg.CONF.libvirt.virt_type except cfg.NoSuchOptError: virt_type = cfg.CONF.libvirt_type if virt_type == 'lxc': linux_net.LinuxBridgeInterfaceDriver.remove_bridge( self._get_br_name(dev)) if not CONF.contrail.use_userspace_vhost: linux_net.delete_net_dev(dev)
class NetnsManager(object): SNAT_RT_TABLES_ID = 42 DEV_NAME_LEN = 14 NETNS_PREFIX = 'vrouter-' LEFT_DEV_PREFIX = 'int-' RIGH_DEV_PREFIX = 'gw-' TAP_PREFIX = 'veth' def __init__(self, vm_uuid, nic_left, nic_right, root_helper='sudo'): self.vm_uuid = vm_uuid self.namespace = self.NETNS_PREFIX + self.vm_uuid self.nic_left = nic_left self.nic_right = nic_right self.root_helper = root_helper self.nic_left['name'] = (self.LEFT_DEV_PREFIX + self.nic_left['uuid'])[:self.DEV_NAME_LEN] self.nic_right['name'] = (self.RIGH_DEV_PREFIX + self.nic_right['uuid'])[:self.DEV_NAME_LEN] self.ip_ns = ip_lib.IPWrapper(root_helper=self.root_helper, namespace=self.namespace) self.vrouter_client = ContrailVRouterApi() def _get_tap_name(self, uuid_str): return (self.TAP_PREFIX + uuid_str)[:self.DEV_NAME_LEN] def is_netns_already_exists(self): return self.ip_ns.netns.exists(self.namespace) def create(self): ip = ip_lib.IPWrapper(self.root_helper) ip.ensure_namespace(self.namespace) if not self.nic_left or not self.nic_right: raise ValueError('Need left and right interfaces to create a ' 'network namespace') for nic in [self.nic_left, self.nic_right]: self._create_interfaces(ip, nic) def set_snat(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Need to create the network namespace before set ' 'up the SNAT') self.ip_ns.netns.execute(['sysctl', '-w', 'net.ipv4.ip_forward=1']) self.ip_ns.netns.execute(['iptables', '-t', 'nat', '-F']) self.ip_ns.netns.execute(['iptables', '-t', 'nat', '-A', 'POSTROUTING', '-s', '0.0.0.0/0', '-o', self.nic_right['name'], '-j', 'MASQUERADE']) self.ip_ns.netns.execute(['ip', 'route', 'replace', 'default', 'dev', self.nic_right['name']]) self.ip_ns.netns.execute(['ip', 'route', 'replace', 'default', 'dev', self.nic_left['name'], 'table', self.SNAT_RT_TABLES_ID]) try: self.ip_ns.netns.execute(['ip', 'rule', 'del', 'iif', str(self.nic_right['name']), 'table', self.SNAT_RT_TABLES_ID]) except RuntimeError: pass self.ip_ns.netns.execute(['ip', 'rule', 'add', 'iif', str(self.nic_right['name']), 'table', self.SNAT_RT_TABLES_ID]) def destroy(self): if not self.ip_ns.netns.exists(self.namespace): raise ValueError('Namespace %s does not exist' % self.namespace) for device in self.ip_ns.get_devices(exclude_loopback=True): ip_lib.IPDevice(device.name, self.root_helper, self.namespace).link.delete() self.ip_ns.netns.delete(self.namespace) def plug_namespace_interface(self): if not self.nic_left or not self.nic_right: raise ValueError('Need left and right interfaces to plug a ' 'network namespace onto vrouter') self._add_port_to_agent(self.nic_left, display_name='NetNS %s left interface' % self.vm_uuid) self._add_port_to_agent(self.nic_right, display_name='NetNS %s right interface' % self.vm_uuid) def unplug_namespace_interface(self): if not self.nic_left or not self.nic_right: raise ValueError('Need left and right interfaces to unplug a ' 'network namespace onto vrouter') for nic in [self.nic_left, self.nic_right]: self._delete_port_to_agent(nic) def _create_interfaces(self, ip, nic): if ip_lib.device_exists(nic['name'], self.root_helper, namespace=self.namespace): ip_lib.IPDevice(nic['name'], self.root_helper, self.namespace).link.delete() root_dev, ns_dev = ip.add_veth(self._get_tap_name(nic['uuid']), nic['name'], namespace2=self.namespace) if nic['mac']: ns_dev.link.set_address(str(nic['mac'])) ns_dev.link.set_up() root_dev.link.set_up() if nic['ip']: ip = nic['ip'] ns_dev.addr.flush() ns_dev.addr.add(ip.version, str(ip), str(ip.broadcast)) else: #TODO(ethuleau): start DHCP client raise NotImplementedError # disable reverse path filtering self.ip_ns.netns.execute(['sysctl', '-w', 'net.ipv4.conf.%s.rp_filter=2' % nic['name']] ) def _add_port_to_agent(self, nic, display_name=None): kwargs = {} kwargs['ip_address'] = str(nic['ip'].ip) if display_name: kwargs['display_name'] = display_name if not (self.vrouter_client.add_port(self.vm_uuid, nic['uuid'], self._get_tap_name(nic['uuid']), str(nic['mac']), **kwargs)): raise ValueError('Cannot add interface %s on the vrouter' % nic['uuid']) def _delete_port_to_agent(self, nic): self.vrouter_client.delete_port(nic['uuid'])
class VRouterApiTest(unittest.TestCase): def setUp(self): self._api = ContrailVRouterApi() def test_create_port(self): mock_client = mock.Mock() self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = mock_client self._api.add_port(str(uuid.uuid1()), str(uuid.uuid1()), 'tapX', 'aa:bb:cc:ee:ff:00') self.assertTrue(mock_client.AddPort.called) def test_delete_port(self): mock_client = mock.Mock() self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = mock_client vm_uuid = uuid.uuid1() vif_uuid = uuid.uuid1() self._api.add_port(str(vm_uuid), str(vif_uuid), 'tapX', 'aa:bb:cc:ee:ff:00') self.assertTrue(mock_client.AddPort.called) self.assertTrue(self._api._ports[vif_uuid]) self._api.delete_port(str(vif_uuid)) self.assertTrue(mock_client.DeletePort.called) def test_resynchronize(self): self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = None vm_uuid = str(uuid.uuid1()) vif_uuid = str(uuid.uuid1()) port1 = ttypes.Port(self._api._uuid_string_to_hex(vif_uuid), self._api._uuid_string_to_hex(vm_uuid), 'tapX', '0.0.0.0', [0] * 16, 'aa:bb:cc:ee:ff:00') self._api.add_port(vm_uuid, vif_uuid, 'tapX', 'aa:bb:cc:ee:ff:00') mock_client = mock.Mock() self._api._rpc_client_instance.return_value = mock_client self._api.periodic_connection_check() mock_client.AddPort.assert_called_with([port1]) def test_resynchronize_multi_ports(self): self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = None vm_uuid = str(uuid.uuid1()) vif_uuid = str(uuid.uuid1()) port1 = ttypes.Port(self._api._uuid_string_to_hex(vif_uuid), self._api._uuid_string_to_hex(vm_uuid), 'tapX', '0.0.0.0', [0] * 16, 'aa:bb:cc:ee:ff:00') self._api.add_port(vm_uuid, vif_uuid, 'tapX', 'aa:bb:cc:ee:ff:00') vm_uuid = str(uuid.uuid1()) vif_uuid = str(uuid.uuid1()) port2 = ttypes.Port(self._api._uuid_string_to_hex(vif_uuid), self._api._uuid_string_to_hex(vm_uuid), 'tapY', '0.0.0.0', [0] * 16, '11:22:33:44:55:66') self._api.add_port(vm_uuid, vif_uuid, 'tapY', '11:22:33:44:55:66') mock_client = mock.Mock() self._api._rpc_client_instance.return_value = mock_client self._api.connect() self._api._resynchronize() mock_client.AddPort.assert_called_with([port1, port2]) def test_additional_arguments(self): mock_client = mock.Mock() self._api._rpc_client_instance = mock.MagicMock( name='rpc_client_instance') self._api._rpc_client_instance.return_value = mock_client vif_uuid = uuid.uuid1() network_uuid = uuid.uuid1() project_id = uuid.uuid1().hex self._api.add_port(str(uuid.uuid1()), str(vif_uuid), 'tapX', 'aa:bb:cc:ee:ff:00', vn_id=str(network_uuid), vm_project_id=project_id) self.assertTrue(mock_client.AddPort.called) port = self._api._ports[vif_uuid] self.assertEqual(self._api._uuid_to_hex(network_uuid), port.vn_id) self.assertEqual(self._api._uuid_string_to_hex(project_id), port.vm_project_id)
class VRouterVIFDriver(LibvirtBaseVIFDriver): """VIF driver for VRouter when running Neutron.""" PORT_TYPE = 'NovaVMPort' def __init__(self, get_connection): super(VRouterVIFDriver, self).__init__(get_connection) self._vrouter_client = ContrailVRouterApi() timer = loopingcall.FixedIntervalLoopingCall(self._keep_alive) timer.start(interval=2) def _keep_alive(self): self._vrouter_client.periodic_connection_check() def get_config(self, instance, vif, image_meta, inst_type): conf = super(VRouterVIFDriver, self).get_config(instance, vif, image_meta, inst_type) dev = self.get_vif_devname(vif) designer.set_vif_host_backend_ethernet_config(conf, dev) designer.set_vif_bandwidth_config(conf, inst_type) return conf def plug(self, instance, vif): dev = self.get_vif_devname(vif) try: linux_net.create_tap_dev(dev) except processutils.ProcessExecutionError: LOG.exception(_LE("Failed while plugging vif"), instance=instance) kwargs = { 'ip_address': vif['network']['subnets'][0]['ips'][0]['address'], 'vn_id': vif['network']['id'], 'display_name': instance['display_name'], 'hostname': instance['hostname'], 'host': instance['host'], 'vm_project_id': instance['project_id'], 'port_type': self.PORT_TYPE, } try: result = self._vrouter_client.add_port(instance['uuid'], vif['id'], dev, vif['address'], **kwargs) if not result: LOG.exception(_LE("Failed while plugging vif"), instance=instance) except TApplicationException: LOG.exception(_LE("Failed while plugging vif"), instance=instance) def unplug(self, instance, vif): dev = self.get_vif_devname(vif) try: self._vrouter_client.delete_port(vif['id']) linux_net.delete_net_dev(dev) except (TApplicationException, processutils.ProcessExecutionError): LOG.exception(_LE("Failed while unplugging vif"), instance=instance)
class OpenContrailVIFDriver(object): def __init__(self): self._vrouter_client = ContrailVRouterApi(doconnect=True) def plug(self, instance, vif): if_local_name = 'veth%s' % vif['id'][:8] if_remote_name = 'ns%s' % vif['id'][:8] # Device already exists so return. if linux_net.device_exists(if_local_name): return undo_mgr = utils.UndoManager() try: utils.execute('ip', 'link', 'add', if_local_name, 'type', 'veth', 'peer', 'name', if_remote_name, run_as_root=True) undo_mgr.undo_with(lambda: utils.execute( 'ip', 'link', 'delete', if_local_name, run_as_root=True)) utils.execute('ip', 'link', 'set', if_remote_name, 'address', vif['address'], run_as_root=True) except Exception: LOG.exception("Failed to configure network") msg = _('Failed to setup the network, rolling back') undo_mgr.rollback_and_reraise(msg=msg, instance=instance) def attach(self, instance, vif, container_id): if_local_name = 'veth%s' % vif['id'][:8] if_remote_name = 'ns%s' % vif['id'][:8] undo_mgr = utils.UndoManager() ipv4_address = '0.0.0.0' ipv6_address = None if 'subnets' in vif['network']: subnets = vif['network']['subnets'] for subnet in subnets: ips = subnet['ips'][0] if (ips['version'] == 4): if ips['address'] is not None: ipv4_address = ips['address'] if (ips['version'] == 6): if ips['address'] is not None: ipv6_address = ips['address'] params = { 'ip_address': ipv4_address, 'vn_id': vif['network']['id'], 'display_name': instance['display_name'], 'hostname': instance['hostname'], 'host': instance['host'], 'vm_project_id': instance['project_id'], 'port_type': 'NovaVMPort', 'ip6_address': ipv6_address, } try: utils.execute('ip', 'link', 'set', if_remote_name, 'netns', container_id, run_as_root=True) result = self._vrouter_client.add_port(instance['uuid'], vif['id'], if_local_name, vif['address'], **params) if not result: # follow the exception path raise RuntimeError('add_port returned %s' % str(result)) utils.execute('ip', 'link', 'set', if_local_name, 'up', run_as_root=True) except Exception: LOG.exception("Failed to attach the network") msg = _('Failed to attach the network, rolling back') undo_mgr.rollback_and_reraise(msg=msg, instance=instance) # TODO(NetNS): attempt DHCP client; fallback to manual config if the # container doesn't have an working dhcpclient utils.execute('ip', 'netns', 'exec', container_id, 'dhclient', if_remote_name, run_as_root=True) def unplug(self, instance, vif): try: self._vrouter_client.delete_port(vif['id']) except Exception: LOG.exception(_("Delete port failed"), instance=instance) if_local_name = 'veth%s' % vif['id'][:8] utils.execute('ip', 'link', 'delete', if_local_name, run_as_root=True)