X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=snaps%2Fopenstack%2Futils%2Fneutron_utils.py;h=806bb532868494c12ebad35f6dd93e5a6bc496a3;hb=d5d282dd1ffab96e85332bea580689c3297a25f8;hp=0dfc61aa908262027fb204e18390a06ee75f0a47;hpb=2a6d3a0f73eb58762bdbe10567cfbb6edec54abb;p=snaps.git diff --git a/snaps/openstack/utils/neutron_utils.py b/snaps/openstack/utils/neutron_utils.py index 0dfc61a..806bb53 100644 --- a/snaps/openstack/utils/neutron_utils.py +++ b/snaps/openstack/utils/neutron_utils.py @@ -17,7 +17,10 @@ import logging from neutronclient.common.exceptions import NotFound from neutronclient.neutron.client import Client -from snaps.domain.network import SecurityGroup, SecurityGroupRule +from snaps.domain.network import ( + Port, SecurityGroup, SecurityGroupRule, Router, InterfaceRouter, Subnet, + Network) +from snaps.domain.project import NetworkQuotas from snaps.domain.vm_inst import FloatingIp from snaps.openstack.utils import keystone_utils @@ -38,7 +41,8 @@ def neutron_client(os_creds): :return: the client object """ return Client(api_version=os_creds.network_api_version, - session=keystone_utils.keystone_session(os_creds)) + session=keystone_utils.keystone_session(os_creds), + region_name=os_creds.region_name) def create_network(neutron, os_creds, network_settings): @@ -49,67 +53,67 @@ def create_network(neutron, os_creds, network_settings): :param network_settings: A dictionary containing the network configuration and is responsible for creating the network request JSON body - :return: the network object + :return: a SNAPS-OO Network domain object """ if neutron and network_settings: logger.info('Creating network with name ' + network_settings.name) json_body = network_settings.dict_for_neutron(os_creds) - return neutron.create_network(body=json_body) + os_network = neutron.create_network(body=json_body) + return Network(**os_network['network']) else: - logger.error("Failed to create network") - raise Exception + raise NeutronException('Failded to create network') def delete_network(neutron, network): """ Deletes a network for OpenStack :param neutron: the client - :param network: the network object + :param network: a SNAPS-OO Network domain object """ if neutron and network: - logger.info('Deleting network with name ' + network['network']['name']) - neutron.delete_network(network['network']['id']) + logger.info('Deleting network with name ' + network.name) + neutron.delete_network(network.id) -def get_network(neutron, network_name, project_id=None): +def get_network(neutron, network_settings=None, network_name=None, + project_id=None): """ - Returns an object (dictionary) of the first network found with a given name - and project_id (if included) + Returns Network SNAPS-OO domain object the first network found with + either the given attributes from the network_settings object if not None, + else the query will use just the name from the network_name parameter. + When the project_id is included, that will be added to the query filter. :param neutron: the client + :param network_settings: the NetworkSettings object used to create filter :param network_name: the name of the network to retrieve :param project_id: the id of the network's project - :return: + :return: a SNAPS-OO Network domain object """ net_filter = dict() - if network_name: + if network_settings: + net_filter['name'] = network_settings.name + elif network_name: net_filter['name'] = network_name + if project_id: net_filter['project_id'] = project_id networks = neutron.list_networks(**net_filter) for network, netInsts in networks.items(): for inst in netInsts: - if inst.get('name') == network_name: - if project_id and inst.get('project_id') == project_id: - return {'network': inst} - else: - return {'network': inst} - return None + return Network(**inst) def get_network_by_id(neutron, network_id): """ - Returns the network object (dictionary) with the given ID + Returns the network object (dictionary) with the given ID else None :param neutron: the client :param network_id: the id of the network to retrieve - :return: + :return: a SNAPS-OO Network domain object """ networks = neutron.list_networks(**{'id': network_id}) - for network, netInsts in networks.items(): - for inst in netInsts: - if inst.get('id') == network_id: - return {'network': inst} - return None + for network in networks['networks']: + if network['id'] == network_id: + return Network(**network) def create_subnet(neutron, subnet_settings, os_creds, network=None): @@ -121,43 +125,96 @@ def create_subnet(neutron, subnet_settings, os_creds, network=None): and is responsible for creating the subnet request JSON body :param os_creds: the OpenStack credentials - :return: the subnet object + :return: a SNAPS-OO Subnet domain object """ if neutron and network and subnet_settings: json_body = {'subnets': [subnet_settings.dict_for_neutron( os_creds, network=network)]} logger.info('Creating subnet with name ' + subnet_settings.name) subnets = neutron.create_subnet(body=json_body) - return {'subnet': subnets['subnets'][0]} + return Subnet(**subnets['subnets'][0]) else: - logger.error("Failed to create subnet.") - raise Exception + raise NeutronException('Failed to create subnet') def delete_subnet(neutron, subnet): """ Deletes a network subnet for OpenStack :param neutron: the client - :param subnet: the subnet object + :param subnet: a SNAPS-OO Subnet domain object """ if neutron and subnet: - logger.info('Deleting subnet with name ' + subnet['subnet']['name']) - neutron.delete_subnet(subnet['subnet']['id']) + logger.info('Deleting subnet with name ' + subnet.name) + neutron.delete_subnet(subnet.id) -def get_subnet_by_name(neutron, subnet_name): +def get_subnet(neutron, subnet_settings=None, subnet_name=None): """ - Returns the first subnet object (dictionary) found with a given name + Returns the first subnet object that fits the query else None including + if subnet_settings or subnet_name parameters are None. :param neutron: the client - :param subnet_name: the name of the network to retrieve - :return: + :param subnet_settings: the subnet settings of the object to retrieve + :param subnet_name: the name of the subnet to retrieve + :return: a SNAPS-OO Subnet domain object or None """ - subnets = neutron.list_subnets(**{'name': subnet_name}) - for subnet, subnetInst in subnets.items(): - for inst in subnetInst: - if inst.get('name') == subnet_name: - return {'subnet': inst} - return None + sub_filter = dict() + if subnet_settings: + sub_filter['name'] = subnet_settings.name + sub_filter['cidr'] = subnet_settings.cidr + if subnet_settings.gateway_ip: + sub_filter['gateway_ip'] = subnet_settings.gateway_ip + + if subnet_settings.enable_dhcp is not None: + sub_filter['enable_dhcp'] = subnet_settings.enable_dhcp + + if subnet_settings.destination: + sub_filter['destination'] = subnet_settings.destination + + if subnet_settings.nexthop: + sub_filter['nexthop'] = subnet_settings.nexthop + + if subnet_settings.ipv6_ra_mode: + sub_filter['ipv6_ra_mode'] = subnet_settings.ipv6_ra_mode + + if subnet_settings.ipv6_address_mode: + sub_filter['ipv6_address_mode'] = subnet_settings.ipv6_address_mode + elif subnet_name: + sub_filter['name'] = subnet_name + else: + return None + + subnets = neutron.list_subnets(**sub_filter) + for subnet in subnets['subnets']: + return Subnet(**subnet) + + +def get_subnet_by_id(neutron, subnet_id): + """ + Returns a SNAPS-OO Subnet domain object for a given ID + :param neutron: the OpenStack neutron client + :param subnet_id: the subnet ID + :return: a Subnet object + """ + os_subnet = neutron.show_subnet(subnet_id) + if os_subnet and 'subnet' in os_subnet: + return Subnet(**os_subnet['subnet']) + + +def get_subnets_by_network(neutron, network): + """ + Returns a list of SNAPS-OO Subnet domain objects + :param neutron: the OpenStack neutron client + :param network: the SNAPS-OO Network domain object + :return: a list of Subnet objects + """ + out = list() + + os_subnets = neutron.list_subnets(network_id=network.id) + + for os_subnet in os_subnets['subnets']: + out.append(Subnet(**os_subnet)) + + return out def create_router(neutron, os_creds, router_settings): @@ -168,41 +225,64 @@ def create_router(neutron, os_creds, router_settings): :param router_settings: A dictionary containing the router configuration and is responsible for creating the subnet request JSON body - :return: the router object + :return: a SNAPS-OO Router domain object """ if neutron: json_body = router_settings.dict_for_neutron(neutron, os_creds) logger.info('Creating router with name - ' + router_settings.name) - return neutron.create_router(json_body) + os_router = neutron.create_router(json_body) + return Router(**os_router['router']) else: logger.error("Failed to create router.") - raise Exception + raise NeutronException('Failed to create router') def delete_router(neutron, router): """ Deletes a router for OpenStack :param neutron: the client - :param router: the router object + :param router: a SNAPS-OO Router domain object """ if neutron and router: - logger.info('Deleting router with name - ' + router['router']['name']) - neutron.delete_router(router=router['router']['id']) - return True + logger.info('Deleting router with name - ' + router.name) + neutron.delete_router(router=router.id) -def get_router_by_name(neutron, router_name): +def get_router_by_id(neutron, router_id): """ - Returns the first router object (dictionary) found with a given name + Returns a router with a given ID, else None if not found :param neutron: the client - :param router_name: the name of the network to retrieve - :return: + :param router_id: the Router ID + :return: a SNAPS-OO Router domain object + """ + router = neutron.show_router(router_id) + if router: + return Router(**router['router']) + + +def get_router(neutron, router_settings=None, router_name=None): """ - routers = neutron.list_routers(**{'name': router_name}) - for router, routerInst in routers.items(): - for inst in routerInst: - if inst.get('name') == router_name: - return {'router': inst} + Returns the first router object (dictionary) found the given the settings + values if not None, else finds the first with the value of the router_name + parameter, else None + :param neutron: the client + :param router_settings: the RouterSettings object + :param router_name: the name of the network to retrieve + :return: a SNAPS-OO Router domain object + """ + router_filter = dict() + if router_settings: + router_filter['name'] = router_settings.name + if router_settings.admin_state_up is not None: + router_filter['admin_state_up'] = router_settings.admin_state_up + elif router_name: + router_filter['name'] = router_name + else: + return None + + routers = neutron.list_routers(**router_filter) + for routerInst in routers['routers']: + return Router(**routerInst) return None @@ -217,34 +297,35 @@ def add_interface_router(neutron, router, subnet=None, port=None): :return: the interface router object """ if subnet and port: - raise Exception('Cannot add interface to the router. Both subnet and ' - 'port were sent in. Either or please.') + raise NeutronException( + 'Cannot add interface to the router. Both subnet and ' + 'port were sent in. Either or please.') if neutron and router and (router or subnet): - logger.info('Adding interface to router with name ' + - router['router']['name']) - return neutron.add_interface_router( - router=router['router']['id'], - body=__create_port_json_body(subnet, port)) + logger.info('Adding interface to router with name ' + router.name) + os_intf_router = neutron.add_interface_router( + router=router.id, body=__create_port_json_body(subnet, port)) + return InterfaceRouter(**os_intf_router) else: - raise Exception('Unable to create interface router as neutron client,' - ' router or subnet were not created') + raise NeutronException( + 'Unable to create interface router as neutron client,' + ' router or subnet were not created') def remove_interface_router(neutron, router, subnet=None, port=None): """ Removes an interface router for OpenStack :param neutron: the client - :param router: the router object + :param router: the SNAPS-OO Router domain object :param subnet: the subnet object (either subnet or port, not both) :param port: the port object """ if router: try: logger.info('Removing router interface from router named ' + - router['router']['name']) + router.name) neutron.remove_interface_router( - router=router['router']['id'], + router=router.id, body=__create_port_json_body(subnet, port)) except NotFound as e: logger.warning('Could not remove router interface. NotFound - %s', @@ -264,14 +345,16 @@ def __create_port_json_body(subnet=None, port=None): :return: the dict """ if subnet and port: - raise Exception('Cannot create JSON body with both subnet and port') + raise NeutronException( + 'Cannot create JSON body with both subnet and port') if not subnet and not port: - raise Exception('Cannot create JSON body without subnet or port') + raise NeutronException( + 'Cannot create JSON body without subnet or port') if subnet: - return {"subnet_id": subnet['subnet']['id']} + return {"subnet_id": subnet.id} else: - return {"port_id": port['port']['id']} + return {"port_id": port.id} def create_port(neutron, os_creds, port_settings): @@ -280,56 +363,111 @@ def create_port(neutron, os_creds, port_settings): :param neutron: the client :param os_creds: the OpenStack credentials :param port_settings: the settings object for port configuration - :return: the port object + :return: the SNAPS-OO Port domain object """ json_body = port_settings.dict_for_neutron(neutron, os_creds) logger.info('Creating port for network with name - %s', port_settings.network_name) - return neutron.create_port(body=json_body) + os_port = neutron.create_port(body=json_body)['port'] + return Port(name=os_port['name'], id=os_port['id'], + ips=os_port['fixed_ips'], + mac_address=os_port['mac_address'], + allowed_address_pairs=os_port['allowed_address_pairs']) def delete_port(neutron, port): """ Removes an OpenStack port :param neutron: the client - :param port: the port object - :return: + :param port: the SNAPS-OO Port domain object """ - logger.info('Deleting port with name ' + port['port']['name']) - neutron.delete_port(port['port']['id']) + logger.info('Deleting port with name ' + port.name) + neutron.delete_port(port.id) -def get_port_by_name(neutron, port_name): +def get_port(neutron, port_settings=None, port_name=None): """ - Returns the first port object (dictionary) found with a given name + Returns the first port object (dictionary) found for the given query :param neutron: the client - :param port_name: the name of the port to retrieve - :return: - """ - ports = neutron.list_ports(**{'name': port_name}) + :param port_settings: the PortSettings object used for generating the query + :param port_name: if port_settings is None, this name is the value to place + into the query + :return: a SNAPS-OO Port domain object + """ + port_filter = dict() + + if port_settings: + if port_settings.name and len(port_settings.name) > 0: + port_filter['name'] = port_settings.name + if port_settings.admin_state_up: + port_filter['admin_state_up'] = port_settings.admin_state_up + if port_settings.device_id: + port_filter['device_id'] = port_settings.device_id + if port_settings.mac_address: + port_filter['mac_address'] = port_settings.mac_address + if port_settings.network_name: + network = get_network(neutron, + network_name=port_settings.network_name) + port_filter['network_id'] = network.id + elif port_name: + port_filter['name'] = port_name + + ports = neutron.list_ports(**port_filter) for port in ports['ports']: - if port['name'] == port_name: - return {'port': port} + return Port(**port) + return None + + +def get_port_by_id(neutron, port_id): + """ + Returns a SNAPS-OO Port domain object for the given ID or none if not found + :param neutron: the client + :param port_id: the to query + :return: a SNAPS-OO Port domain object or None + """ + port = neutron.show_port(port_id) + if port: + return Port(**port['port']) return None +def get_ports(neutron, network, ips=None): + """ + Returns a list of SNAPS-OO Port objects for all OpenStack Port objects that + are associated with the 'network' parameter + :param neutron: the client + :param network: SNAPS-OO Network domain object + :param ips: the IPs to lookup if not None + :return: a SNAPS-OO Port domain object or None if not found + """ + out = list() + ports = neutron.list_ports(**{'network_id': network.id}) + for port in ports['ports']: + if ips: + for fixed_ips in port['fixed_ips']: + if ('ip_address' in fixed_ips and + fixed_ips['ip_address'] in ips) or ips is None: + out.append(Port(**port)) + break + else: + out.append(Port(**port)) + + return out + + def create_security_group(neutron, keystone, sec_grp_settings): """ Creates a security group object in OpenStack :param neutron: the Neutron client :param keystone: the Keystone client :param sec_grp_settings: the security group settings - :return: the security group object + :return: a SNAPS-OO SecurityGroup domain object """ logger.info('Creating security group with name - %s', sec_grp_settings.name) os_group = neutron.create_security_group( sec_grp_settings.dict_for_neutron(keystone)) - return SecurityGroup( - id=os_group['security_group']['id'], - name=os_group['security_group']['name'], - project_id=os_group['security_group'].get( - 'project_id', os_group['security_group'].get('tenant_id'))) + return SecurityGroup(**os_group['security_group']) def delete_security_group(neutron, sec_grp): @@ -342,21 +480,38 @@ def delete_security_group(neutron, sec_grp): neutron.delete_security_group(sec_grp.id) -def get_security_group(neutron, name): +def get_security_group(neutron, sec_grp_settings=None, sec_grp_name=None, + project_id=None): """ - Returns the first security group object of the given name else None + Returns the first security group for a given query. The query gets built + from the sec_grp_settings parameter if not None, else only the name of + the security group will be used, else if the query parameters are None then + None will be returned :param neutron: the client - :param name: the name of security group object to retrieve + :param sec_grp_settings: an instance of SecurityGroupSettings config object + :param sec_grp_name: the name of security group object to retrieve + :param project_id: the ID of the project/tentant object that owns the + secuity group to retrieve + :return: a SNAPS-OO SecurityGroup domain object or None if not found """ - logger.info('Retrieving security group with name - ' + name) - groups = neutron.list_security_groups(**{'name': name}) + sec_grp_filter = dict() + if project_id: + sec_grp_filter['tenant_id'] = project_id + + if sec_grp_settings: + sec_grp_filter['name'] = sec_grp_settings.name + + if sec_grp_settings.description: + sec_grp_filter['description'] = sec_grp_settings.description + elif sec_grp_name: + sec_grp_filter['name'] = sec_grp_name + else: + return None + + groups = neutron.list_security_groups(**sec_grp_filter) for group in groups['security_groups']: - if group['name'] == name: - return SecurityGroup( - id=group['id'], name=group['name'], - project_id=group.get('project_id', group.get('tenant_id'))) - return None + return SecurityGroup(**group) def get_security_group_by_id(neutron, sec_grp_id): @@ -364,15 +519,14 @@ def get_security_group_by_id(neutron, sec_grp_id): Returns the first security group object of the given name else None :param neutron: the client :param sec_grp_id: the id of the security group to retrieve + :return: a SNAPS-OO SecurityGroup domain object or None if not found """ logger.info('Retrieving security group with ID - ' + sec_grp_id) groups = neutron.list_security_groups(**{'id': sec_grp_id}) for group in groups['security_groups']: if group['id'] == sec_grp_id: - return SecurityGroup( - id=group['id'], name=group['name'], - project_id=group.get('project_id', group.get('tenant_id'))) + return SecurityGroup(**group) return None @@ -381,7 +535,7 @@ def create_security_group_rule(neutron, sec_grp_rule_settings): Creates a security group object in OpenStack :param neutron: the client :param sec_grp_rule_settings: the security group rule settings - :return: the security group object + :return: a SNAPS-OO SecurityGroupRule domain object """ logger.info('Creating security group to security group - %s', sec_grp_rule_settings.sec_grp_name) @@ -405,7 +559,7 @@ def get_rules_by_security_group(neutron, sec_grp): """ Retrieves all of the rules for a given security group :param neutron: the client - :param sec_grp: the SNAPS SecurityGroup object + :param sec_grp: a list of SNAPS SecurityGroupRule domain objects """ logger.info('Retrieving security group rules associate with the ' 'security group - %s', sec_grp.name) @@ -424,6 +578,7 @@ def get_rule_by_id(neutron, sec_grp, rule_id): :param neutron: the client :param sec_grp: the SNAPS SecurityGroup domain object :param rule_id: the rule's ID + :param sec_grp: a SNAPS SecurityGroupRule domain object """ rules = neutron.list_security_group_rules( **{'security_group_id': sec_grp.id}) @@ -438,26 +593,39 @@ def get_external_networks(neutron): Returns a list of external OpenStack network object/dict for all external networks :param neutron: the client - :return: a list of external networks (empty list if none configured) + :return: a list of external networks of Type SNAPS-OO domain class Network """ out = list() for network in neutron.list_networks( **{'router:external': True})['networks']: - out.append({'network': network}) + out.append(Network(**network)) return out -def get_floating_ips(neutron): +def get_floating_ips(neutron, ports=None): """ Returns all of the floating IPs + When ports is not None, FIPs returned must be associated with one of the + ports in the list and a tuple 2 where the first element being the port's + ID and the second being the FloatingIp SNAPS-OO domain object. + When ports is None, all known FloatingIp SNAPS-OO domain objects will be + returned in a list :param neutron: the Neutron client - :return: a list of SNAPS FloatingIp objects + :param ports: a list of tuple 2 where index 0 is the port name and index 1 + is the SNAPS-OO Port object + :return: a list of tuple 2 (port_id, SNAPS FloatingIp) objects when ports + is not None else a list of Port objects """ out = list() fips = neutron.list_floatingips() for fip in fips['floatingips']: - out.append(FloatingIp(inst_id=fip['id'], - ip=fip['floating_ip_address'])) + if ports: + for port_name, port in ports: + if port and port.id == fip['port_id']: + out.append((port.id, FloatingIp(**fip))) + break + else: + out.append(FloatingIp(**fip)) return out @@ -471,17 +639,17 @@ def create_floating_ip(neutron, ext_net_name): :return: the SNAPS FloatingIp object """ logger.info('Creating floating ip to external network - ' + ext_net_name) - ext_net = get_network(neutron, ext_net_name) + ext_net = get_network(neutron, network_name=ext_net_name) if ext_net: fip = neutron.create_floatingip( body={'floatingip': - {'floating_network_id': ext_net['network']['id']}}) + {'floating_network_id': ext_net.id}}) - return FloatingIp(inst_id=fip['floatingip']['id'], + return FloatingIp(id=fip['floatingip']['id'], ip=fip['floatingip']['floating_ip_address']) else: - raise Exception('Cannot create floating IP, ' - 'external network not found') + raise NeutronException( + 'Cannot create floating IP, external network not found') def get_floating_ip(neutron, floating_ip): @@ -494,13 +662,12 @@ def get_floating_ip(neutron, floating_ip): """ logger.debug('Attempting to retrieve existing floating ip with IP - %s', floating_ip.ip) - os_fip = get_os_floating_ip(neutron, floating_ip) + os_fip = __get_os_floating_ip(neutron, floating_ip) if os_fip: - return FloatingIp( - inst_id=os_fip['id'], ip=os_fip['floating_ip_address']) + return FloatingIp(id=os_fip['id'], ip=os_fip['floating_ip_address']) -def get_os_floating_ip(neutron, floating_ip): +def __get_os_floating_ip(neutron, floating_ip): """ Returns an OpenStack floating IP object parameter @@ -527,3 +694,42 @@ def delete_floating_ip(neutron, floating_ip): logger.debug('Attempting to delete existing floating ip with IP - %s', floating_ip.ip) return neutron.delete_floatingip(floating_ip.id) + + +def get_network_quotas(neutron, project_id): + """ + Returns a list of all available keypairs + :param neutron: the neutron client + :param project_id: the project's ID of the quotas to lookup + :return: an object of type NetworkQuotas or None if not found + """ + quota = neutron.show_quota(project_id) + if quota: + return NetworkQuotas(**quota['quota']) + + +def update_quotas(neutron, project_id, network_quotas): + """ + Updates the networking quotas for a given project + :param neutron: the Neutron client + :param project_id: the project's ID that requires quota updates + :param network_quotas: an object of type NetworkQuotas containing the + values to update + :return: + """ + update_body = dict() + update_body['security_group'] = network_quotas.security_group + update_body['security_group_rule'] = network_quotas.security_group_rule + update_body['floatingip'] = network_quotas.floatingip + update_body['network'] = network_quotas.network + update_body['port'] = network_quotas.port + update_body['router'] = network_quotas.router + update_body['subnet'] = network_quotas.subnet + + return neutron.update_quota(project_id, {'quota': update_body}) + + +class NeutronException(Exception): + """ + Exception when calls to the Keystone client cannot be served properly + """