Initial patch with all code from CableLabs repository.
[snaps.git] / snaps / openstack / utils / neutron_utils.py
diff --git a/snaps/openstack/utils/neutron_utils.py b/snaps/openstack/utils/neutron_utils.py
new file mode 100644 (file)
index 0000000..6c92d2e
--- /dev/null
@@ -0,0 +1,405 @@
+# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+#                    and others.  All rights reserved.
+#
+# 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.
+import logging
+
+from neutronclient.common.exceptions import NotFound
+from neutronclient.neutron.client import Client
+import keystone_utils
+
+__author__ = 'spisarski'
+
+logger = logging.getLogger('neutron_utils')
+
+"""
+Utilities for basic neutron API calls
+"""
+
+
+def neutron_client(os_creds):
+    """
+    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))
+
+
+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
+    """
+    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)
+    else:
+        logger.error("Failed to create network")
+        raise Exception
+
+
+def delete_network(neutron, network):
+    """
+    Deletes a network for OpenStack
+    :param neutron: the client
+    :param network: the network object
+    """
+    if neutron and network:
+        logger.info('Deleting network with name ' + network['network']['name'])
+        neutron.delete_network(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)
+    :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:
+    """
+    net_filter = dict()
+    if 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.iteritems():
+        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
+
+
+def get_network_by_id(neutron, network_id):
+    """
+    Returns the network object (dictionary) with the given ID
+    :param neutron: the client
+    :param network_id: the id of the network to retrieve
+    :return:
+    """
+    networks = neutron.list_networks(**{'id': network_id})
+    for network, netInsts in networks.iteritems():
+        for inst in netInsts:
+            if inst.get('id') == network_id:
+                return {'network': inst}
+    return None
+
+
+def create_subnet(neutron, subnet_settings, os_creds, network=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 os_creds: the OpenStack credentials
+    :return: the subnet 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]}
+    else:
+        logger.error("Failed to create subnet.")
+        raise Exception
+
+
+def delete_subnet(neutron, subnet):
+    """
+    Deletes a network subnet for OpenStack
+    :param neutron: the client
+    :param subnet: the subnet object
+    """
+    if neutron and subnet:
+        logger.info('Deleting subnet with name ' + subnet['subnet']['name'])
+        neutron.delete_subnet(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:
+    """
+    subnets = neutron.list_subnets(**{'name': subnet_name})
+    for subnet, subnetInst in subnets.iteritems():
+        for inst in subnetInst:
+            if inst.get('name') == subnet_name:
+                return {'subnet': inst}
+    return None
+
+
+def create_router(neutron, os_creds, router_settings):
+    """
+    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
+    """
+    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)
+    else:
+        logger.error("Failed to create router.")
+        raise Exception
+
+
+def delete_router(neutron, router):
+    """
+    Deletes a router for OpenStack
+    :param neutron: the client
+    :param router: the router object
+    """
+    if neutron and router:
+        logger.info('Deleting router with name - ' + router['router']['name'])
+        neutron.delete_router(router=router['router']['id'])
+        return True
+
+
+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:
+    """
+    routers = neutron.list_routers(**{'name': router_name})
+    for router, routerInst in routers.iteritems():
+        for inst in routerInst:
+            if inst.get('name') == router_name:
+                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.
+    :param neutron: the client
+    :param router: the router object
+    :param subnet: the subnet object
+    :param port: the port 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.')
+
+    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))
+    else:
+        raise Exception("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 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))
+        except NotFound as e:
+            logger.warn('Could not remove router interface. NotFound - ' + e.message)
+            pass
+    else:
+        logger.warn('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
+    :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')
+    if not subnet and not port:
+        raise Exception('Cannot create JSON body without subnet or port')
+
+    if subnet:
+        return {"subnet_id": subnet['subnet']['id']}
+    else:
+        return {"port_id": port['port']['id']}
+
+
+def create_port(neutron, os_creds, port_settings):
+    """
+    Creates a port for OpenStack
+    :param neutron: the client
+    :param os_creds: the OpenStack credentials
+    :param port_settings: the settings object for port configuration
+    :return: the port 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)
+
+
+def delete_port(neutron, port):
+    """
+    Removes an OpenStack port
+    :param neutron: the client
+    :param port: the port object
+    :return:
+    """
+    logger.info('Deleting port with name ' + port['port']['name'])
+    neutron.delete_port(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:
+    """
+    ports = neutron.list_ports(**{'name': port_name})
+    for port in ports['ports']:
+        if port['name'] == port_name:
+            return {'port': port}
+    return None
+
+
+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
+    """
+    logger.info('Creating security group with name - ' + sec_grp_settings.name)
+    return neutron.create_security_group(sec_grp_settings.dict_for_neutron(keystone))
+
+
+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
+    """
+    logger.info('Deleting security group with name - ' + sec_grp['security_group']['name'])
+    return neutron.delete_security_group(sec_grp['security_group']['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
+    """
+    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 None
+
+
+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
+    """
+    logger.info('Retrieving security group with ID - ' + sec_grp_id)
+
+    groups = neutron.list_security_groups(**{'sec_grp_id': sec_grp_id})
+    for group in groups['security_groups']:
+        return {'security_group': group}
+    return None
+
+
+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
+    """
+    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))
+
+
+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
+    """
+    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'])
+
+
+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
+    """
+    logger.info('Retrieving security group rules associate with the security group - ' +
+                sec_grp['security_group']['name'])
+    out = list()
+    rules = neutron.list_security_group_rules(**{'security_group_id': sec_grp['security_group']['id']})
+    for rule in rules['security_group_rules']:
+        if rule['security_group_id'] == sec_grp['security_group']['id']:
+            out.append({'security_group_rule': rule})
+    return out
+
+
+def get_rule_by_id(neutron, sec_grp, rule_id):
+    """
+    Deletes a security group object from OpenStack
+    :param neutron: the client
+    :param sec_grp: the security group object
+    :param rule_id: the rule's ID
+    """
+    rules = neutron.list_security_group_rules(**{'security_group_id': sec_grp['security_group']['id']})
+    for rule in rules['security_group_rules']:
+        if rule['id'] == rule_id:
+            return {'security_group_rule': rule}
+    return None
+
+
+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)
+    """
+    out = list()
+    for network in neutron.list_networks(**{'router:external': True})['networks']:
+        out.append({'network': network})
+    return out