import logging
from neutronclient.common.exceptions import NotFound
+
+from snaps.openstack.openstack_creator import OpenStackNetworkObject
from snaps.openstack.utils import keystone_utils, neutron_utils
__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):
: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))
+
+ if self.__network:
+ for subnet_setting in self.network_settings.subnet_settings:
+ sub_inst = neutron_utils.get_subnet(
+ self._neutron, subnet_settings=subnet_setting)
+ if sub_inst:
+ self.__subnets.append(sub_inst)
+ logger.debug(
+ "Subnet '%s' created successfully" % sub_inst.id)
+
+ 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['network'][
- 'id'])
-
- logger.debug('Creating Subnets....')
+ 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)
+
for subnet_setting in self.network_settings.subnet_settings:
- sub_inst = neutron_utils.get_subnet_by_name(self.__neutron,
- subnet_setting.name)
+ sub_inst = neutron_utils.get_subnet(
+ self._neutron, subnet_settings=subnet_setting)
+ if not sub_inst:
+ sub_inst = neutron_utils.create_subnet(
+ self._neutron, subnet_setting, self._os_creds,
+ self.__network)
if sub_inst:
self.__subnets.append(sub_inst)
logger.debug(
- "Subnet '%s' created successfully" % sub_inst['subnet'][
- 'id'])
- else:
- if not cleanup:
- self.__subnets.append(
- neutron_utils.create_subnet(self.__neutron,
- subnet_setting,
- self.__os_creds,
- self.__network))
+ "Subnet '%s' created successfully" % sub_inst.id)
return self.__network
for subnet in self.__subnets:
try:
logger.info(
- 'Deleting subnet with name ' + subnet['subnet']['name'])
- neutron_utils.delete_subnet(self.__neutron, subnet)
+ '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))
if self.__network:
try:
- neutron_utils.delete_network(self.__neutron, self.__network)
+ neutron_utils.delete_network(self._neutron, self.__network)
except NotFound:
pass
(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:
"""
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')
SubnetSettings(**subnet_config['subnet']))
if not self.name or len(self.name) < 1:
- raise Exception('Name required for networks')
+ raise NetworkSettingsError('Name required for networks')
def get_project_id(self, os_creds):
"""
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
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 Exception(
+ raise NetworkSettingsError(
'Could not find project ID for project named - ' +
self.project_name)
if self.network_type:
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}
+class NetworkSettingsError(Exception):
+ """
+ Exception to be thrown when networks settings attributes are incorrect
+ """
+
+
class SubnetSettings:
"""
Class representing a subnet configuration
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":[
"nexthop":"192.168.0.1"
}
]
- :param destination: The destination for static route
- :param nexthop: The next hop for the destination.
+ :param destination: The destination for static route (optional)
+ :param nexthop: The next hop for the destination (optional)
:param ipv6_ra_mode: A valid value is dhcpv6-stateful,
- dhcpv6-stateless, or slaac.
+ dhcpv6-stateless, or slaac (optional)
:param ipv6_address_mode: A valid value is dhcpv6-stateful,
- dhcpv6-stateless, or slaac.
- :raise: Exception when config does not have or cidr values are None
+ dhcpv6-stateless, or slaac (optional)
+ :raise: SubnetSettingsError when config does not have or cidr values
+ are None
"""
- self.cidr = kwargs['cidr']
+ self.cidr = kwargs.get('cidr')
if kwargs.get('ip_version'):
self.ip_version = kwargs['ip_version']
else:
self.ipv6_address_mode = kwargs.get('ipv6_address_mode')
if not self.name or not self.cidr:
- raise Exception('Name and cidr required for subnets')
+ raise SubnetSettingsError('Name and cidr required for subnets')
def dict_for_neutron(self, os_creds, network=None):
"""
}
if network:
- out['network_id'] = network['network']['id']
+ out['network_id'] = network.id
if self.name:
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 Exception(
+ raise SubnetSettingsError(
'Could not find project ID for project named - ' +
self.project_name)
if self.start and self.end:
return out
+class SubnetSettingsError(Exception):
+ """
+ Exception to be thrown when subnet settings attributes are incorrect
+ """
+
+
class PortSettings:
"""
Class representing a port configuration
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:
"""
- self.network = None
+ if 'port' in kwargs:
+ kwargs = kwargs['port']
self.name = kwargs.get('name')
self.network_name = kwargs.get('network_name')
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')
self.device_owner = kwargs.get('device_owner')
self.device_id = kwargs.get('device_id')
- if not self.name or not self.network_name:
- raise Exception(
- 'The attributes neutron, name, and network_name are required '
- 'for PortSettings')
+ if not self.network_name:
+ raise PortSettingsError(
+ '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['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 Exception(
+ 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.
: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:
- raise Exception(
+ 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['network']['id']
+ out['network_id'] = network.id
if self.admin_state_up is not None:
out['admin_state_up'] = self.admin_state_up
out['name'] = self.name
if self.project_name:
if project_id:
- out['project_id'] = project_id
+ out['tenant_id'] = project_id
else:
- raise Exception(
+ 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:
if self.device_id:
out['device_id'] = self.device_id
return {'port': out}
+
+ def __eq__(self, other):
+ return (self.name == other.name and
+ self.network_name == other.network_name and
+ self.admin_state_up == other.admin_state_up and
+ 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.security_groups == other.security_groups and
+ self.allowed_address_pairs == other.allowed_address_pairs and
+ self.opt_value == other.opt_value and
+ self.opt_name == other.opt_name and
+ self.device_owner == other.device_owner and
+ self.device_id == other.device_id)
+
+
+class PortSettingsError(Exception):
+ """
+ Exception to be thrown when port settings attributes are incorrect
+ """