def plug(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None): """Plug in the interface.""" if not bridge: bridge = self.conf.ovs_integration_bridge self.check_bridge_exists(bridge) if not ip_lib.device_exists( device_name, self.conf.root_helper, namespace=namespace): self._ovs_add_port(bridge, device_name, port_id, mac_address) ip = ip_lib.IPWrapper(self.conf.root_helper) device = ip.device(device_name) device.link.set_address(mac_address) if self.conf.network_device_mtu: device.link.set_mtu(self.conf.network_device_mtu) if namespace: namespace_obj = ip.ensure_namespace(namespace) namespace_obj.add_device_to_namespace(device) device.link.set_up()
def setup_rpc(self, physical_interfaces): if physical_interfaces: mac = utils.get_interface_mac(physical_interfaces[0]) else: devices = ip_lib.IPWrapper(self.root_helper).get_devices(True) if devices: mac = utils.get_interface_mac(devices[0].name) else: LOG.error("Unable to obtain MAC address for unique ID. " "Agent terminated!") exit(1) self.agent_id = '%s%s' % ('lb', (mac.replace(":", ""))) LOG.info("RPC agent_id: %s" % self.agent_id) self.topic = topics.AGENT self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) # RPC network init self.context = context.RequestContext('quantum', 'quantum', is_admin=False) # Handle updates from service self.callbacks = LinuxBridgeRpcCallbacks(self.context, self.linux_br) self.dispatcher = self.callbacks.create_rpc_dispatcher() # Define the listening consumers for the agent consumers = [[topics.PORT, topics.UPDATE], [topics.NETWORK, topics.DELETE]] self.connection = agent_rpc.create_consumers(self.dispatcher, self.topic, consumers) self.udev = pyudev.Context() monitor = pyudev.Monitor.from_netlink(self.udev) monitor.filter_by('net')
def plug(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None): """Plugin the interface.""" if not ip_lib.device_exists( device_name, self.root_helper, namespace=namespace): ip = ip_lib.IPWrapper(self.root_helper) # Enable agent to define the prefix if prefix: tap_name = device_name.replace(prefix, 'tap') else: tap_name = device_name.replace(self.DEV_NAME_PREFIX, 'tap') # Create ns_veth in a namespace if one is configured. root_veth, ns_veth = ip.add_veth(tap_name, device_name, namespace2=namespace) ns_veth.link.set_address(mac_address) if self.conf.network_device_mtu: root_veth.link.set_mtu(self.conf.network_device_mtu) ns_veth.link.set_mtu(self.conf.network_device_mtu) root_veth.link.set_up() ns_veth.link.set_up() else: LOG.warn(_("Device %s already exists"), device_name)
def setup_rpc(self, physical_interfaces): if physical_interfaces: mac = utils.get_interface_mac(physical_interfaces[0]) else: devices = ip_lib.IPWrapper(self.root_helper).get_devices(True) if devices: mac = utils.get_interface_mac(devices[0].name) else: LOG.error(_("Unable to obtain MAC address for unique ID. " "Agent terminated!")) exit(1) self.agent_id = '%s%s' % ('lb', (mac.replace(":", ""))) LOG.info(_("RPC agent_id: %s"), self.agent_id) self.topic = topics.AGENT self.plugin_rpc = LinuxBridgePluginApi(topics.PLUGIN) self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN) # RPC network init self.context = context.get_admin_context_without_session() # Handle updates from service self.callbacks = LinuxBridgeRpcCallbacks(self.context, self) self.dispatcher = self.callbacks.create_rpc_dispatcher() # Define the listening consumers for the agent consumers = [[topics.PORT, topics.UPDATE], [topics.NETWORK, topics.DELETE], [topics.SECURITY_GROUP, topics.UPDATE]] self.connection = agent_rpc.create_consumers(self.dispatcher, self.topic, consumers) report_interval = cfg.CONF.AGENT.report_interval if report_interval: heartbeat = loopingcall.LoopingCall(self._report_state) heartbeat.start(interval=report_interval)
def test_namespace_is_empty(self): ip = ip_lib.IPWrapper('sudo', 'ns') with mock.patch.object(ip, 'get_devices') as get_devices: get_devices.return_value = [mock.Mock()] self.assertFalse(ip.namespace_is_empty()) get_devices.assert_called_once_with(exclude_loopback=True)
def disable(self, retain_port=False): """Disable DHCP for this network by killing the local process.""" pid = self.pid if self.active: cmd = ['kill', '-9', pid] if self.namespace: ip_wrapper = ip_lib.IPWrapper(self.root_helper, self.namespace) ip_wrapper.netns.execute(cmd) else: utils.execute(cmd, self.root_helper) if not retain_port: self.device_delegate.destroy(self.network, self.interface_name) elif pid: LOG.debug( _('DHCP for %(net_id)s pid %(pid)d is stale, ignoring ' 'command'), { 'net_id': self.network.id, 'pid': pid }) else: LOG.debug(_('No DHCP started for %s'), self.network.id) self._remove_config_files()
def plug(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None): """Plugin the interface.""" if not ip_lib.device_exists(device_name, self.conf.root_helper, namespace=namespace): ip = ip_lib.IPWrapper(self.conf.root_helper) # Enable agent to define the prefix if prefix: tap_name = device_name.replace(prefix, 'tap') else: tap_name = device_name.replace(self.DEV_NAME_PREFIX, 'tap') root_veth, dhcp_veth = ip.add_veth(tap_name, device_name) root_veth.link.set_address(mac_address) if namespace: namespace_obj = ip.ensure_namespace(namespace) namespace_obj.add_device_to_namespace(dhcp_veth) root_veth.link.set_up() dhcp_veth.link.set_up() else: LOG.warn(_("Device %s already exists") % device_name)
def _plug(self, namespace, port, reuse_existing=True): self.vip_plug_callback('plug', port) interface_name = self.vif_driver.get_device_name(Wrap(port)) if ip_lib.device_exists(interface_name, self.root_helper, namespace): if not reuse_existing: raise exceptions.PreexistingDeviceFailure( dev_name=interface_name ) else: self.vif_driver.plug( port['network_id'], port['id'], interface_name, port['mac_address'], namespace=namespace ) cidrs = [ '%s/%s' % (ip['ip_address'], netaddr.IPNetwork(ip['subnet']['cidr']).prefixlen) for ip in port['fixed_ips'] ] self.vif_driver.init_l3(interface_name, cidrs, namespace=namespace) gw_ip = port['fixed_ips'][0]['subnet'].get('gateway_ip') if gw_ip: cmd = ['route', 'add', 'default', 'gw', gw_ip] ip_wrapper = ip_lib.IPWrapper(self.root_helper, namespace=namespace) ip_wrapper.netns.execute(cmd, check_exit_code=False)
def external_gateway_added(self, ri, ex_gw_port, internal_cidrs): interface_name = self.get_external_device_name(ex_gw_port['id']) ex_gw_ip = ex_gw_port['fixed_ips'][0]['ip_address'] if not ip_lib.device_exists(interface_name, root_helper=self.conf.root_helper, namespace=ri.ns_name()): self.driver.plug(ex_gw_port['network_id'], ex_gw_port['id'], interface_name, ex_gw_port['mac_address'], bridge=self.conf.external_network_bridge, namespace=ri.ns_name(), prefix=EXTERNAL_DEV_PREFIX) self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']], namespace=ri.ns_name()) ip_address = ex_gw_port['ip_cidr'].split('/')[0] self._send_gratuitous_arp_packet(ri, interface_name, ip_address) gw_ip = ex_gw_port['subnet']['gateway_ip'] if ex_gw_port['subnet']['gateway_ip']: cmd = ['route', 'add', 'default', 'gw', gw_ip] if self.conf.use_namespaces: ip_wrapper = ip_lib.IPWrapper(self.conf.root_helper, namespace=ri.ns_name()) ip_wrapper.netns.execute(cmd, check_exit_code=False) else: utils.execute(cmd, check_exit_code=False, root_helper=self.conf.root_helper) for (c, r) in self.external_gateway_nat_rules(ex_gw_ip, internal_cidrs, interface_name): ri.iptables_manager.ipv4['nat'].add_rule(c, r) ri.iptables_manager.apply()
def spawn_process(self): """Spawns a Dnsmasq process for the network.""" env = { self.QUANTUM_NETWORK_ID_KEY: self.network.id, self.QUANTUM_RELAY_SOCKET_PATH_KEY: self.conf.dhcp_lease_relay_socket } cmd = [ 'dnsmasq', '--no-hosts', '--no-resolv', '--strict-order', '--bind-interfaces', '--interface=%s' % self.interface_name, '--except-interface=lo', '--pid-file=%s' % self.get_conf_file_name('pid', ensure_conf_dir=True), #TODO (mark): calculate value from cidr (defaults to 150) #'--dhcp-lease-max=%s' % ?, '--dhcp-hostsfile=%s' % self._output_hosts_file(), '--dhcp-optsfile=%s' % self._output_opts_file(), '--dhcp-script=%s' % self._lease_relay_script_path(), '--leasefile-ro', ] for i, subnet in enumerate(self.network.subnets): # if a subnet is specified to have dhcp disabled if not subnet.enable_dhcp: continue if subnet.ip_version == 4: mode = 'static' else: # TODO(mark): how do we indicate other options # ra-only, slaac, ra-nameservers, and ra-stateless. mode = 'static' if self.version >= self.MINIMUM_VERSION: set_tag = 'set:' else: set_tag = '' cmd.append( '--dhcp-range=%s%s,%s,%s,%ss' % (set_tag, self._TAG_PREFIX % i, netaddr.IPNetwork( subnet.cidr).network, mode, self.conf.dhcp_lease_time)) cmd.append('--conf-file=%s' % self.conf.dnsmasq_config_file) if self.conf.dnsmasq_dns_server: cmd.append('--server=%s' % self.conf.dnsmasq_dns_server) if self.conf.dhcp_domain: cmd.append('--domain=%s' % self.conf.dhcp_domain) if self.namespace: ip_wrapper = ip_lib.IPWrapper(self.root_helper, self.namespace) ip_wrapper.netns.execute(cmd, addl_env=env) else: # For normal sudo prepend the env vars before command cmd = ['%s=%s' % pair for pair in env.items()] + cmd utils.execute(cmd, self.root_helper)
def __init__(self, interface_mappings, root_helper): self.interface_mappings = interface_mappings self.root_helper = root_helper self.ip = ip_lib.IPWrapper(self.root_helper) self.udev = pyudev.Context() monitor = pyudev.Monitor.from_netlink(self.udev) monitor.filter_by('net')
def setup_physical_bridges(self, bridge_mappings): '''Setup the physical network bridges. Creates physical network bridges and links them to the integration bridge using veths. :param bridge_mappings: map physical network names to bridge names. ''' self.phys_brs = {} self.int_ofports = {} self.phys_ofports = {} ip_wrapper = ip_lib.IPWrapper(self.root_helper) for physical_network, bridge in bridge_mappings.iteritems(): LOG.info( _("Mapping physical network %(physical_network)s to " "bridge %(bridge)s"), { 'physical_network': physical_network, 'bridge': bridge }) # setup physical bridge if not ip_lib.device_exists(bridge, self.root_helper): LOG.error( _("Bridge %(bridge)s for physical network " "%(physical_network)s does not exist. Agent " "terminated!"), { 'physical_network': physical_network, 'bridge': bridge }) sys.exit(1) br = ovs_lib.OVSBridge(bridge, self.root_helper) br.remove_all_flows() br.add_flow(priority=1, actions="normal") self.phys_brs[physical_network] = br # create veth to patch physical bridge with integration bridge int_veth_name = constants.VETH_INTEGRATION_PREFIX + bridge self.int_br.delete_port(int_veth_name) phys_veth_name = constants.VETH_PHYSICAL_PREFIX + bridge br.delete_port(phys_veth_name) if ip_lib.device_exists(int_veth_name, self.root_helper): ip_lib.IPDevice(int_veth_name, self.root_helper).link.delete() int_veth, phys_veth = ip_wrapper.add_veth(int_veth_name, phys_veth_name) self.int_ofports[physical_network] = self.int_br.add_port(int_veth) self.phys_ofports[physical_network] = br.add_port(phys_veth) # block all untranslated traffic over veth between bridges self.int_br.add_flow(priority=2, in_port=self.int_ofports[physical_network], actions="drop") br.add_flow(priority=2, in_port=self.phys_ofports[physical_network], actions="drop") # enable veth to pass traffic int_veth.link.set_up() phys_veth.link.set_up()
def enable(self, cmd_callback): if not self.active: cmd = cmd_callback(self.get_pid_file_name(ensure_pids_dir=True)) if self.namespace: ip_wrapper = ip_lib.IPWrapper(self.root_helper, self.namespace) ip_wrapper.netns.execute(cmd) else: # For normal sudo prepend the env vars before command utils.execute(cmd, self.root_helper)
def test_ensure_namespace(self): with mock.patch.object(ip_lib, 'IPDevice') as ip_dev: ip = ip_lib.IPWrapper('sudo') with mock.patch.object(ip.netns, 'exists') as ns_exists: ns_exists.return_value = False ns = ip.ensure_namespace('ns') self.execute.assert_has_calls( [mock.call([], 'netns', ('add', 'ns'), 'sudo', None)]) ip_dev.assert_has_calls([mock.call('lo', 'sudo', 'ns'), mock.call().link.set_up()])
def _destroy_router_namespace(self, namespace): ns_ip = ip_lib.IPWrapper(self.conf.root_helper, namespace=namespace) for d in ns_ip.get_devices(): if d.name.startswith(INTERNAL_DEV_PREFIX): # device is on default bridge self.driver.unplug(d.name) elif d.name.startswith(EXTERNAL_DEV_PREFIX): self.driver.unplug(d.name, bridge=self.conf.external_network_bridge) if self.conf.use_namespaces: ns_ip.netns.delete(namespace)
def exec_command(self, port_id, command=None): port = DictModel(self.client.show_port(port_id)['port']) ip = ip_lib.IPWrapper(self.root_helper) namespace = self._get_namespace(port) if self.conf.use_namespaces: if not command: return "sudo ip netns exec %s" % self._get_namespace(port) namespace = ip.ensure_namespace(namespace) return namespace.netns.execute(shlex.split(command)) else: return utils.execute(shlex.split(command))
def _destroy_all_router_namespaces(self): """Destroy all router namespaces on the host to eliminate all stale linux devices, iptables rules, and namespaces. """ root_ip = ip_lib.IPWrapper(self.conf.root_helper) for ns in root_ip.get_namespaces(self.conf.root_helper): if ns.startswith(NS_PREFIX): try: self._destroy_router_namespace(ns) except: LOG.exception(_("Couldn't delete namespace '%s'"), ns)
def test_get_devices_malformed_line(self): self.execute.return_value = '\n'.join(LINK_SAMPLE + ['gibberish']) retval = ip_lib.IPWrapper('sudo').get_devices() self.assertEquals(retval, [ip_lib.IPDevice('lo'), ip_lib.IPDevice('eth0'), ip_lib.IPDevice('br-int'), ip_lib.IPDevice('gw-ddc717df-49')]) self.execute.assert_called_once_with('o', 'link', ('list',), 'sudo', None)
def delete_probe(self, port_id): port = DictModel(self.client.show_port(port_id)['port']) ip = ip_lib.IPWrapper(self.conf.root_helper) namespace = self._get_namespace(port) if self.conf.use_namespaces and ip.netns.exists(namespace): self.driver.unplug(self.driver.get_device_name(port), namespace=namespace) ip.netns.delete(namespace) else: self.driver.unplug(self.driver.get_device_name(port)) self.client.delete_port(port.id)
def test_garbage_collect_namespace_does_not_exist(self): with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd_cls: ip_ns_cmd_cls.return_value.exists.return_value = False ip = ip_lib.IPWrapper('sudo', 'ns') with mock.patch.object(ip, 'namespace_is_empty') as mock_is_empty: self.assertFalse(ip.garbage_collect_namespace()) ip_ns_cmd_cls.assert_has_calls([mock.call().exists('ns')]) self.assertNotIn(mock.call().delete('ns'), ip_ns_cmd_cls.return_value.mock_calls) self.assertEqual(mock_is_empty.mock_calls, [])
def unplug(self, device_name, bridge=None, namespace=None): """Unplug the interface.""" if not bridge: bridge = self.conf.ovs_integration_bridge self.check_bridge_exists(bridge) bridge = ovs_lib.OVSBridge(bridge, self.conf.root_helper) bridge.delete_port(device_name) if namespace: ip = ip_lib.IPWrapper(self.conf.root_helper, namespace) ip.garbage_collect_namespace()
def plug(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None): """Plug in the interface.""" if not bridge: bridge = self.conf.ovs_integration_bridge self.check_bridge_exists(bridge) if not ip_lib.device_exists( device_name, self.root_helper, namespace=namespace): ip = ip_lib.IPWrapper(self.root_helper) tap_name = self._get_tap_name(device_name, prefix) if self.conf.ovs_use_veth: # Create ns_dev in a namespace if one is configured. root_dev, ns_dev = ip.add_veth(tap_name, device_name, namespace2=namespace) else: ns_dev = ip.device(device_name) internal = not self.conf.ovs_use_veth self._ovs_add_port(bridge, tap_name, port_id, mac_address, internal=internal) ns_dev.link.set_address(mac_address) if self.conf.network_device_mtu: ns_dev.link.set_mtu(self.conf.network_device_mtu) if self.conf.ovs_use_veth: root_dev.link.set_mtu(self.conf.network_device_mtu) # Add an interface created by ovs to the namespace. if not self.conf.ovs_use_veth and namespace: namespace_obj = ip.ensure_namespace(namespace) namespace_obj.add_device_to_namespace(ns_dev) ns_dev.link.set_up() if self.conf.ovs_use_veth: root_dev.link.set_up() else: LOG.warn(_("Device %s already exists"), device_name)
def _destroy_router_namespace(self, namespace): ns_ip = ip_lib.IPWrapper(self.root_helper, namespace=namespace) for d in ns_ip.get_devices(exclude_loopback=True): if d.name.startswith(INTERNAL_DEV_PREFIX): # device is on default bridge self.driver.unplug(d.name, namespace=namespace, prefix=INTERNAL_DEV_PREFIX) elif d.name.startswith(EXTERNAL_DEV_PREFIX): self.driver.unplug(d.name, bridge=self.conf.external_network_bridge, namespace=namespace, prefix=EXTERNAL_DEV_PREFIX)
def eligible_for_deletion(conf, namespace, force=False): """Determine whether a namespace is eligible for deletion. Eligibility is determined by having only the lo device or if force is passed as a parameter. """ # filter out namespaces without UUID as the name if not re.match(NS_MANGLING_PATTERN, namespace): return False ip = ip_lib.IPWrapper(conf.root_helper, namespace) return force or ip.namespace_is_empty()
def unplug(self, device_name, bridge=None, namespace=None): """Unplug the interface.""" device = ip_lib.IPDevice(device_name, self.conf.root_helper, namespace) try: device.link.delete() LOG.debug(_("Unplugged interface '%s'") % device_name) except RuntimeError: LOG.error(_("Failed unplugging interface '%s'") % device_name) if namespace: ip = ip_lib.IPWrapper(self.conf.root_helper, namespace) ip.garbage_collect_namespace()
def test_garbage_collect_namespace_existing_empty_ns(self): with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd_cls: ip_ns_cmd_cls.return_value.exists.return_value = True ip = ip_lib.IPWrapper('sudo', 'ns') with mock.patch.object(ip, 'namespace_is_empty') as mock_is_empty: mock_is_empty.return_value = True self.assertTrue(ip.garbage_collect_namespace()) mock_is_empty.assert_called_once_with() expected = [mock.call().exists('ns'), mock.call().delete('ns')] ip_ns_cmd_cls.assert_has_calls(expected)
def exists(self, pool_id): namespace = get_ns_name(pool_id) root_ns = ip_lib.IPWrapper(self.root_helper) socket_path = self._get_state_file_path(pool_id, 'sock') if root_ns.netns.exists(namespace) and os.path.exists(socket_path): try: s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect(socket_path) return True except socket.error: pass return False
def _update_routing_table(self, ri, operation, route): cmd = [ 'ip', 'route', operation, 'to', route['destination'], 'via', route['nexthop'] ] #TODO(nati) move this code to iplib if self.conf.use_namespaces: ip_wrapper = ip_lib.IPWrapper(self.conf.root_helper, namespace=ri.ns_name()) ip_wrapper.netns.execute(cmd, check_exit_code=False) else: utils.execute(cmd, check_exit_code=False, root_helper=self.conf.root_helper)
def disable(self): pid = self.pid if self.active: cmd = ['kill', '-9', pid] if self.namespace: ip_wrapper = ip_lib.IPWrapper(self.root_helper, self.namespace) ip_wrapper.netns.execute(cmd) else: utils.execute(cmd, self.root_helper) elif pid: LOG.debug(_('Process for %(uuid)s pid %(pid)d is stale, ignoring ' 'command') % {'uuid': self.uuid, 'pid': pid}) else: LOG.debug(_('No process started for %s') % self.uuid)
def _send_gratuitous_arp_packet(self, ri, interface_name, ip_address): if self.conf.send_arp_for_ha > 0: arping_cmd = ['arping', '-A', '-U', '-I', interface_name, '-c', self.conf.send_arp_for_ha, ip_address] try: if self.conf.use_namespaces: ip_wrapper = ip_lib.IPWrapper(self.conf.root_helper, namespace=ri.ns_name()) ip_wrapper.netns.execute(arping_cmd, check_exit_code=True) else: utils.execute(arping_cmd, check_exit_code=True, root_helper=self.conf.root_helper) except Exception as e: LOG.error(_("Failed sending gratuitous ARP: %s") % str(e))