def create_port_dict(self, network_id, subnet_id, mac_address, ip_version=4, ip_address=None): ip_address = (self._IP_ADDRS[ip_version]['addr'] if not ip_address else ip_address) port_dict = dhcp.DictModel({ "id": uuidutils.generate_uuid(), "name": "foo", "mac_address": mac_address, "network_id": network_id, "admin_state_up": True, "device_id": uuidutils.generate_uuid(), "device_owner": "foo", "fixed_ips": [{ "subnet_id": subnet_id, "ip_address": ip_address }], }) return port_dict
def port_update_end(self, context, payload): """Handle the port.update.end notification event.""" updated_port = dhcp.DictModel(payload['port']) if self.cache.is_port_message_stale(payload['port']): LOG.debug("Discarding stale port update: %s", updated_port) return network = self.cache.get_network_by_id(updated_port.network_id) if network: LOG.info(_LI("Trigger reload_allocations for port %s"), updated_port) driver_action = 'reload_allocations' if self._is_port_on_this_agent(updated_port): orig = self.cache.get_port_by_id(updated_port['id']) # assume IP change if not in cache orig = orig or {'fixed_ips': []} old_ips = {i['ip_address'] for i in orig['fixed_ips'] or []} new_ips = {i['ip_address'] for i in updated_port['fixed_ips']} old_subs = {i['subnet_id'] for i in orig['fixed_ips'] or []} new_subs = {i['subnet_id'] for i in updated_port['fixed_ips']} if new_subs != old_subs: # subnets being serviced by port have changed, this could # indicate a subnet_delete is in progress. schedule a # resync rather than an immediate restart so we don't # attempt to re-allocate IPs at the same time the server # is deleting them. self.schedule_resync("Agent port was modified", updated_port.network_id) return elif old_ips != new_ips: LOG.debug("Agent IPs on network %s changed from %s to %s", network.id, old_ips, new_ips) driver_action = 'restart' self.cache.put_port(updated_port) self.call_driver(driver_action, network) self.dhcp_ready_ports.add(updated_port.id)
def get_subnet(self, subnet_id): """Get data for the specified subnet.""" LOG.debug("Get subnet %s", subnet_id) if subnet_id not in self.subnets_by_id: return None data = self.subnets_by_id[subnet_id] LOG.debug("Subnet data: %s", data) # Convert to form expected by NetModel. ip_version = 6 if ':' in data['cidr'] else 4 subnet = { 'enable_dhcp': True, 'ip_version': ip_version, 'cidr': data['cidr'], 'dns_nameservers': data.get('dns_servers') or [], 'id': subnet_id, 'gateway_ip': data['gateway_ip'], 'host_routes': data.get('host_routes', []), 'network_id': data.get('network_id', NETWORK_ID) } if ip_version == 6: subnet['ipv6_address_mode'] = DHCPV6_STATEFUL subnet['ipv6_ra_mode'] = DHCPV6_STATEFUL return dhcp.DictModel(subnet)
def port_update_end(self, context, payload): """Handle the port.update.end notification event.""" updated_port = dhcp.DictModel(payload['port']) network = self.cache.get_network_by_id(updated_port.network_id) if network: self.cache.put_port(updated_port) self.call_driver('reload_allocations', network)
def create_dhcp_port(self, port): """Make a remote process call to create the dhcp port.""" cctxt = self.client.prepare(version='1.1') port = cctxt.call(self.context, 'create_dhcp_port', port=port, host=self.host) if port: return dhcp.DictModel(port)
def create_dhcp_port(self, port): """Make a remote process call to create the dhcp port.""" port = self.call( self.context, self.make_msg('create_dhcp_port', port=port, host=self.host)) if port: return dhcp.DictModel(port)
def _get_subnet(self, subnet_id): # Read data for subnet SUBNET_ID from etcd and translate it to the dict # form expected for insertion into a Neutron NetModel. LOG.debug("Read subnet %s from etcd", subnet_id) self.dirty_subnets.discard(subnet_id) subnet_key = datamodel_v1.key_for_subnet(subnet_id) response = self.client.read(subnet_key, consistent=True) data = etcdutils.safe_decode_json(response.value, 'subnet') LOG.debug("Subnet data: %s", data) if not (isinstance(data, dict) and 'cidr' in data and 'gateway_ip' in data): # Subnet data was invalid. LOG.warning("Invalid subnet data: %s => %s", response.value, data) raise ValidationFailed("Invalid subnet data") # Convert to form expected by NetModel. ip_version = 6 if ':' in data['cidr'] else 4 subnet = { 'enable_dhcp': True, 'ip_version': ip_version, 'cidr': data['cidr'], 'dns_nameservers': data.get('dns_servers') or [], 'id': subnet_id, 'gateway_ip': data['gateway_ip'], 'host_routes': data.get('host_routes', []), 'network_id': data.get('network_id', NETWORK_ID) } if ip_version == 6: subnet['ipv6_address_mode'] = DHCPV6_STATEFUL subnet['ipv6_ra_mode'] = DHCPV6_STATEFUL return dhcp.DictModel(subnet)
def create_subnet_dict(self, net_id, dhcp_enabled=True, ip_version=4, prefix_override=None): cidr = self._IP_ADDRS[ip_version]['cidr'] if prefix_override is not None: cidr = '/'.join((cidr.split('/')[0], str(prefix_override))) sn_dict = dhcp.DictModel({ "id": uuidutils.generate_uuid(), "network_id": net_id, "ip_version": ip_version, "cidr": cidr, "gateway_ip": (self._IP_ADDRS[ip_version]['gateway']), "enable_dhcp": dhcp_enabled, "dns_nameservers": [], "host_routes": [], "ipv6_ra_mode": None, "ipv6_address_mode": None }) if ip_version == 6: sn_dict['ipv6_address_mode'] = lib_const.DHCPV6_STATEFUL return sn_dict
def _get_network(self, network_id): network_dict = self.client.show_network(network_id)['network'] network = dhcp.DictModel(network_dict) network.external = network_dict.get('router:external') obj_subnet = [self._get_subnet(s_id) for s_id in network.subnets] network.subnets = obj_subnet return network
def test_build_cmdline(self, commonutils, device_mgr_cls): v4subnet = mock.Mock() v4subnet.id = 'v4subnet-1' v4subnet.enable_dhcp = True v4subnet.ip_version = 4 v4subnet.cidr = '10.28.0.0/24' v6subnet = mock.Mock() v6subnet.id = 'v6subnet-1' v6subnet.enable_dhcp = True v6subnet.ip_version = 6 v6subnet.cidr = '2001:db8:1::/80' v6subnet.ipv6_ra_mode = DHCPV6_STATEFUL v6subnet.ipv6_address_mode = DHCPV6_STATEFUL network = mock.Mock() network.id = 'calico' network.subnets = [v4subnet, v6subnet] network.mtu = 0 network.ports = [ dhcp.DictModel({'device_id': 'tap1'}), dhcp.DictModel({'device_id': 'tap2'}), dhcp.DictModel({'device_id': 'tap3'}), ] network.non_local_subnets = [] network.get.side_effect = lambda key, dflt=None: dflt device_mgr_cls.return_value.driver.bridged = False dhcp_driver = DnsmasqRouted(cfg.CONF, network, None, plugin=FakePlugin()) with mock.patch.object(dhcp_driver, '_get_value_from_conf_file') as gv: gv.return_value = 'ns-dhcp' cmdline = dhcp_driver._build_cmdline_callback('/run/pid_file') self.assertEqual([ 'dnsmasq', '--no-hosts', '--no-resolv', '--except-interface=lo', '--pid-file=/run/pid_file', '--dhcp-hostsfile=/run/calico/host', '--addn-hosts=/run/calico/addn_hosts', '--dhcp-optsfile=/run/calico/opts', '--dhcp-leasefile=/run/calico/leases', '--dhcp-match=set:ipxe,175', '--bind-dynamic', '--interface=ns-dhcp', '--dhcp-range=set:subnet-v4subnet-1,10.28.0.0' + ',static,255.255.255.0,86400s', '--dhcp-lease-max=16777216', '--conf-file=', '--dhcp-range=set:subnet-v6subnet-1,2001:db8:1::' + ',static,off-link,80,86400s', '--enable-ra', '--interface=tap1', '--interface=tap2', '--interface=tap3', '--bridge-interface=ns-dhcp,tap1,tap2,tap3' ], cmdline)
def get_dhcp_port(self, network_id, device_id): """Make a remote process call to get the dhcp port.""" return dhcp.DictModel(self.call(self.context, self.make_msg('get_dhcp_port', network_id=network_id, device_id=device_id, host=self.host), topic=self.topic))
def _get_network(self, network_id): network_dict = self.client.show_network(network_id)['network'] network = dhcp.DictModel(network_dict) # pylint: disable=assigning-non-slot network.external = network_dict.get('router:external') obj_subnet = [self._get_subnet(s_id) for s_id in network.subnets] network.subnets = obj_subnet return network
def exec_command(self, port_id, command=None): port = dhcp.DictModel(self.client.show_port(port_id)['port']) ip = ip_lib.IPWrapper() namespace = self._get_namespace(port) 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))
def get_dhcp_port(self, network_id, device_id): """Make a remote process call to get the dhcp port.""" cctxt = self.client.prepare() port = cctxt.call(self.context, 'get_dhcp_port', network_id=network_id, device_id=device_id, host=self.host) if port: return dhcp.DictModel(port)
def update_dhcp_port(self, port_id, port): """Make a remote process call to update the dhcp port.""" return dhcp.DictModel(self.call(self.context, self.make_msg('update_dhcp_port', port_id=port_id, port=port, host=self.host), topic=self.topic))
def ensure_probe(self, network_id): ports = self.client.list_ports(network_id=network_id, device_id=socket.gethostname(), device_owner=DEVICE_OWNER_NETWORK_PROBE) info = ports.get('ports', []) if info: return dhcp.DictModel(info[0]) else: return self.create_probe(network_id)
def port_create_end(self, context, payload): """Handle the port.create.end notification event.""" created_port = dhcp.DictModel(payload['port']) update = queue.ResourceUpdate(created_port.network_id, payload.get('priority', DEFAULT_PRIORITY), action='_port_create', resource=created_port) self._queue.add(update)
def list_probes(self): ports = self.client.list_ports(device_owner=[ DEVICE_OWNER_NETWORK_PROBE, DEVICE_OWNER_COMPUTE_PROBE ]) info = ports['ports'] for port in info: port['device_name'] = self.driver.get_device_name( dhcp.DictModel(port)) return info
def port_create_end(self, context, payload): """Handle the port.create.end notification event.""" created_port = dhcp.DictModel(payload['port']) if not dhcp.port_requires_dhcp_configuration(created_port): return update = DHCPResourceUpdate(created_port.network_id, payload.get('priority', DEFAULT_PRIORITY), action='_port_create', resource=created_port, obj_type='port') self._queue.add(update)
def exec_command(self, port_id, command=None): port = dhcp.DictModel(self.client.show_port(port_id)['port']) ip = ip_lib.IPWrapper() namespace = self._get_namespace(port) if not command: return "sudo ip netns exec %s" % self._get_namespace(port) namespace = ip.ensure_namespace(namespace) # NOTE(ralonsoh): this is going to be called from inside the # "neutron-debug" shell command; privsep is not configured. return namespace.netns.execute(shlex.split(command))
def port_update_end(self, context, payload): """Handle the port.update.end notification event.""" updated_port = dhcp.DictModel(payload['port']) with _net_lock(updated_port.network_id): if self.cache.is_port_message_stale(payload['port']): LOG.debug("Discarding stale port update: %s", updated_port) return network = self.cache.get_network_by_id(updated_port.network_id) if not network: return self.reload_allocations(updated_port, network)
def port_update_end(self, context, payload): """Handle the port.update.end notification event.""" updated_port = dhcp.DictModel(payload['port']) if self.cache.is_port_message_stale(updated_port): LOG.debug("Discarding stale port update: %s", updated_port) return update = queue.ResourceUpdate(updated_port.network_id, payload.get('priority', DEFAULT_PRIORITY), action='_port_update', resource=updated_port) self._queue.add(update)
def delete_probe(self, port_id): port = dhcp.DictModel(self.client.show_port(port_id)['port']) namespace = self._get_namespace(port) if ip_lib.network_namespace_exists(namespace): self.driver.unplug(self.driver.get_device_name(port), namespace=namespace) try: ip_lib.delete_network_namespace(namespace) except Exception: LOG.warning('Failed to delete namespace %s', namespace) else: self.driver.unplug(self.driver.get_device_name(port)) self.client.delete_port(port.id)
def create_port_dict(self, network_id, subnet_id, mac_address, ip_version=lib_const.IP_VERSION_4, ip_address=None): ip_address = (self._IP_ADDRS[ip_version]['addr'] if not ip_address else ip_address) port_dict = dhcp.DictModel(id=uuidutils.generate_uuid(), name="foo", mac_address=mac_address, network_id=network_id, admin_state_up=True, device_id=uuidutils.generate_uuid(), device_owner="foo", fixed_ips=[{"subnet_id": subnet_id, "ip_address": ip_address}]) return port_dict
def port_update_end(self, context, payload): """Handle the port.update.end notification event.""" updated_port = dhcp.DictModel(payload['port']) network = self.cache.get_network_by_id(updated_port.network_id) if network: driver_action = 'reload_allocations' if self._is_port_on_this_agent(updated_port): orig = self.cache.get_port_by_id(updated_port['id']) # assume IP change if not in cache old_ips = {i['ip_address'] for i in orig['fixed_ips'] or []} new_ips = {i['ip_address'] for i in updated_port['fixed_ips']} if old_ips != new_ips: driver_action = 'restart' self.cache.put_port(updated_port) self.call_driver(driver_action, network)
def create_subnet_dict(self, net_id, dhcp_enabled=True, ip_version=4): sn_dict = dhcp.DictModel({ "id": uuidutils.generate_uuid(), "network_id": net_id, "ip_version": ip_version, "cidr": self._IP_ADDRS[ip_version]['cidr'], "gateway_ip": (self. _IP_ADDRS[ip_version]['gateway']), "enable_dhcp": dhcp_enabled, "dns_nameservers": [], "host_routes": [], "ipv6_ra_mode": None, "ipv6_address_mode": None}) if ip_version == 6: sn_dict['ipv6_address_mode'] = constants.DHCPV6_STATEFUL return sn_dict
def _create_port(self, network, device_owner): host = self.conf.host body = {'port': {'admin_state_up': True, 'network_id': network.id, 'device_id': '%s' % socket.gethostname(), 'device_owner': '%s:probe' % device_owner, 'tenant_id': network.tenant_id, portbindings.HOST_ID: host, 'fixed_ips': [dict(subnet_id=s.id) for s in network.subnets]}} port_dict = self.client.create_port(body)['port'] port = dhcp.DictModel(port_dict) port.network = network for fixed_ip in port.fixed_ips: fixed_ip.subnet = self._get_subnet(fixed_ip.subnet_id) return port
def port_update_end(self, context, payload): """Handle the port.update.end notification event.""" updated_port = dhcp.DictModel(payload['port']) with _net_lock(updated_port.network_id): if self.cache.is_port_message_stale(payload['port']): LOG.debug("Discarding stale port update: %s", updated_port) return network = self.cache.get_network_by_id(updated_port.network_id) if not network: return LOG.info("Trigger reload_allocations for port %s", updated_port) driver_action = 'reload_allocations' orig = self.cache.get_port_by_id(updated_port['id']) orig = orig or {'fixed_ips': []} if self._is_port_on_this_agent(updated_port): old_ips = {i['ip_address'] for i in orig['fixed_ips'] or []} new_ips = {i['ip_address'] for i in updated_port['fixed_ips']} old_subs = {i['subnet_id'] for i in orig['fixed_ips'] or []} new_subs = {i['subnet_id'] for i in updated_port['fixed_ips']} if new_subs != old_subs: # subnets being serviced by port have changed, this could # indicate a subnet_delete is in progress. schedule a # resync rather than an immediate restart so we don't # attempt to re-allocate IPs at the same time the server # is deleting them. self.schedule_resync("Agent port was modified", updated_port.network_id) return elif old_ips != new_ips: LOG.debug("Agent IPs on network %s changed from %s to %s", network.id, old_ips, new_ips) driver_action = 'restart' elif not old_ips and not new_ips: driver_action = 'disable' self.cache.put_port(updated_port) # NOTE(alegacy): the port may no longer have any IP addresses so if # that's the case we need to use the old ones in order to determine # which VLAN we are operating against. orig_vlan_ids = self._get_port_vlan_ids(orig, network) updated_vlan_ids = self._get_port_vlan_ids(updated_port, network) for vlan_id in set(orig_vlan_ids) | set(updated_vlan_ids): driver_kwargs = {} if not vlan_id else {'vlan_id': vlan_id} self.call_driver(driver_action, network, **driver_kwargs) self.dhcp_ready_ports.add(updated_port.id)
def delete_probe(self, port_id): port = dhcp.DictModel(self.client.show_port(port_id)['port']) network = self._get_network(port.network_id) bridge = None if network.external: bridge = self.conf.external_network_bridge namespace = self._get_namespace(port) if ip_lib.network_namespace_exists(namespace): self.driver.unplug(self.driver.get_device_name(port), bridge=bridge, namespace=namespace) try: ip_lib.delete_network_namespace(namespace) except Exception: LOG.warning('Failed to delete namespace %s', namespace) else: self.driver.unplug(self.driver.get_device_name(port), bridge=bridge) self.client.delete_port(port.id)
def port_create_end(self, context, payload): """Handle the port.create.end notification event.""" created_port = dhcp.DictModel(payload['port']) with _net_lock(created_port.network_id): network = self.cache.get_network_by_id(created_port.network_id) if not network: return new_ips = {i['ip_address'] for i in created_port['fixed_ips']} for port_cached in network.ports: # if there are other ports cached with the same ip address in # the same network this indicate that the cache is out of sync cached_ips = {i['ip_address'] for i in port_cached['fixed_ips']} if new_ips.intersection(cached_ips): self.schedule_resync("Duplicate IP addresses found, " "DHCP cache is out of sync", created_port.network_id) return self.reload_allocations(created_port, network)