def parse_tier_data(tier, data): data = safe_decode_json(data, log_tag="tier %s" % tier) try: common.validate_tier_data(tier, data) except ValidationFailed as e: _log.error("Validation failed for tier data for tier %s: %r", tier, e) return None else: return data
def parse_ipam_pool(pool_id, raw_json): pool = safe_decode_json(raw_json, log_tag="ipam pool %s" % pool_id) try: common.validate_ipam_pool(pool_id, pool, 4) except ValidationFailed as e: _log.exception("Validation failed for ipam pool %s: %s; %r", pool_id, pool, e) return None else: return pool
def parse_rules(profile_id, raw_json): rules = safe_decode_json(raw_json, log_tag="rules %s" % profile_id) try: common.validate_rules(profile_id, rules) except ValidationFailed as e: _log.exception("Validation failed for profile %s rules: %s; %r", profile_id, rules, e) return None else: return rules
def parse_profile(profile_id, raw_json, require_selector=False, require_order=False): rules = safe_decode_json(raw_json, log_tag="rules %s" % profile_id) try: common.validate_profile(profile_id, rules) except ValidationFailed as e: _log.exception("Validation failed for profile %s rules: %s", profile_id, rules) return None else: return rules
def parse_policy(profile_id, raw_json, require_selector=False, require_order=False): policy = safe_decode_json(raw_json, log_tag="policy %s" % profile_id) try: common.validate_policy(profile_id, policy) except ValidationFailed as e: _log.exception("Validation failed for policy %s: %s", profile_id, policy) return None else: return policy
def parse_labels(profile_id, raw_json): labels = safe_decode_json(raw_json, log_tag="profile labels for %s" % profile_id) try: common.validate_labels(profile_id, labels) except ValidationFailed: _log.exception("Validation failed for profile %s labels : %s", profile_id, labels) return None else: return labels
def parse_tags(profile_id, raw_json): tags = safe_decode_json(raw_json, log_tag="tags %s" % profile_id) try: common.validate_tags(profile_id, tags) except ValidationFailed: _log.exception("Validation failed for profile %s tags : %s", profile_id, tags) return None else: # The tags aren't in a top-level object so we need to manually # intern them here. return intern_list(tags)
def parse_endpoint(config, combined_id, raw_json): endpoint = safe_decode_json(raw_json, log_tag="endpoint %s" % combined_id.endpoint) try: common.validate_endpoint(config, combined_id, endpoint) except ValidationFailed as e: _log.warning("Validation failed for endpoint %s, treating as " "missing: %s; %r", combined_id, e.message, raw_json) endpoint = None else: _log.debug("Validated endpoint : %s", endpoint) return endpoint
def parse_host_ep(config, combined_id, raw_json): iface_data = safe_decode_json(raw_json, log_tag="iface %s" % combined_id.endpoint) try: common.validate_host_endpoint(config, combined_id, iface_data) except ValidationFailed as e: _log.warning("Validation failed for host endpoint %s, treating as " "missing: %s; %r", combined_id, e.message, raw_json) iface_data = None else: _log.debug("Validated endpoint : %s", iface_data) return iface_data
def calculate_new_subnets(self, ports, current_subnets): """Calculate and return subnets needed for PORTS. Given a current set of PORTS that we need to provide DHCP for, calculate all the subnets that we need for those, and get their data either from CURRENT_SUBNETS or from reading etcd. If the new set of subnets is equivalent to what we already had in CURRENT_SUBNETS, return None. Otherwise return the new set of subnets. """ # Gather required subnet IDs. subnet_ids = set() for port in ports: for fixed_ip in port['fixed_ips']: subnet_ids.add(fixed_ip['subnet_id']) LOG.debug("Needed subnet IDs: %s", subnet_ids) # Compare against the existing set of IDs. existing_ids = set([s.id for s in current_subnets]) LOG.debug("Existing subnet IDs: %s", existing_ids) if subnet_ids == existing_ids: LOG.debug("Subnets unchanged") return None # Prepare required new subnet data. new_subnets = [] for subnet_id in subnet_ids: # Check if we already have this subnet. existing = [s for s in current_subnets if s.id == subnet_id] if existing: # We do. Assume subnet data hasn't changed. new_subnets.extend(existing) else: LOG.debug("Read subnet %s from etcd", subnet_id) # Read the data for this subnet. subnet_key = key_for_subnet(subnet_id) try: response = self.client.read(subnet_key, consistent=True) data = 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 etcd.EtcdKeyNotFound() # 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': [] } if ip_version == 6: subnet['ipv6_address_mode'] = constants.DHCPV6_STATEFUL subnet['ipv6_ra_mode'] = constants.DHCPV6_STATEFUL # Add this to the set to be returned. new_subnets.append(subnet) except etcd.EtcdKeyNotFound: LOG.warning("No data for subnet %s", subnet_id) return new_subnets
def on_endpoint_set(self, response, hostname, orchestrator, workload_id, endpoint_id): """Handler for endpoint creations and updates. Endpoint data is, for example: { 'state': 'active' or 'inactive', 'name': port['interface_name'], 'mac': port['mac_address'], 'profile_ids': port['security_groups'], 'ipv4_nets': ['10.28.0.2/32'], 'ipv4_gateway': '10.28.0.1', 'ipv6_nets': ['2001:db8:1::2/128'], 'ipv6_gateway': '2001:db8:1::1' } Port properties needed by DHCP code are: { 'id': <unique ID>, 'network_id': <network ID>, 'device_owner': 'calico', 'device_id': <Linux interface name>, 'fixed_ips': [ { 'subnet_id': <subnet ID>, 'ip_address': '10.28.0.2' } ], 'mac_address: <MAC address>, 'extra_dhcp_opts': ... (optional) } Network properties are: { 'subnets': [ <subnet object> ], 'id': <network ID>, 'namespace': None, 'ports: [ <port object> ], 'tenant_id': ? } Subnet properties are: { 'enable_dhcp': True, 'ip_version': 4 or 6, 'cidr': '10.28.0.0/24', 'dns_nameservers': [], 'id': <subnet ID>, 'gateway_ip': <gateway IP address>, 'host_routes': [], 'ipv6_address_mode': 'dhcpv6-stateful' | 'dhcpv6-stateless', 'ipv6_ra_mode': 'dhcpv6-stateful' | 'dhcpv6-stateless' } """ # Get the endpoint data. endpoint = safe_decode_json(response.value, 'endpoint') if not (isinstance(endpoint, dict) and 'ipv4_nets' in endpoint and 'ipv4_subnet_ids' in endpoint and 'ipv6_nets' in endpoint and 'ipv6_subnet_ids' in endpoint and 'name' in endpoint and 'mac' in endpoint): # Endpoint data is invalid. LOG.warning("Invalid endpoint data: %s => %s", response.value, endpoint) return # Construct NetModel port equivalent of Calico's endpoint data. fixed_ips = [] dns_assignments = [] fqdn = endpoint.get('fqdn') for ip_version in [4, 6]: # Generate the fixed IPs and DNS assignments for the current IP # version. for addrm, subnet_id in zip(endpoint['ipv%s_nets' % ip_version], endpoint['ipv%s_subnet_ids' % ip_version]): ip_addr = addrm.split('/')[0] fixed_ips.append({'subnet_id': subnet_id, 'ip_address': ip_addr}) if fqdn: dns_assignments.append({'hostname': fqdn.split('.')[0], 'ip_address': ip_addr, 'fqdn': fqdn}) port = {'id': endpoint_id, 'network_id': NETWORK_ID, 'device_owner': 'calico', 'device_id': endpoint['name'], 'fixed_ips': fixed_ips, 'mac_address': endpoint['mac'], 'extra_dhcp_opts': []} if fqdn: port['dns_assignment'] = dns_assignments # Add this port into the NetModel. LOG.debug("new port: %s", port) self.agent.cache.put_port(dhcp.DictModel(port)) # Now check for impact on subnets and DHCP driver. self.on_ports_changed()
def on_endpoint_set(self, response, hostname, orchestrator, workload_id, endpoint_id): """Handler for endpoint creations and updates. Endpoint data is, for example: { 'state': 'active' or 'inactive', 'name': port['interface_name'], 'mac': port['mac_address'], 'profile_ids': port['security_groups'], 'ipv4_nets': ['10.28.0.2/32'], 'ipv4_gateway': '10.28.0.1', 'ipv6_nets': ['2001:db8:1::2/128'], 'ipv6_gateway': '2001:db8:1::1' } Port properties needed by DHCP code are: { 'id': <unique ID>, 'network_id': <network ID>, 'device_owner': 'calico', 'device_id': <Linux interface name>, 'fixed_ips': [ { 'subnet_id': <subnet ID>, 'ip_address': '10.28.0.2' } ], 'mac_address: <MAC address>, 'extra_dhcp_opts': ... (optional) } Network properties are: { 'subnets': [ <subnet object> ], 'id': <network ID>, 'namespace': None, 'ports: [ <port object> ], 'tenant_id': ? } Subnet properties are: { 'enable_dhcp': True, 'ip_version': 4 or 6, 'cidr': '10.28.0.0/24', 'dns_nameservers': [], 'id': <subnet ID>, 'gateway_ip': <gateway IP address>, 'host_routes': [], 'ipv6_address_mode': 'dhcpv6-stateful' | 'dhcpv6-stateless', 'ipv6_ra_mode': 'dhcpv6-stateful' | 'dhcpv6-stateless' } """ # Get the endpoint data. endpoint = safe_decode_json(response.value, 'endpoint') if not (isinstance(endpoint, dict) and 'ipv4_nets' in endpoint and 'ipv4_subnet_ids' in endpoint and 'ipv6_nets' in endpoint and 'ipv6_subnet_ids' in endpoint and 'name' in endpoint and 'mac' in endpoint): # Endpoint data is invalid. LOG.warning("Invalid endpoint data: %s => %s", response.value, endpoint) return # Construct NetModel port equivalent of Calico's endpoint data. fixed_ips = [] dns_assignments = [] fqdn = endpoint.get('fqdn') for ip_version in [4, 6]: # Generate the fixed IPs and DNS assignments for the current IP # version. for addrm, subnet_id in zip( endpoint['ipv%s_nets' % ip_version], endpoint['ipv%s_subnet_ids' % ip_version]): ip_addr = addrm.split('/')[0] fixed_ips.append({ 'subnet_id': subnet_id, 'ip_address': ip_addr }) if fqdn: dns_assignments.append({ 'hostname': fqdn.split('.')[0], 'ip_address': ip_addr, 'fqdn': fqdn }) port = { 'id': endpoint_id, 'network_id': NETWORK_ID, 'device_owner': 'calico', 'device_id': endpoint['name'], 'fixed_ips': fixed_ips, 'mac_address': endpoint['mac'], 'extra_dhcp_opts': [] } if fqdn: port['dns_assignment'] = dns_assignments # Add this port into the NetModel. LOG.debug("new port: %s", port) self.agent.cache.put_port(dhcp.DictModel(port)) # Now check for impact on subnets and DHCP driver. self.on_ports_changed()
def calculate_new_subnets(self, ports, current_subnets): """Calculate and return subnets needed for PORTS. Given a current set of PORTS that we need to provide DHCP for, calculate all the subnets that we need for those, and get their data either from CURRENT_SUBNETS or from reading etcd. If the new set of subnets is equivalent to what we already had in CURRENT_SUBNETS, return None. Otherwise return the new set of subnets. """ # Gather required subnet IDs. subnet_ids = set() for port in ports: for fixed_ip in port['fixed_ips']: subnet_ids.add(fixed_ip['subnet_id']) LOG.debug("Needed subnet IDs: %s", subnet_ids) # Compare against the existing set of IDs. existing_ids = set([s.id for s in current_subnets]) LOG.debug("Existing subnet IDs: %s", existing_ids) if subnet_ids == existing_ids: LOG.debug("Subnets unchanged") return None # Prepare required new subnet data. new_subnets = [] for subnet_id in subnet_ids: # Check if we already have this subnet. existing = [s for s in current_subnets if s.id == subnet_id] if existing: # We do. Assume subnet data hasn't changed. new_subnets.extend(existing) else: LOG.debug("Read subnet %s from etcd", subnet_id) # Read the data for this subnet. subnet_key = key_for_subnet(subnet_id) try: response = self.client.read(subnet_key, consistent=True) data = 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 etcd.EtcdKeyNotFound() # 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': []} if ip_version == 6: subnet['ipv6_address_mode'] = constants.DHCPV6_STATEFUL subnet['ipv6_ra_mode'] = constants.DHCPV6_STATEFUL # Add this to the set to be returned. new_subnets.append(subnet) except etcd.EtcdKeyNotFound: LOG.warning("No data for subnet %s", subnet_id) return new_subnets