-# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+# Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
# and others. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
from neutronclient.common.exceptions import NotFound
from neutronclient.neutron.client import Client
-import keystone_utils
+
+from snaps.domain.network import (
+ Port, SecurityGroup, SecurityGroupRule, Router, InterfaceRouter, Subnet,
+ Network)
+from snaps.domain.vm_inst import FloatingIp
+from snaps.openstack.utils import keystone_utils
__author__ = 'spisarski'
def neutron_client(os_creds):
"""
- Instantiates and returns a client for communications with OpenStack's Neutron server
+ Instantiates and returns a client for communications with OpenStack's
+ Neutron server
:param os_creds: the credentials for connecting to the OpenStack remote API
:return: the client object
"""
- return Client(api_version=os_creds.network_api_version, session=keystone_utils.keystone_session(os_creds))
+ return Client(api_version=os_creds.network_api_version,
+ session=keystone_utils.keystone_session(os_creds),
+ region_name=os_creds.region_name)
def create_network(neutron, os_creds, network_settings):
Creates a network for OpenStack
:param neutron: the client
:param os_creds: the OpenStack credentials
- :param network_settings: A dictionary containing the network configuration and is responsible for creating the
- network request JSON body
- :return: the network object
+ :param network_settings: A dictionary containing the network configuration
+ and is responsible for creating the network
+ request JSON body
+ :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):
"""
- Returns an object (dictionary) of the first network found with a given name and project_id (if included)
+ Returns an object (dictionary) of the first network found with a given name
+ and project_id (if included)
:param neutron: the client
: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:
net_filter['project_id'] = project_id
networks = neutron.list_networks(**net_filter)
- for network, netInsts in networks.iteritems():
+ 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 Network(**inst)
return None
Returns the network object (dictionary) with the given ID
: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.iteritems():
+ for network, netInsts in networks.items():
for inst in netInsts:
if inst.get('id') == network_id:
- return {'network': inst}
+ return Network(**inst)
return None
Creates a network subnet for OpenStack
:param neutron: the client
:param network: the network object
- :param subnet_settings: A dictionary containing the subnet configuration and is responsible for creating the subnet
- request JSON body
+ :param subnet_settings: A dictionary containing the subnet configuration
+ 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)]}
+ 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):
Returns the first subnet object (dictionary) found with a given name
:param neutron: the client
:param subnet_name: the name of the network to retrieve
- :return:
+ :return: a SNAPS-OO Subnet domain object
"""
subnets = neutron.list_subnets(**{'name': subnet_name})
- for subnet, subnetInst in subnets.iteritems():
+ for subnet, subnetInst in subnets.items():
for inst in subnetInst:
- if inst.get('name') == subnet_name:
- return {'subnet': inst}
+ if inst['name'] == subnet_name:
+ return Subnet(**inst)
return None
Creates a router for OpenStack
:param neutron: the client
:param os_creds: the OpenStack credentials
- :param router_settings: A dictionary containing the router configuration and is responsible for creating the subnet
- request JSON body
- :return: the router object
+ :param router_settings: A dictionary containing the router configuration
+ and is responsible for creating the subnet request
+ JSON body
+ :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):
Returns the first router object (dictionary) found with a given name
:param neutron: the client
:param router_name: the name of the network to retrieve
- :return:
+ :return: a SNAPS-OO Router domain object
"""
routers = neutron.list_routers(**{'name': router_name})
- for router, routerInst in routers.iteritems():
+ for router, routerInst in routers.items():
for inst in routerInst:
if inst.get('name') == router_name:
- return {'router': inst}
+ return Router(**inst)
return None
def add_interface_router(neutron, router, subnet=None, port=None):
"""
- Adds an interface router for OpenStack for either a subnet or port. Exception will be raised if requesting for both.
+ Adds an interface router for OpenStack for either a subnet or port.
+ Exception will be raised if requesting for both.
:param neutron: the client
:param router: the router object
:param subnet: the subnet object
: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'])
- neutron.remove_interface_router(router=router['router']['id'], body=__create_port_json_body(subnet, port))
+ logger.info('Removing router interface from router named ' +
+ router.name)
+ neutron.remove_interface_router(
+ router=router.id,
+ body=__create_port_json_body(subnet, port))
except NotFound as e:
- logger.warn('Could not remove router interface. NotFound - ' + e.message)
+ logger.warning('Could not remove router interface. NotFound - %s',
+ e)
pass
else:
- logger.warn('Could not remove router interface, No router object')
+ logger.warning('Could not remove router interface, No router object')
def __create_port_json_body(subnet=None, port=None):
"""
- Returns the dictionary required for creating and deleting router interfaces. Will only work on a subnet or port
- object. Will throw and exception if parameters contain both or neither
+ Returns the dictionary required for creating and deleting router
+ interfaces. Will only work on a subnet or port object. Will throw and
+ exception if parameters contain both or neither
:param subnet: the subnet object
:param port: the port object
: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):
: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 - ' + port_settings.network_name)
- return neutron.create_port(body=json_body)
+ logger.info('Creating port for network with name - %s',
+ port_settings.network_name)
+ 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):
Returns the first port object (dictionary) found with a given name
:param neutron: the client
:param port_name: the name of the port to retrieve
- :return:
+ :return: a SNAPS-OO Port domain object
"""
ports = neutron.list_ports(**{'name': port_name})
for port in ports['ports']:
if port['name'] == port_name:
- return {'port': port}
+ return Port(name=port['name'], id=port['id'],
+ ips=port['fixed_ips'], mac_address=port['mac_address'])
return None
: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 - ' + sec_grp_settings.name)
- return neutron.create_security_group(sec_grp_settings.dict_for_neutron(keystone))
+ 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')))
def delete_security_group(neutron, sec_grp):
"""
Deletes a security group object from OpenStack
:param neutron: the client
- :param sec_grp: the security group object to delete
+ :param sec_grp: the SNAPS SecurityGroup object to delete
"""
- logger.info('Deleting security group with name - ' + sec_grp['security_group']['name'])
- return neutron.delete_security_group(sec_grp['security_group']['id'])
+ logger.info('Deleting security group with name - %s', sec_grp.name)
+ neutron.delete_security_group(sec_grp.id)
def get_security_group(neutron, name):
Returns the first security group object of the given name else None
:param neutron: the client
:param name: the name of security group object 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})
for group in groups['security_groups']:
if group['name'] == name:
- return {'security_group': group}
+ return SecurityGroup(
+ id=group['id'], name=group['name'],
+ project_id=group.get('project_id', group.get('tenant_id')))
return None
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(**{'sec_grp_id': sec_grp_id})
+ groups = neutron.list_security_groups(**{'id': sec_grp_id})
for group in groups['security_groups']:
- return {'security_group': group}
+ 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 None
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 - ' + sec_grp_rule_settings.sec_grp_name)
- return neutron.create_security_group_rule(sec_grp_rule_settings.dict_for_neutron(neutron))
+ logger.info('Creating security group to security group - %s',
+ sec_grp_rule_settings.sec_grp_name)
+ os_rule = neutron.create_security_group_rule(
+ sec_grp_rule_settings.dict_for_neutron(neutron))
+ return SecurityGroupRule(**os_rule['security_group_rule'])
def delete_security_group_rule(neutron, sec_grp_rule):
"""
Deletes a security group object from OpenStack
:param neutron: the client
- :param sec_grp_rule: the security group rule object to delete
+ :param sec_grp_rule: the SNAPS SecurityGroupRule object to delete
"""
- logger.info('Deleting security group rule with ID - ' + sec_grp_rule['security_group_rule']['id'])
- neutron.delete_security_group_rule(sec_grp_rule['security_group_rule']['id'])
+ logger.info('Deleting security group rule with ID - %s',
+ sec_grp_rule.id)
+ neutron.delete_security_group_rule(sec_grp_rule.id)
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 security group object
+ :param sec_grp: a list of SNAPS SecurityGroupRule domain objects
"""
- logger.info('Retrieving security group rules associate with the security group - ' +
- sec_grp['security_group']['name'])
+ logger.info('Retrieving security group rules associate with the '
+ 'security group - %s', sec_grp.name)
out = list()
- rules = neutron.list_security_group_rules(**{'security_group_id': sec_grp['security_group']['id']})
+ rules = neutron.list_security_group_rules(
+ **{'security_group_id': sec_grp.id})
for rule in rules['security_group_rules']:
- if rule['security_group_id'] == sec_grp['security_group']['id']:
- out.append({'security_group_rule': rule})
+ if rule['security_group_id'] == sec_grp.id:
+ out.append(SecurityGroupRule(**rule))
return out
"""
Deletes a security group object from OpenStack
:param neutron: the client
- :param sec_grp: the security group object
+ :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['security_group']['id']})
+ rules = neutron.list_security_group_rules(
+ **{'security_group_id': sec_grp.id})
for rule in rules['security_group_rules']:
if rule['id'] == rule_id:
- return {'security_group_rule': rule}
+ return SecurityGroupRule(**rule)
return None
def get_external_networks(neutron):
"""
- Returns a list of external OpenStack network object/dict for all external networks
+ 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))
+ return out
+
+
+def get_floating_ips(neutron):
+ """
+ Returns all of the floating IPs
+ :param neutron: the Neutron client
+ :return: a list of SNAPS FloatingIp objects
"""
out = list()
- for network in neutron.list_networks(**{'router:external': True})['networks']:
- out.append({'network': network})
+ fips = neutron.list_floatingips()
+ for fip in fips['floatingips']:
+ out.append(FloatingIp(inst_id=fip['id'],
+ ip=fip['floating_ip_address']))
+
return out
+
+
+def create_floating_ip(neutron, ext_net_name):
+ """
+ Returns the floating IP object that was created with this call
+ :param neutron: the Neutron client
+ :param ext_net_name: the name of the external network on which to apply the
+ floating IP address
+ :return: the SNAPS FloatingIp object
+ """
+ logger.info('Creating floating ip to external network - ' + ext_net_name)
+ ext_net = get_network(neutron, ext_net_name)
+ if ext_net:
+ fip = neutron.create_floatingip(
+ body={'floatingip':
+ {'floating_network_id': ext_net.id}})
+
+ return FloatingIp(inst_id=fip['floatingip']['id'],
+ ip=fip['floatingip']['floating_ip_address'])
+ else:
+ raise NeutronException(
+ 'Cannot create floating IP, external network not found')
+
+
+def get_floating_ip(neutron, floating_ip):
+ """
+ Returns a floating IP object that should be identical to the floating_ip
+ parameter
+ :param neutron: the Neutron client
+ :param floating_ip: the SNAPS FloatingIp object
+ :return: hopefully the same floating IP object input
+ """
+ logger.debug('Attempting to retrieve existing floating ip with IP - %s',
+ floating_ip.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'])
+
+
+def __get_os_floating_ip(neutron, floating_ip):
+ """
+ Returns an OpenStack floating IP object
+ parameter
+ :param neutron: the Neutron client
+ :param floating_ip: the SNAPS FloatingIp object
+ :return: hopefully the same floating IP object input
+ """
+ logger.debug('Attempting to retrieve existing floating ip with IP - %s',
+ floating_ip.ip)
+ fips = neutron.list_floatingips(ip=floating_ip.id)
+
+ for fip in fips['floatingips']:
+ if fip['id'] == floating_ip.id:
+ return fip
+
+
+def delete_floating_ip(neutron, floating_ip):
+ """
+ Responsible for deleting a floating IP
+ :param neutron: the Neutron client
+ :param floating_ip: the SNAPS FloatingIp object
+ :return:
+ """
+ logger.debug('Attempting to delete existing floating ip with IP - %s',
+ floating_ip.ip)
+ return neutron.delete_floatingip(floating_ip.id)
+
+
+class NeutronException(Exception):
+ """
+ Exception when calls to the Keystone client cannot be served properly
+ """