# Copyright 2012 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2012 Cisco Systems, Inc. # Copyright 2012 NEC Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from __future__ import absolute_import import collections import logging import warnings import netaddr from django.conf import settings from django.utils.datastructures import SortedDict from django.utils.translation import ugettext_lazy as _ from neutronclient.v2_0 import client as neutron_client from horizon import messages from horizon.utils.memoized import memoized # noqa from openstack_dashboard.api import base from openstack_dashboard.api import network_base from openstack_dashboard.api import nova from openstack_dashboard import policy LOG = logging.getLogger(__name__) IP_VERSION_DICT = {4: 'IPv4', 6: 'IPv6'} OFF_STATE = 'OFF' ON_STATE = 'ON' class NeutronAPIDictWrapper(base.APIDictWrapper): def set_id_as_name_if_empty(self, length=8): try: if not self._apidict['name']: id = self._apidict['id'] if length: id = id[:length] self._apidict['name'] = '(%s)' % id except KeyError: pass def items(self): return self._apidict.items() @property def name_or_id(self): return (self._apidict.get('name') or '(%s)' % self._apidict['id'][:13]) class Agent(NeutronAPIDictWrapper): """Wrapper for neutron agents.""" def __init__(self, apiresource): apiresource['admin_state'] = \ 'UP' if apiresource['admin_state_up'] else 'DOWN' super(Agent, self).__init__(apiresource) class Network(NeutronAPIDictWrapper): """Wrapper for neutron Networks.""" def __init__(self, apiresource): apiresource['admin_state'] = \ 'UP' if apiresource['admin_state_up'] else 'DOWN' # Django cannot handle a key name with a colon, so remap another key for key in apiresource.keys(): if key.find(':'): apiresource['__'.join(key.split(':'))] = apiresource[key] super(Network, self).__init__(apiresource) class Subnet(NeutronAPIDictWrapper): """Wrapper for neutron subnets.""" def __init__(self, apiresource): apiresource['ipver_str'] = get_ipver_str(apiresource['ip_version']) super(Subnet, self).__init__(apiresource) class Port(NeutronAPIDictWrapper): """Wrapper for neutron ports.""" def __init__(self, apiresource): apiresource['admin_state'] = \ 'UP' if apiresource['admin_state_up'] else 'DOWN' if 'mac_learning_enabled' in apiresource: apiresource['mac_state'] = \ ON_STATE if apiresource['mac_learning_enabled'] else OFF_STATE super(Port, self).__init__(apiresource) class Profile(NeutronAPIDictWrapper): """Wrapper for neutron profiles.""" _attrs = ['profile_id', 'name', 'segment_type', 'segment_range', 'sub_type', 'multicast_ip_index', 'multicast_ip_range'] def __init__(self, apiresource): super(Profile, self).__init__(apiresource) class Router(NeutronAPIDictWrapper): """Wrapper for neutron routers.""" def __init__(self, apiresource): apiresource['admin_state'] = \ 'UP' if apiresource['admin_state_up'] else 'DOWN' super(Router, self).__init__(apiresource) class SecurityGroup(NeutronAPIDictWrapper): # Required attributes: id, name, description, tenant_id, rules def __init__(self, sg, sg_dict=None): if sg_dict is None: sg_dict = {sg['id']: sg['name']} sg['rules'] = [SecurityGroupRule(rule, sg_dict) for rule in sg['security_group_rules']] super(SecurityGroup, self).__init__(sg) class SecurityGroupRule(NeutronAPIDictWrapper): # Required attributes: # id, parent_group_id # ip_protocol, from_port, to_port, ip_range, group # ethertype, direction (Neutron specific) def _get_secgroup_name(self, sg_id, sg_dict): if sg_id: if sg_dict is None: sg_dict = {} # If sg name not found in sg_dict, # first two parts of UUID is used as sg name. return sg_dict.get(sg_id, sg_id[:13]) else: return u'' def __init__(self, sgr, sg_dict=None): # In Neutron, if both remote_ip_prefix and remote_group_id are None, # it means all remote IP range is allowed, i.e., 0.0.0.0/0 or ::/0. if not sgr['remote_ip_prefix'] and not sgr['remote_group_id']: if sgr['ethertype'] == 'IPv6': sgr['remote_ip_prefix'] = '::/0' else: sgr['remote_ip_prefix'] = '0.0.0.0/0' rule = { 'id': sgr['id'], 'parent_group_id': sgr['security_group_id'], 'direction': sgr['direction'], 'ethertype': sgr['ethertype'], 'ip_protocol': sgr['protocol'], 'from_port': sgr['port_range_min'], 'to_port': sgr['port_range_max'], } cidr = sgr['remote_ip_prefix'] rule['ip_range'] = {'cidr': cidr} if cidr else {} group = self._get_secgroup_name(sgr['remote_group_id'], sg_dict) rule['group'] = {'name': group} if group else {} super(SecurityGroupRule, self).__init__(rule) def __unicode__(self): if 'name' in self.group: remote = self.group['name'] elif 'cidr' in self.ip_range: remote = self.ip_range['cidr'] else: remote = 'ANY' direction = 'to' if self.direction == 'egress' else 'from' if self.from_port: if self.from_port == self.to_port: proto_port = ("%s/%s" % (self.from_port, self.ip_protocol.lower())) else: proto_port = ("%s-%s/%s" % (self.from_port, self.to_port, self.ip_protocol.lower())) elif self.ip_protocol: try: ip_proto = int(self.ip_protocol) proto_port = "ip_proto=%d" % ip_proto except Exception: # well-defined IP protocol name like TCP, UDP, ICMP. proto_port = self.ip_protocol else: proto_port = '' return (_('ALLOW %(ethertype)s %(proto_port)s ' '%(direction)s %(remote)s') % {'ethertype': self.ethertype, 'proto_port': proto_port, 'remote': remote, 'direction': direction}) class SecurityGroupManager(network_base.SecurityGroupManager): backend = 'neutron' def __init__(self, request): self.request = request self.client = neutronclient(request) def _list(self, **filters): secgroups = self.client.list_security_groups(**filters) return [SecurityGroup(sg) for sg in secgroups.get('security_groups')] def list(self): tenant_id = self.request.user.tenant_id return self._list(tenant_id=tenant_id) def _sg_name_dict(self, sg_id, rules): """Create a mapping dict from secgroup id to its name.""" related_ids = set([sg_id]) related_ids |= set(filter(None, [r['remote_group_id'] for r in rules])) related_sgs = self.client.list_security_groups(id=related_ids, fields=['id', 'name']) related_sgs = related_sgs.get('security_groups') return dict((sg['id'], sg['name']) for sg in related_sgs) def get(self, sg_id): secgroup = self.client.show_security_group(sg_id).get('security_group') sg_dict = self._sg_name_dict(sg_id, secgroup['security_group_rules']) return SecurityGroup(secgroup, sg_dict) def create(self, name, desc): body = {'security_group': {'name': name, 'description': desc}} secgroup = self.client.create_security_group(body) return SecurityGroup(secgroup.get('security_group')) def update(self, sg_id, name, desc): body = {'security_group': {'name': name, 'description': desc}} secgroup = self.client.update_security_group(sg_id, body) return SecurityGroup(secgroup.get('security_group')) def delete(self, sg_id): self.client.delete_security_group(sg_id) def rule_create(self, parent_group_id, direction=None, ethertype=None, ip_protocol=None, from_port=None, to_port=None, cidr=None, group_id=None): if not cidr: cidr = None if from_port < 0: from_port = None if to_port < 0: to_port = None if isinstance(ip_protocol, int) and ip_protocol < 0: ip_protocol = None body = {'security_group_rule': {'security_group_id': parent_group_id, 'direction': direction, 'ethertype': ethertype, 'protocol': ip_protocol, 'port_range_min': from_port, 'port_range_max': to_port, 'remote_ip_prefix': cidr, 'remote_group_id': group_id}} rule = self.client.create_security_group_rule(body) rule = rule.get('security_group_rule') sg_dict = self._sg_name_dict(parent_group_id, [rule]) return SecurityGroupRule(rule, sg_dict) def rule_delete(self, sgr_id): self.client.delete_security_group_rule(sgr_id) def list_by_instance(self, instance_id): """Gets security groups of an instance.""" ports = port_list(self.request, device_id=instance_id) sg_ids = [] for p in ports: sg_ids += p.security_groups return self._list(id=set(sg_ids)) if sg_ids else [] def update_instance_security_group(self, instance_id, new_security_group_ids): ports = port_list(self.request, device_id=instance_id) for p in ports: params = {'security_groups': new_security_group_ids} port_update(self.request, p.id, **params) class FloatingIp(base.APIDictWrapper): _attrs = ['id', 'ip', 'fixed_ip', 'port_id', 'instance_id', 'instance_type', 'pool'] def __init__(self, fip): fip['ip'] = fip['floating_ip_address'] fip['fixed_ip'] = fip['fixed_ip_address'] fip['pool'] = fip['floating_network_id'] super(FloatingIp, self).__init__(fip) class FloatingIpPool(base.APIDictWrapper): pass class FloatingIpTarget(base.APIDictWrapper): pass class FloatingIpManager(network_base.FloatingIpManager): device_owner_map = { 'compute:': 'compute', 'neutron:LOADBALANCER': 'loadbalancer', } def __init__(self, request): self.request = request self.client = neutronclient(request) def list_pools(self): search_opts = {'router:external': True} return [FloatingIpPool(pool) for pool in self.client.list_networks(**search_opts).get('networks')] def _get_instance_type_from_device_owner(self, device_owner): for key, value in self.device_owner_map.items(): if device_owner.startswith(key): return value return device_owner def _set_instance_info(self, fip, port=None): if fip['port_id']: if not port: port = port_get(self.request, fip['port_id']) fip['instance_id'] = port.device_id fip['instance_type'] = self._get_instance_type_from_device_owner( port.device_owner) else: fip['instance_id'] = None fip['instance_type'] = None def list(self, all_tenants=False, **search_opts): if not all_tenants: tenant_id = self.request.user.tenant_id # In Neutron, list_floatingips returns Floating IPs from # all tenants when the API is called with admin role, so # we need to filter them with tenant_id. search_opts['tenant_id'] = tenant_id port_search_opts = {'tenant_id': tenant_id} else: port_search_opts = {} fips = self.client.list_floatingips(**search_opts) fips = fips.get('floatingips') # Get port list to add instance_id to floating IP list # instance_id is stored in device_id attribute ports = port_list(self.request, **port_search_opts) port_dict = SortedDict([(p['id'], p) for p in ports]) for fip in fips: self._set_instance_info(fip, port_dict.get(fip['port_id'])) return [FloatingIp(fip) for fip in fips] def get(self, floating_ip_id): fip = self.client.show_floatingip(floating_ip_id).get('floatingip') self._set_instance_info(fip) return FloatingIp(fip) def allocate(self, pool): body = {'floatingip': {'floating_network_id': pool}} fip = self.client.create_floatingip(body).get('floatingip') self._set_instance_info(fip) return FloatingIp(fip) def release(self, floating_ip_id): self.client.delete_floatingip(floating_ip_id) def associate(self, floating_ip_id, port_id): # NOTE: In Neutron Horizon floating IP support, port_id is # "_" format to identify multiple ports. pid, ip_address = port_id.split('_', 1) update_dict = {'port_id': pid, 'fixed_ip_address': ip_address} self.client.update_floatingip(floating_ip_id, {'floatingip': update_dict}) def disassociate(self, floating_ip_id, port_id): update_dict = {'port_id': None} self.client.update_floatingip(floating_ip_id, {'floatingip': update_dict}) def _get_reachable_subnets(self, ports): # Retrieve subnet list reachable from external network ext_net_ids = [ext_net.id for ext_net in self.list_pools()] gw_routers = [r.id for r in router_list(self.request) if (r.external_gateway_info and r.external_gateway_info.get('network_id') in ext_net_ids)] reachable_subnets = set([p.fixed_ips[0]['subnet_id'] for p in ports if ((p.device_owner == 'network:router_interface') and (p.device_id in gw_routers))]) return reachable_subnets def list_targets(self): tenant_id = self.request.user.tenant_id ports = port_list(self.request, tenant_id=tenant_id) servers, has_more = nova.server_list(self.request) server_dict = SortedDict([(s.id, s.name) for s in servers]) reachable_subnets = self._get_reachable_subnets(ports) targets = [] for p in ports: # Remove network ports from Floating IP targets if p.device_owner.startswith('network:'): continue port_id = p.id server_name = server_dict.get(p.device_id) for ip in p.fixed_ips: if ip['subnet_id'] not in reachable_subnets: continue target = {'name': '%s: %s' % (server_name, ip['ip_address']), 'id': '%s_%s' % (port_id, ip['ip_address']), 'instance_id': p.device_id} targets.append(FloatingIpTarget(target)) return targets def _target_ports_by_instance(self, instance_id): if not instance_id: return None search_opts = {'device_id': instance_id} return port_list(self.request, **search_opts) def get_target_id_by_instance(self, instance_id, target_list=None): if target_list is not None: targets = [target for target in target_list if target['instance_id'] == instance_id] if not targets: return None return targets[0]['id'] else: # In Neutron one port can have multiple ip addresses, so this # method picks up the first one and generate target id. ports = self._target_ports_by_instance(instance_id) if not ports: return None return '{0}_{1}'.format(ports[0].id, ports[0].fixed_ips[0]['ip_address']) def list_target_id_by_instance(self, instance_id, target_list=None): if target_list is not None: return [target['id'] for target in target_list if target['instance_id'] == instance_id] else: ports = self._target_ports_by_instance(instance_id) return ['{0}_{1}'.format(p.id, p.fixed_ips[0]['ip_address']) for p in ports] def is_simple_associate_supported(self): # NOTE: There are two reason that simple association support # needs more considerations. (1) Neutron does not support the # default floating IP pool at the moment. It can be avoided # in case where only one floating IP pool exists. # (2) Neutron floating IP is associated with each VIF and # we need to check whether such VIF is only one for an instance # to enable simple association support. return False def is_supported(self): network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}) return network_config.get('enable_router', True) def get_ipver_str(ip_version): """Convert an ip version number to a human-friendly string.""" return IP_VERSION_DICT.get(ip_version, '') @memoized def neutronclient(request): insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None) LOG.debug('neutronclient connection created using token "%s" and url "%s"' % (request.user.token.id, base.url_for(request, 'network'))) LOG.debug('user_id=%(user)s, tenant_id=%(tenant)s' % {'user': request.user.id, 'tenant': request.user.tenant_id}) c = neutron_client.Client(token=request.user.token.id, auth_url=base.url_for(request, 'identity'), endpoint_url=base.url_for(request, 'network'), insecure=insecure, ca_cert=cacert) return c def network_list(request, **params): LOG.debug("network_list(): params=%s", params) networks = neutronclient(request).list_networks(**params).get('networks') # Get subnet list to expand subnet info in network list. subnets = subnet_list(request) subnet_dict = dict([(s['id'], s) for s in subnets]) # Expand subnet list from subnet_id to values. for n in networks: # Due to potential timing issues, we can't assume the subnet_dict data # is in sync with the network data. n['subnets'] = [subnet_dict[s] for s in n.get('subnets', []) if s in subnet_dict] return [Network(n) for n in networks] def network_list_for_tenant(request, tenant_id, **params): """Return a network list available for the tenant. The list contains networks owned by the tenant and public networks. If requested_networks specified, it searches requested_networks only. """ LOG.debug("network_list_for_tenant(): tenant_id=%s, params=%s" % (tenant_id, params)) # If a user has admin role, network list returned by Neutron API # contains networks that do not belong to that tenant. # So we need to specify tenant_id when calling network_list(). networks = network_list(request, tenant_id=tenant_id, shared=False, **params) # In the current Neutron API, there is no way to retrieve # both owner networks and public networks in a single API call. networks += network_list(request, shared=True, **params) return networks def network_get(request, network_id, expand_subnet=True, **params): LOG.debug("network_get(): netid=%s, params=%s" % (network_id, params)) network = neutronclient(request).show_network(network_id, **params).get('network') # Since the number of subnets per network must be small, # call subnet_get() for each subnet instead of calling # subnet_list() once. if expand_subnet: network['subnets'] = [subnet_get(request, sid) for sid in network['subnets']] return Network(network) def network_create(request, **kwargs): """Create a subnet on a specified network. :param request: request context :param tenant_id: (optional) tenant id of the network created :param name: (optional) name of the network created :returns: Subnet object """ LOG.debug("network_create(): kwargs = %s" % kwargs) # In the case network profiles are being used, profile id is needed. if 'net_profile_id' in kwargs: kwargs['n1kv:profile_id'] = kwargs.pop('net_profile_id') body = {'network': kwargs} network = neutronclient(request).create_network(body=body).get('network') return Network(network) def network_update(request, network_id, **kwargs): LOG.debug("network_update(): netid=%s, params=%s" % (network_id, kwargs)) body = {'network': kwargs} network = neutronclient(request).update_network(network_id, body=body).get('network') return Network(network) def network_delete(request, network_id): LOG.debug("network_delete(): netid=%s" % network_id) neutronclient(request).delete_network(network_id) def subnet_list(request, **params): LOG.debug("subnet_list(): params=%s" % (params)) subnets = neutronclient(request).list_subnets(**params).get('subnets') return [Subnet(s) for s in subnets] def subnet_get(request, subnet_id, **params): LOG.debug("subnet_get(): subnetid=%s, params=%s" % (subnet_id, params)) subnet = neutronclient(request).show_subnet(subnet_id, **params).get('subnet') return Subnet(subnet) def subnet_create(request, network_id, cidr, ip_version, **kwargs): """Create a subnet on a specified network. :param request: request context :param network_id: network id a subnet is created on :param cidr: subnet IP address range :param ip_version: IP version (4 or 6) :param gateway_ip: (optional) IP address of gateway :param tenant_id: (optional) tenant id of the subnet created :param name: (optional) name of the subnet created :returns: Subnet object """ LOG.debug("subnet_create(): netid=%s, cidr=%s, ipver=%d, kwargs=%s" % (network_id, cidr, ip_version, kwargs)) body = {'subnet': {'network_id': network_id, 'ip_version': ip_version, 'cidr': cidr}} body['subnet'].update(kwargs) subnet = neutronclient(request).create_subnet(body=body).get('subnet') return Subnet(subnet) def subnet_update(request, subnet_id, **kwargs): LOG.debug("subnet_update(): subnetid=%s, kwargs=%s" % (subnet_id, kwargs)) body = {'subnet': kwargs} subnet = neutronclient(request).update_subnet(subnet_id, body=body).get('subnet') return Subnet(subnet) def subnet_delete(request, subnet_id): LOG.debug("subnet_delete(): subnetid=%s" % subnet_id) neutronclient(request).delete_subnet(subnet_id) def port_list(request, **params): LOG.debug("port_list(): params=%s" % (params)) ports = neutronclient(request).list_ports(**params).get('ports') return [Port(p) for p in ports] def port_get(request, port_id, **params): LOG.debug("port_get(): portid=%s, params=%s" % (port_id, params)) port = neutronclient(request).show_port(port_id, **params).get('port') return Port(port) def port_create(request, network_id, **kwargs): """Create a port on a specified network. :param request: request context :param network_id: network id a subnet is created on :param device_id: (optional) device id attached to the port :param tenant_id: (optional) tenant id of the port created :param name: (optional) name of the port created :returns: Port object """ LOG.debug("port_create(): netid=%s, kwargs=%s" % (network_id, kwargs)) # In the case policy profiles are being used, profile id is needed. if 'policy_profile_id' in kwargs: kwargs['n1kv:profile_id'] = kwargs.pop('policy_profile_id') body = {'port': {'network_id': network_id}} body['port'].update(kwargs) port = neutronclient(request).create_port(body=body).get('port') return Port(port) def port_delete(request, port_id): LOG.debug("port_delete(): portid=%s" % port_id) neutronclient(request).delete_port(port_id) def port_update(request, port_id, **kwargs): LOG.debug("port_update(): portid=%s, kwargs=%s" % (port_id, kwargs)) body = {'port': kwargs} port = neutronclient(request).update_port(port_id, body=body).get('port') return Port(port) def profile_list(request, type_p, **params): LOG.debug("profile_list(): " "profile_type=%(profile_type)s, params=%(params)s", {'profile_type': type_p, 'params': params}) if type_p == 'network': profiles = neutronclient(request).list_network_profiles( **params).get('network_profiles') elif type_p == 'policy': profiles = neutronclient(request).list_policy_profiles( **params).get('policy_profiles') return [Profile(n) for n in profiles] def profile_get(request, profile_id, **params): LOG.debug("profile_get(): " "profileid=%(profileid)s, params=%(params)s", {'profileid': profile_id, 'params': params}) profile = neutronclient(request).show_network_profile( profile_id, **params).get('network_profile') return Profile(profile) def profile_create(request, **kwargs): LOG.debug("profile_create(): kwargs=%s", kwargs) body = {'network_profile': {}} body['network_profile'].update(kwargs) profile = neutronclient(request).create_network_profile( body=body).get('network_profile') return Profile(profile) def profile_delete(request, profile_id): LOG.debug("profile_delete(): profile_id=%s", profile_id) neutronclient(request).delete_network_profile(profile_id) def profile_update(request, profile_id, **kwargs): LOG.debug("profile_update(): " "profileid=%(profileid)s, kwargs=%(kwargs)s", {'profileid': profile_id, 'kwargs': kwargs}) body = {'network_profile': kwargs} profile = neutronclient(request).update_network_profile( profile_id, body=body).get('network_profile') return Profile(profile) def profile_bindings_list(request, type_p, **params): LOG.debug("profile_bindings_list(): " "profile_type=%(profile_type)s params=%(params)s", {'profile_type': type_p, 'params': params}) if type_p == 'network': bindings = neutronclient(request).list_network_profile_bindings( **params).get('network_profile_bindings') elif type_p == 'policy': bindings = neutronclient(request).list_policy_profile_bindings( **params).get('policy_profile_bindings') return [Profile(n) for n in bindings] def router_create(request, **kwargs): LOG.debug("router_create():, kwargs=%s" % kwargs) body = {'router': {}} body['router'].update(kwargs) router = neutronclient(request).create_router(body=body).get('router') return Router(router) def router_update(request, r_id, **kwargs): LOG.debug("router_update(): router_id=%s, kwargs=%s" % (r_id, kwargs)) body = {'router': {}} body['router'].update(kwargs) router = neutronclient(request).update_router(r_id, body=body) return Router(router['router']) def router_get(request, router_id, **params): router = neutronclient(request).show_router(router_id, **params).get('router') return Router(router) def router_list(request, **params): routers = neutronclient(request).list_routers(**params).get('routers') return [Router(r) for r in routers] def router_delete(request, router_id): neutronclient(request).delete_router(router_id) def router_add_interface(request, router_id, subnet_id=None, port_id=None): body = {} if subnet_id: body['subnet_id'] = subnet_id if port_id: body['port_id'] = port_id client = neutronclient(request) return client.add_interface_router(router_id, body) def router_remove_interface(request, router_id, subnet_id=None, port_id=None): body = {} if subnet_id: body['subnet_id'] = subnet_id if port_id: body['port_id'] = port_id neutronclient(request).remove_interface_router(router_id, body) def router_add_gateway(request, router_id, network_id): body = {'network_id': network_id} neutronclient(request).add_gateway_router(router_id, body) def router_remove_gateway(request, router_id): neutronclient(request).remove_gateway_router(router_id) def tenant_quota_get(request, tenant_id): return base.QuotaSet(neutronclient(request).show_quota(tenant_id)['quota']) def tenant_quota_update(request, tenant_id, **kwargs): quotas = {'quota': kwargs} return neutronclient(request).update_quota(tenant_id, quotas) def agent_list(request, **params): agents = neutronclient(request).list_agents(**params) return [Agent(a) for a in agents['agents']] def list_dhcp_agent_hosting_networks(request, network, **params): agents = neutronclient(request).list_dhcp_agent_hosting_networks(network, **params) return [Agent(a) for a in agents['agents']] def add_network_to_dhcp_agent(request, dhcp_agent, network_id): body = {'network_id': network_id} return neutronclient(request).add_network_to_dhcp_agent(dhcp_agent, body) def remove_network_from_dhcp_agent(request, dhcp_agent, network_id): return neutronclient(request).remove_network_from_dhcp_agent(dhcp_agent, network_id) def provider_list(request): providers = neutronclient(request).list_service_providers() return providers['service_providers'] def servers_update_addresses(request, servers, all_tenants=False): """Retrieve servers networking information from Neutron if enabled. Should be used when up to date networking information is required, and Nova's networking info caching mechanism is not fast enough. """ # Get all (filtered for relevant servers) information from Neutron try: ports = port_list(request, device_id=[instance.id for instance in servers]) fips = FloatingIpManager(request) if fips.is_supported(): floating_ips = fips.list(all_tenants=all_tenants, port_id=[port.id for port in ports]) else: floating_ips = [] networks = network_list(request, id=[port.network_id for port in ports]) except Exception: error_message = _('Unable to connect to Neutron.') LOG.error(error_message) messages.error(request, error_message) return # Map instance to its ports instances_ports = collections.defaultdict(list) for port in ports: instances_ports[port.device_id].append(port) # Map port to its floating ips ports_floating_ips = collections.defaultdict(list) for fip in floating_ips: ports_floating_ips[fip.port_id].append(fip) # Map network id to its name network_names = dict(((network.id, network.name) for network in networks)) for server in servers: try: addresses = _server_get_addresses( request, server, instances_ports, ports_floating_ips, network_names) except Exception as e: LOG.error(e) else: server.addresses = addresses def _server_get_addresses(request, server, ports, floating_ips, network_names): def _format_address(mac, ip, type): try: version = netaddr.IPAddress(ip).version except Exception as e: error_message = _('Unable to parse IP address %s.') % ip LOG.error(error_message) messages.error(request, error_message) raise e return {u'OS-EXT-IPS-MAC:mac_addr': mac, u'version': version, u'addr': ip, u'OS-EXT-IPS:type': type} addresses = collections.defaultdict(list) instance_ports = ports.get(server.id, []) for port in instance_ports: network_name = network_names.get(port.network_id) if network_name is not None: for fixed_ip in port.fixed_ips: addresses[network_name].append( _format_address(port.mac_address, fixed_ip['ip_address'], u'fixed')) port_fips = floating_ips.get(port.id, []) for fip in port_fips: addresses[network_name].append( _format_address(port.mac_address, fip.floating_ip_address, u'floating')) return dict(addresses) @memoized def list_extensions(request): extensions_list = neutronclient(request).list_extensions() if 'extensions' in extensions_list: return extensions_list['extensions'] else: return {} @memoized def is_extension_supported(request, extension_alias): extensions = list_extensions(request) for extension in extensions: if extension['alias'] == extension_alias: return True else: return False def is_enabled_by_config(name, default=True): if hasattr(settings, 'OPENSTACK_QUANTUM_NETWORK'): warnings.warn( 'OPENSTACK_QUANTUM_NETWORK setting is deprecated and will be ' 'removed in the near future. ' 'Please use OPENSTACK_NEUTRON_NETWORK instead.', DeprecationWarning) network_config = (getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}) or getattr(settings, 'OPENSTACK_QUANTUM_NETWORK', {})) return network_config.get(name, default) @memoized def is_service_enabled(request, config_name, ext_name): return (is_enabled_by_config(config_name) and is_extension_supported(request, ext_name)) @memoized def is_quotas_extension_supported(request): if (is_enabled_by_config('enable_quotas', False) and is_extension_supported(request, 'quotas')): return True else: return False # Using this mechanism till a better plugin/sub-plugin detection # mechanism is available. # When using specific plugins the profile_support can be # turned on if needed to configure and/or use profiles. # Since this is a temporary mechanism used to detect profile_support # @memorize is not being used. # TODO(absubram): Change this config variable check with # subplugin/plugin detection API when it becomes available. def is_port_profiles_supported(): network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}) # Can be used to check for vendor specific plugin profile_support = network_config.get('profile_support', None) if str(profile_support).lower() == 'cisco': return True # FEATURE_MAP is used to define: # - related neutron extension name (key: "extension") # - corresponding dashboard config (key: "config") # - RBAC policies (key: "poclies") # If a key is not contained, the corresponding permission check is skipped. FEATURE_MAP = { 'dvr': { 'extension': 'dvr', 'config': { 'name': 'enable_distributed_router', 'default': False, }, 'policies': { 'get': 'get_router:distributed', 'create': 'create_router:distributed', 'update': 'update_router:distributed', } }, 'l3-ha': { 'extension': 'l3-ha', 'config': {'name': 'enable_ha_router', 'default': False}, 'policies': { 'get': 'get_router:ha', 'create': 'create_router:ha', 'update': 'update_router:ha', } }, } def get_feature_permission(request, feature, operation=None): """Check if a feature-specific field can be displayed. This method check a permission for a feature-specific field. Such field is usually provided through Neutron extension. :param request: Request Object :param feature: feature name defined in FEATURE_MAP :param operation (optional): Operation type. The valid value should be defined in FEATURE_MAP[feature]['policies'] It must be specified if FEATURE_MAP[feature] has 'policies'. """ network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {}) feature_info = FEATURE_MAP.get(feature) if not feature_info: # Translators: Only used inside Horizon code and invisible to users raise ValueError(_("The requested feature '%(feature)s' is unknown. " "Please make sure to specify a feature defined " "in FEATURE_MAP.")) # Check dashboard settings feature_config = feature_info.get('config') if feature_config: if not network_config.get(feature_config['name'], feature_config['default']): return False # Check policy feature_policies = feature_info.get('policies') policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None) if feature_policies and policy_check: policy_name = feature_policies.get(operation) if not policy_name: # Translators: Only used inside Horizon code and invisible to users raise ValueError(_("The 'operation' parameter for " "get_feature_permission '%(feature)s' " "is invalid. It should be one of %(allowed)s") % {'feature': feature, 'allowed': ' '.join(feature_policies.keys())}) role = (('network', policy_name),) if not policy.check(role, request): return False # Check if a required extension is enabled feature_extension = feature_info.get('extension') if feature_extension: try: return is_extension_supported(request, feature_extension) except Exception: msg = (_("Failed to check Neutron '%s' extension is not supported") % feature_extension) LOG.info(msg) return False # If all checks are passed, now a given feature is allowed. return True