X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=snaps%2Fopenstack%2Fcreate_network.py;h=d639c2bea8d499795f00934c45d8323dd17f85da;hb=b59094ae4f53e0da4e4a5fb7c2aece3647d23b8d;hp=4f27eec11840529d3e9cf5346a6ba26993b0b74e;hpb=b0af6e93bb5cc338c289577aad5c4b1bf8de7053;p=snaps.git diff --git a/snaps/openstack/create_network.py b/snaps/openstack/create_network.py index 4f27eec..d639c2b 100644 --- a/snaps/openstack/create_network.py +++ b/snaps/openstack/create_network.py @@ -14,7 +14,10 @@ # limitations under the License. import logging -from neutronclient.common.exceptions import NotFound +import enum +from neutronclient.common.exceptions import NetworkNotFoundClient + +from snaps.openstack.openstack_creator import OpenStackNetworkObject from snaps.openstack.utils import keystone_utils, neutron_utils __author__ = 'spisarski' @@ -22,9 +25,9 @@ __author__ = 'spisarski' logger = logging.getLogger('OpenStackNetwork') -class OpenStackNetwork: +class OpenStackNetwork(OpenStackNetworkObject): """ - Class responsible for creating a network in OpenStack + Class responsible for managing a network in OpenStack """ def __init__(self, os_creds, network_settings): @@ -33,56 +36,39 @@ class OpenStackNetwork: :param os_creds: The credentials to connect with OpenStack :param network_settings: The settings used to create a network """ - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) + self.network_settings = network_settings - self.__neutron = None # Attributes instantiated on create() self.__network = None - self.__subnets = list() - def create(self, cleanup=False): + def initialize(self): + """ + Loads the existing OpenStack network/subnet + :return: The Network domain object or None + """ + super(self.__class__, self).initialize() + + self.__network = neutron_utils.get_network( + self._neutron, network_settings=self.network_settings, + project_id=self.network_settings.get_project_id(self._os_creds)) + + return self.__network + + def create(self): """ Responsible for creating not only the network but then a private subnet, router, and an interface to the router. - :param cleanup: When true, only perform lookups for OpenStack objects. - :return: the created network object or None + :return: the Network domain object """ - self.__neutron = neutron_utils.neutron_client(self.__os_creds) - - logger.info( - 'Creating neutron network %s...' % self.network_settings.name) - net_inst = neutron_utils.get_network( - self.__neutron, self.network_settings.name, - self.network_settings.get_project_id(self.__os_creds)) - if net_inst: - self.__network = net_inst - else: - if not cleanup: - self.__network = neutron_utils.create_network( - self.__neutron, self.__os_creds, self.network_settings) - else: - logger.info( - 'Network does not exist and will not create as in cleanup' - ' mode') - return - logger.debug( - "Network '%s' created successfully" % self.__network.id) - - logger.debug('Creating Subnets....') - for subnet_setting in self.network_settings.subnet_settings: - sub_inst = neutron_utils.get_subnet_by_name( - self.__neutron, subnet_setting.name) - if sub_inst: - self.__subnets.append(sub_inst) - logger.debug( - "Subnet '%s' created successfully" % sub_inst.id) - else: - if not cleanup: - self.__subnets.append( - neutron_utils.create_subnet( - self.__neutron, subnet_setting, self.__os_creds, - self.__network)) + self.initialize() + + if not self.__network: + self.__network = neutron_utils.create_network( + self._neutron, self._os_creds, self.network_settings) + logger.debug( + 'Network [%s] created successfully' % self.__network.id) return self.__network @@ -90,23 +76,11 @@ class OpenStackNetwork: """ Removes and deletes all items created in reverse order. """ - for subnet in self.__subnets: - try: - logger.info( - 'Deleting subnet with name ' + subnet.name) - neutron_utils.delete_subnet(self.__neutron, subnet) - except NotFound as e: - logger.warning( - 'Error deleting subnet with message - ' + str(e)) - pass - self.__subnets = list() - if self.__network: try: - neutron_utils.delete_network(self.__neutron, self.__network) - except NotFound: + neutron_utils.delete_network(self._neutron, self.__network) + except NetworkNotFoundClient: pass - self.__network = None def get_network(self): @@ -116,13 +90,6 @@ class OpenStackNetwork: """ return self.__network - def get_subnets(self): - """ - Returns the OpenStack subnet objects - :return: - """ - return self.__subnets - class NetworkSettings: """ @@ -148,7 +115,9 @@ class NetworkSettings: (default False). :param network_type: the type of network (i.e. vlan|flat). :param physical_network: the name of the physical network - (this is required when network_type is 'flat') + (required when network_type is 'flat') + :param segmentation_id: the id of the segmentation + (this is required when network_type is 'vlan') :param subnets or subnet_settings: List of SubnetSettings objects. :return: """ @@ -175,11 +144,12 @@ class NetworkSettings: self.network_type = kwargs.get('network_type') self.physical_network = kwargs.get('physical_network') + self.segmentation_id = kwargs.get('segmentation_id') self.subnet_settings = list() subnet_settings = kwargs.get('subnets') if not subnet_settings: - subnet_settings = kwargs.get('subnet_settings') + subnet_settings = kwargs.get('subnet_settings', list()) if subnet_settings: for subnet_config in subnet_settings: if isinstance(subnet_config, SubnetSettings): @@ -190,7 +160,6 @@ class NetworkSettings: if not self.name or len(self.name) < 1: raise NetworkSettingsError('Name required for networks') - raise NetworkSettingsError('Name required for networks') def get_project_id(self, os_creds): """ @@ -203,8 +172,8 @@ class NetworkSettings: else: if self.project_name: keystone = keystone_utils.keystone_client(os_creds) - project = keystone_utils.get_project(keystone, - self.project_name) + project = keystone_utils.get_project( + keystone=keystone, project_name=self.project_name) if project: return project.id @@ -231,7 +200,7 @@ class NetworkSettings: if self.project_name: project_id = self.get_project_id(os_creds) if project_id: - out['project_id'] = project_id + out['tenant_id'] = project_id else: raise NetworkSettingsError( 'Could not find project ID for project named - ' + @@ -240,6 +209,8 @@ class NetworkSettings: out['provider:network_type'] = self.network_type if self.physical_network: out['provider:physical_network'] = self.physical_network + if self.segmentation_id: + out['provider:segmentation_id'] = self.segmentation_id if self.external: out['router:external'] = self.external return {'network': out} @@ -251,6 +222,15 @@ class NetworkSettingsError(Exception): """ +class IPv6Mode(enum.Enum): + """ + A rule's direction + """ + slaac = 'slaac' + stateful = 'dhcpv6-stateful' + stateless = 'dhcpv6-stateless' + + class SubnetSettings: """ Class representing a subnet configuration @@ -259,22 +239,23 @@ class SubnetSettings: def __init__(self, **kwargs): """ Constructor - all parameters are optional except cidr (subnet mask) - :param cidr: The CIDR. REQUIRED if config parameter is None - :param ip_version: The IP version, which is 4 or 6. - :param name: The subnet name. + :param name: The subnet name (required) + :param cidr: The CIDR (required) + :param ip_version: The IP version, which is 4 or 6 (required) :param project_name: The name of the project who owns the network. Only administrative users can specify a project ID other than their own. You cannot change this value - through authorization policies. - :param start: The start address for the allocation pools. - :param end: The end address for the allocation pools. - :param gateway_ip: The gateway IP address. + through authorization policies (optional) + :param start: The start address for the allocation pools (optional) + :param end: The end address for the allocation pools (optional) + :param gateway_ip: The gateway IP address (optional) :param enable_dhcp: Set to true if DHCP is enabled and false if DHCP is - disabled. + disabled (optional) :param dns_nameservers: A list of DNS name servers for the subnet. Specify each name server as an IP address and separate multiple entries with a space. - For example [8.8.8.7 8.8.8.8]. + For example [8.8.8.7 8.8.8.8] + (default '8.8.8.8') :param host_routes: A list of host route dictionaries for the subnet. For example: "host_routes":[ @@ -287,12 +268,12 @@ class SubnetSettings: "nexthop":"192.168.0.1" } ] - :param destination: The destination for static route - :param nexthop: The next hop for the destination. - :param ipv6_ra_mode: A valid value is dhcpv6-stateful, - dhcpv6-stateless, or slaac. - :param ipv6_address_mode: A valid value is dhcpv6-stateful, - dhcpv6-stateless, or slaac. + :param destination: The destination for static route (optional) + :param nexthop: The next hop for the destination (optional) + :param ipv6_ra_mode: an instance of the IPv6Mode enum + (optional when enable_dhcp is True) + :param ipv6_address_mode: an instance of the IPv6Mode enum + (optional when enable_dhcp is True) :raise: SubnetSettingsError when config does not have or cidr values are None """ @@ -310,16 +291,19 @@ class SubnetSettings: self.gateway_ip = kwargs.get('gateway_ip') self.enable_dhcp = kwargs.get('enable_dhcp') - if kwargs.get('dns_nameservers'): + if 'dns_nameservers' in kwargs: self.dns_nameservers = kwargs.get('dns_nameservers') else: - self.dns_nameservers = ['8.8.8.8'] + if self.ip_version == 4: + self.dns_nameservers = ['8.8.8.8'] + else: + self.dns_nameservers = list() self.host_routes = kwargs.get('host_routes') self.destination = kwargs.get('destination') self.nexthop = kwargs.get('nexthop') - self.ipv6_ra_mode = kwargs.get('ipv6_ra_mode') - self.ipv6_address_mode = kwargs.get('ipv6_address_mode') + self.ipv6_ra_mode = map_mode(kwargs.get('ipv6_ra_mode')) + self.ipv6_address_mode = map_mode(kwargs.get('ipv6_address_mode')) if not self.name or not self.cidr: raise SubnetSettingsError('Name and cidr required for subnets') @@ -345,12 +329,13 @@ class SubnetSettings: out['name'] = self.name if self.project_name: keystone = keystone_utils.keystone_client(os_creds) - project = keystone_utils.get_project(keystone, self.project_name) + project = keystone_utils.get_project( + keystone=keystone, project_name=self.project_name) project_id = None if project: project_id = project.id if project_id: - out['project_id'] = project_id + out['tenant_id'] = project_id else: raise SubnetSettingsError( 'Could not find project ID for project named - ' + @@ -370,12 +355,40 @@ class SubnetSettings: if self.nexthop: out['nexthop'] = self.nexthop if self.ipv6_ra_mode: - out['ipv6_ra_mode'] = self.ipv6_ra_mode + out['ipv6_ra_mode'] = self.ipv6_ra_mode.value if self.ipv6_address_mode: - out['ipv6_address_mode'] = self.ipv6_address_mode + out['ipv6_address_mode'] = self.ipv6_address_mode.value return out +def map_mode(mode): + """ + Takes a the direction value maps it to the Direction enum. When None return + None + :param mode: the mode value + :return: the IPv6Mode enum object + :raise: SubnetSettingsError if value is invalid + """ + if not mode: + return None + if isinstance(mode, IPv6Mode): + return mode + else: + mode_str = str(mode) + if mode_str == 'slaac': + return IPv6Mode.slaac + elif mode_str == 'dhcpv6-stateful': + return IPv6Mode.stateful + elif mode_str == 'stateful': + return IPv6Mode.stateful + elif mode_str == 'dhcpv6-stateless': + return IPv6Mode.stateless + elif mode_str == 'stateless': + return IPv6Mode.stateless + else: + raise SubnetSettingsError('Invalid mode - ' + mode_str) + + class SubnetSettingsError(Exception): """ Exception to be thrown when subnet settings attributes are incorrect @@ -389,46 +402,42 @@ class PortSettings: def __init__(self, **kwargs): """ - Constructor - all parameters are optional - :param name: A symbolic name for the port. + Constructor + :param name: A symbolic name for the port (optional). :param network_name: The name of the network on which to create the - port. + port (required). :param admin_state_up: A boolean value denoting the administrative - status of the port. True = up / False = down + status of the port (default = True) :param project_name: The name of the project who owns the network. Only administrative users can specify a project ID other than their own. You cannot change this value - through authorization policies. + through authorization policies (optional) :param mac_address: The MAC address. If you specify an address that is not valid, a Bad Request (400) status code is returned. If you do not specify a MAC address, OpenStack Networking tries to allocate one. If a failure occurs, a Service Unavailable (503) status - code is returned. + code is returned (optional) :param ip_addrs: A list of dict objects where each contains two keys 'subnet_name' and 'ip' values which will get mapped to self.fixed_ips. These values will be directly - translated into the fixed_ips dict - :param fixed_ips: A dict where the key is the subnet IDs and value is - the IP address to assign to the port + translated into the fixed_ips dict (optional) :param security_groups: One or more security group IDs. :param allowed_address_pairs: A dictionary containing a set of zero or more allowed address pairs. An address pair contains an IP address and MAC - address. - :param opt_value: The extra DHCP option value. - :param opt_name: The extra DHCP option name. + address (optional) + :param opt_value: The extra DHCP option value (optional) + :param opt_name: The extra DHCP option name (optional) :param device_owner: The ID of the entity that uses this port. - For example, a DHCP agent. + For example, a DHCP agent (optional) :param device_id: The ID of the device that uses this port. - For example, a virtual server. + For example, a virtual server (optional) :return: """ if 'port' in kwargs: kwargs = kwargs['port'] - self.network = None - self.name = kwargs.get('name') self.network_name = kwargs.get('network_name') @@ -440,7 +449,6 @@ class PortSettings: self.project_name = kwargs.get('project_name') self.mac_address = kwargs.get('mac_address') self.ip_addrs = kwargs.get('ip_addrs') - self.fixed_ips = kwargs.get('fixed_ips') self.security_groups = kwargs.get('security_groups') self.allowed_address_pairs = kwargs.get('allowed_address_pairs') self.opt_value = kwargs.get('opt_value') @@ -448,32 +456,33 @@ class PortSettings: self.device_owner = kwargs.get('device_owner') self.device_id = kwargs.get('device_id') - if not self.name or not self.network_name: + if not self.network_name: raise PortSettingsError( - 'The attributes neutron, name, and network_name are required ' - 'for PortSettings') + 'The attribute network_name is required') - def __set_fixed_ips(self, neutron): + def __get_fixed_ips(self, neutron): """ Sets the self.fixed_ips value :param neutron: the Neutron client :return: None """ - if not self.fixed_ips and self.ip_addrs: - self.fixed_ips = list() + + fixed_ips = list() + if self.ip_addrs: for ip_addr_dict in self.ip_addrs: - subnet = neutron_utils.get_subnet_by_name(neutron, - ip_addr_dict[ - 'subnet_name']) - if subnet: - self.fixed_ips.append({'ip_address': ip_addr_dict['ip'], - 'subnet_id': subnet.id}) + subnet = neutron_utils.get_subnet( + neutron, subnet_name=ip_addr_dict['subnet_name']) + if subnet and 'ip' in ip_addr_dict: + fixed_ips.append({'ip_address': ip_addr_dict['ip'], + 'subnet_id': subnet.id}) else: raise PortSettingsError( 'Invalid port configuration, subnet does not exist ' 'with name - ' + ip_addr_dict['subnet_name']) + return fixed_ips + def dict_for_neutron(self, neutron, os_creds): """ Returns a dictionary object representing this object. @@ -485,26 +494,24 @@ class PortSettings: :param os_creds: the OpenStack credentials :return: the dictionary object """ - self.__set_fixed_ips(neutron) out = dict() project_id = None if self.project_name: keystone = keystone_utils.keystone_client(os_creds) - project = keystone_utils.get_project(keystone, self.project_name) + project = keystone_utils.get_project( + keystone=keystone, project_name=self.project_name) if project: project_id = project.id - if not self.network: - self.network = neutron_utils.get_network(neutron, - self.network_name, - project_id) - if not self.network: + network = neutron_utils.get_network( + neutron, network_name=self.network_name, project_id=project_id) + if not network: raise PortSettingsError( 'Cannot locate network with name - ' + self.network_name) - out['network_id'] = self.network.id + out['network_id'] = network.id if self.admin_state_up is not None: out['admin_state_up'] = self.admin_state_up @@ -512,15 +519,18 @@ class PortSettings: out['name'] = self.name if self.project_name: if project_id: - out['project_id'] = project_id + out['tenant_id'] = project_id else: raise PortSettingsError( 'Could not find project ID for project named - ' + self.project_name) if self.mac_address: out['mac_address'] = self.mac_address - if self.fixed_ips and len(self.fixed_ips) > 0: - out['fixed_ips'] = self.fixed_ips + + fixed_ips = self.__get_fixed_ips(neutron) + if fixed_ips and len(fixed_ips) > 0: + out['fixed_ips'] = fixed_ips + if self.security_groups: out['security_groups'] = self.security_groups if self.allowed_address_pairs and len(self.allowed_address_pairs) > 0: @@ -542,7 +552,7 @@ class PortSettings: self.project_name == other.project_name and self.mac_address == other.mac_address and self.ip_addrs == other.ip_addrs and - self.fixed_ips == other.fixed_ips and + # self.fixed_ips == other.fixed_ips and self.security_groups == other.security_groups and self.allowed_address_pairs == other.allowed_address_pairs and self.opt_value == other.opt_value and