aboutsummaryrefslogtreecommitdiffstats
path: root/sfc/lib/openstack_utils.py
diff options
context:
space:
mode:
authorManuel Buil <mbuil@suse.com>2017-12-12 12:15:00 +0100
committerManuel Buil <mbuil@suse.com>2017-12-12 15:40:50 +0100
commitd4757d239155cbd21ce45b656469c10d67cdf569 (patch)
tree5192ead08005f563cfe7d85147ef176d1939d9e7 /sfc/lib/openstack_utils.py
parent8814891148390d427c22e583b2d7451dbd600197 (diff)
Clean up our utils.py
Utils.py was getting messy. This patch divides it into three different files: * test_utils.py * odl_utils.py * openstack_utils.py The tacker library is integrated into openstack_utils.py Change-Id: I310949d1cee49b6aa1c9b3396bf6d6ca458cbaac Signed-off-by: Manuel Buil <mbuil@suse.com>
Diffstat (limited to 'sfc/lib/openstack_utils.py')
-rw-r--r--sfc/lib/openstack_utils.py616
1 files changed, 616 insertions, 0 deletions
diff --git a/sfc/lib/openstack_utils.py b/sfc/lib/openstack_utils.py
new file mode 100644
index 0000000..2d4ecff
--- /dev/null
+++ b/sfc/lib/openstack_utils.py
@@ -0,0 +1,616 @@
+import logging
+import os
+import time
+import json
+import yaml
+import functest.utils.openstack_utils as os_utils
+from tackerclient.tacker import client as tackerclient
+from functest.utils.constants import CONST
+
+
+logger = logging.getLogger(__name__)
+DEFAULT_TACKER_API_VERSION = '1.0'
+
+
+def get_av_zones():
+ '''
+ Return the availability zone each host belongs to
+ '''
+ nova_client = os_utils.get_nova_client()
+ hosts = os_utils.get_hypervisors(nova_client)
+ return ['nova::{0}'.format(host) for host in hosts]
+
+
+def get_compute_client():
+ '''
+ Return the compute where the client sits
+ '''
+ nova_client = os_utils.get_nova_client()
+ hosts = os_utils.get_hypervisors(nova_client)
+ for compute in hosts:
+ vms = nova_client.servers.list(search_opts={'host': compute})
+ for vm in vms:
+ if "client" in vm.name:
+ return compute
+ return False
+
+
+def setup_neutron(neutron_client, net, subnet, router, subnet_cidr):
+ n_dict = os_utils.create_network_full(neutron_client,
+ net,
+ subnet,
+ router,
+ subnet_cidr)
+ if not n_dict:
+ logger.error("failed to create neutron network")
+ return False
+
+ return n_dict["net_id"]
+
+
+def create_secgroup_rule(neutron_client, sg_id, direction, protocol,
+ port_range_min=None, port_range_max=None):
+ # We create a security group in 2 steps
+ # 1 - we check the format and set the json body accordingly
+ # 2 - we call neturon client to create the security group
+
+ # Format check
+ json_body = {'security_group_rule': {'direction': direction,
+ 'security_group_id': sg_id,
+ 'protocol': protocol}}
+ # parameters may be
+ # - both None => we do nothing
+ # - both Not None => we add them to the json description
+ # but one cannot be None is the other is not None
+ if (port_range_min is not None and port_range_max is not None):
+ # add port_range in json description
+ json_body['security_group_rule']['port_range_min'] = port_range_min
+ json_body['security_group_rule']['port_range_max'] = port_range_max
+ logger.debug("Security_group format set (port range included)")
+ else:
+ # either both port range are set to None => do nothing
+ # or one is set but not the other => log it and return False
+ if port_range_min is None and port_range_max is None:
+ logger.debug("Security_group format set (no port range mentioned)")
+ else:
+ logger.error("Bad security group format."
+ "One of the port range is not properly set:"
+ "range min: {},"
+ "range max: {}".format(port_range_min,
+ port_range_max))
+ return False
+
+ # Create security group using neutron client
+ try:
+ neutron_client.create_security_group_rule(json_body)
+ return True
+ except:
+ return False
+
+
+def setup_ingress_egress_secgroup(neutron_client, protocol,
+ min_port=None, max_port=None):
+ secgroups = os_utils.get_security_groups(neutron_client)
+ for sg in secgroups:
+ # TODO: the version of the create_secgroup_rule function in
+ # functest swallows the exception thrown when a secgroup rule
+ # already exists and prints a ton of noise in the test output.
+ # Instead of making changes in functest code this late in the
+ # release cycle, we keep our own version without the exception
+ # logging. We must find a way to properly cleanup sec group
+ # rules using "functest openstack clean" or pretty printing the
+ # specific exception in the next release
+ create_secgroup_rule(neutron_client, sg['id'],
+ 'ingress', protocol,
+ port_range_min=min_port,
+ port_range_max=max_port)
+ create_secgroup_rule(neutron_client, sg['id'],
+ 'egress', protocol,
+ port_range_min=min_port,
+ port_range_max=max_port)
+
+
+def create_security_groups(neutron_client, secgroup_name, secgroup_descr):
+ sg_id = os_utils.create_security_group_full(neutron_client,
+ secgroup_name, secgroup_descr)
+ setup_ingress_egress_secgroup(neutron_client, "icmp")
+ setup_ingress_egress_secgroup(neutron_client, "tcp", 22, 22)
+ setup_ingress_egress_secgroup(neutron_client, "tcp", 80, 80)
+ setup_ingress_egress_secgroup(neutron_client, "udp", 67, 68)
+ return sg_id
+
+
+def create_instance(nova_client, name, flavor, image_id, network_id, sg_id,
+ secgroup_name=None, fixed_ip=None,
+ av_zone='', userdata=None, files=None):
+ logger.info("Creating instance '%s'..." % name)
+ logger.debug(
+ "Configuration:\n name=%s \n flavor=%s \n image=%s \n"
+ " network=%s\n secgroup=%s \n hypervisor=%s \n"
+ " fixed_ip=%s\n files=%s\n userdata=\n%s\n"
+ % (name, flavor, image_id, network_id, sg_id,
+ av_zone, fixed_ip, files, userdata))
+ instance = os_utils.create_instance_and_wait_for_active(
+ flavor,
+ image_id,
+ network_id,
+ name,
+ config_drive=True,
+ userdata=userdata,
+ av_zone=av_zone,
+ fixed_ip=fixed_ip,
+ files=files)
+
+ if instance is None:
+ logger.error("Error while booting instance.")
+ return None
+
+ if secgroup_name:
+ logger.debug("Adding '%s' to security group '%s'..."
+ % (name, secgroup_name))
+ else:
+ logger.debug("Adding '%s' to security group '%s'..."
+ % (name, sg_id))
+ os_utils.add_secgroup_to_instance(nova_client, instance.id, sg_id)
+
+ return instance
+
+
+def assign_floating_ip(nova_client, neutron_client, instance_id):
+ instance = nova_client.servers.get(instance_id)
+ floating_ip = os_utils.create_floating_ip(neutron_client)['fip_addr']
+ instance.add_floating_ip(floating_ip)
+ logger.info("Assigned floating ip [%s] to instance [%s]"
+ % (floating_ip, instance.name))
+
+ return floating_ip
+
+
+def get_nova_id(tacker_client, resource, vnf_id=None, vnf_name=None):
+ vnf = get_vnf(tacker_client, vnf_id, vnf_name)
+ try:
+ if vnf is None:
+ raise Exception("VNF not found")
+ heat = os_utils.get_heat_client()
+ resource = heat.resources.get(vnf['instance_id'], resource)
+ return resource.attributes['id']
+ except:
+ logger.error("Cannot get nova ID for VNF (id='%s', name='%s')"
+ % (vnf_id, vnf_name))
+ return None
+
+
+def get_neutron_interfaces(vm):
+ '''
+ Get the interfaces of an instance
+ '''
+ nova_client = os_utils.get_nova_client()
+ interfaces = nova_client.servers.interface_list(vm.id)
+ return interfaces
+
+
+def get_client_port_id(vm):
+ '''
+ Get the neutron port id of the client
+ '''
+ interfaces = get_neutron_interfaces(vm)
+ if len(interfaces) > 1:
+ raise Exception("Client has more than one interface. Not expected!")
+ return interfaces[0].id
+
+# TACKER SECTION #
+
+
+def get_tacker_client_version():
+ api_version = os.getenv('OS_TACKER_API_VERSION')
+ if api_version is not None:
+ logger.info("OS_TACKER_API_VERSION is set in env as '%s'", api_version)
+ return api_version
+ return DEFAULT_TACKER_API_VERSION
+
+
+def get_tacker_client(other_creds={}):
+ sess = os_utils.get_session(other_creds)
+ return tackerclient.Client(get_tacker_client_version(), session=sess)
+
+
+def get_id_from_name(tacker_client, resource_type, resource_name):
+ try:
+ req_params = {'fields': 'id', 'name': resource_name}
+ endpoint = '/{0}s'.format(resource_type)
+ resp = tacker_client.get(endpoint, params=req_params)
+ endpoint = endpoint.replace('-', '_')
+ return resp[endpoint[1:]][0]['id']
+ except Exception, e:
+ logger.error("Error [get_id_from_name(tacker_client, "
+ "resource_type, resource_name)]: %s" % e)
+ return None
+
+
+def get_vnfd_id(tacker_client, vnfd_name):
+ return get_id_from_name(tacker_client, 'vnfd', vnfd_name)
+
+
+def get_vim_id(tacker_client, vim_name):
+ return get_id_from_name(tacker_client, 'vim', vim_name)
+
+
+def get_vnf_id(tacker_client, vnf_name, timeout=5):
+ vnf_id = None
+ while vnf_id is None and timeout >= 0:
+ vnf_id = get_id_from_name(tacker_client, 'vnf', vnf_name)
+ if vnf_id is None:
+ logger.info("Could not retrieve ID for vnf with name [%s]."
+ " Retrying." % vnf_name)
+ time.sleep(1)
+ timeout -= 1
+ return vnf_id
+
+
+def get_vnffg_id(tacker_client, vnffg_name, timeout=5):
+ vnffg_id = None
+ while vnffg_id is None and timeout >= 0:
+ vnffg_id = get_id_from_name(tacker_client, 'vnffg', vnffg_name)
+ if vnffg_id is None:
+ logger.info("Could not retrieve ID for vnffg with name [%s]."
+ " Retrying." % vnffg_name)
+ time.sleep(1)
+ timeout -= 1
+ return vnffg_id
+
+
+def get_vnffgd_id(tacker_client, vnffgd_name):
+ return get_id_from_name(tacker_client, 'vnffgd', vnffgd_name)
+
+
+def list_vnfds(tacker_client, verbose=False):
+ try:
+ vnfds = tacker_client.list_vnfds(retrieve_all=True)
+ if not verbose:
+ vnfds = [vnfd['id'] for vnfd in vnfds['vnfds']]
+ return vnfds
+ except Exception, e:
+ logger.error("Error [list_vnfds(tacker_client)]: %s" % e)
+ return None
+
+
+def create_vnfd(tacker_client, tosca_file=None, vnfd_name=None):
+ try:
+ vnfd_body = {}
+ if tosca_file is not None:
+ with open(tosca_file) as tosca_fd:
+ vnfd_body = tosca_fd.read()
+ logger.info('VNFD template:\n{0}'.format(vnfd_body))
+ return tacker_client.create_vnfd(
+ body={"vnfd": {"attributes": {"vnfd": vnfd_body},
+ "name": vnfd_name}})
+ except Exception, e:
+ logger.error("Error [create_vnfd(tacker_client, '%s')]: %s"
+ % (tosca_file, e))
+ return None
+
+
+def delete_vnfd(tacker_client, vnfd_id=None, vnfd_name=None):
+ try:
+ vnfd = vnfd_id
+ if vnfd is None:
+ if vnfd_name is None:
+ raise Exception('You need to provide VNFD id or VNFD name')
+ vnfd = get_vnfd_id(tacker_client, vnfd_name)
+ return tacker_client.delete_vnfd(vnfd)
+ except Exception, e:
+ logger.error("Error [delete_vnfd(tacker_client, '%s', '%s')]: %s"
+ % (vnfd_id, vnfd_name, e))
+ return None
+
+
+def list_vnfs(tacker_client, verbose=False):
+ try:
+ vnfs = tacker_client.list_vnfs(retrieve_all=True)
+ if not verbose:
+ vnfs = [vnf['id'] for vnf in vnfs['vnfs']]
+ return vnfs
+ except Exception, e:
+ logger.error("Error [list_vnfs(tacker_client)]: %s" % e)
+ return None
+
+
+def create_vnf(tacker_client, vnf_name, vnfd_id=None,
+ vnfd_name=None, vim_id=None, vim_name=None, param_file=None):
+ try:
+ vnf_body = {
+ 'vnf': {
+ 'attributes': {},
+ 'name': vnf_name
+ }
+ }
+ if param_file is not None:
+ params = None
+ with open(param_file) as f:
+ params = f.read()
+ vnf_body['vnf']['attributes']['param_values'] = params
+
+ if vnfd_id is not None:
+ vnf_body['vnf']['vnfd_id'] = vnfd_id
+ else:
+ if vnfd_name is None:
+ raise Exception('vnfd id or vnfd name is required')
+ vnf_body['vnf']['vnfd_id'] = get_vnfd_id(tacker_client, vnfd_name)
+
+ if vim_id is not None:
+ vnf_body['vnf']['vim_id'] = vim_id
+ else:
+ if vim_name is None:
+ raise Exception('vim id or vim name is required')
+ vnf_body['vnf']['vim_id'] = get_vim_id(tacker_client, vim_name)
+ return tacker_client.create_vnf(body=vnf_body)
+
+ except Exception, e:
+ logger.error("error [create_vnf(tacker_client,"
+ " '%s', '%s', '%s')]: %s"
+ % (vnf_name, vnfd_id, vnfd_name, e))
+ return None
+
+
+def get_vnf(tacker_client, vnf_id=None, vnf_name=None):
+ try:
+ if vnf_id is None and vnf_name is None:
+ raise Exception('You must specify vnf_id or vnf_name')
+
+ _id = get_vnf_id(tacker_client, vnf_name) if vnf_id is None else vnf_id
+
+ if _id is not None:
+ all_vnfs = list_vnfs(tacker_client, verbose=True)['vnfs']
+ return next((vnf for vnf in all_vnfs if vnf['id'] == _id), None)
+ else:
+ raise Exception('Could not retrieve ID from name [%s]' % vnf_name)
+
+ except Exception, e:
+ logger.error("Could not retrieve VNF [vnf_id=%s, vnf_name=%s] - %s"
+ % (vnf_id, vnf_name, e))
+ return None
+
+
+def wait_for_vnf(tacker_client, vnf_id=None, vnf_name=None, timeout=100):
+ try:
+ vnf = get_vnf(tacker_client, vnf_id, vnf_name)
+ if vnf is None:
+ raise Exception("Could not retrieve VNF - id='%s', name='%s'"
+ % vnf_id, vnf_name)
+ logger.info('Waiting for vnf {0}'.format(str(vnf)))
+ while vnf['status'] != 'ACTIVE' and timeout >= 0:
+ if vnf['status'] == 'ERROR':
+ raise Exception('Error when booting vnf %s' % vnf['id'])
+ elif vnf['status'] == 'PENDING_CREATE':
+ time.sleep(3)
+ timeout -= 3
+ vnf = get_vnf(tacker_client, vnf_id, vnf_name)
+
+ if (timeout < 0):
+ raise Exception('Timeout when booting vnf %s' % vnf['id'])
+
+ return vnf['id']
+ except Exception, e:
+ logger.error("error [wait_for_vnf(tacker_client, '%s', '%s')]: %s"
+ % (vnf_id, vnf_name, e))
+ return None
+
+
+def delete_vnf(tacker_client, vnf_id=None, vnf_name=None):
+ try:
+ vnf = vnf_id
+ if vnf is None:
+ if vnf_name is None:
+ raise Exception('You need to provide a VNF id or name')
+ vnf = get_vnf_id(tacker_client, vnf_name)
+ return tacker_client.delete_vnf(vnf)
+ except Exception, e:
+ logger.error("Error [delete_vnf(tacker_client, '%s', '%s')]: %s"
+ % (vnf_id, vnf_name, e))
+ return None
+
+
+def create_vim(tacker_client, vim_file=None):
+ try:
+ vim_body = {}
+ if vim_file is not None:
+ with open(vim_file) as vim_fd:
+ vim_body = json.load(vim_fd)
+ logger.info('VIM template:\n{0}'.format(vim_body))
+ return tacker_client.create_vim(body=vim_body)
+ except Exception, e:
+ logger.error("Error [create_vim(tacker_client, '%s')]: %s"
+ % (vim_file, e))
+ return None
+
+
+def create_vnffgd(tacker_client, tosca_file=None, vnffgd_name=None):
+ try:
+ vnffgd_body = {}
+ if tosca_file is not None:
+ with open(tosca_file) as tosca_fd:
+ vnffgd_body = yaml.safe_load(tosca_fd)
+ logger.info('VNFFGD template:\n{0}'.format(vnffgd_body))
+ return tacker_client.create_vnffgd(
+ body={'vnffgd': {'name': vnffgd_name,
+ 'template': {'vnffgd': vnffgd_body}}})
+ except Exception, e:
+ logger.error("Error [create_vnffgd(tacker_client, '%s')]: %s"
+ % (tosca_file, e))
+ return None
+
+
+def create_vnffg(tacker_client, vnffg_name=None, vnffgd_id=None,
+ vnffgd_name=None, param_file=None):
+ '''
+ Creates the vnffg which will provide the RSP and the classifier
+ '''
+ try:
+ vnffg_body = {
+ 'vnffg': {
+ 'attributes': {},
+ 'name': vnffg_name
+ }
+ }
+ if param_file is not None:
+ params = None
+ with open(param_file) as f:
+ params = f.read()
+ params_dict = yaml.safe_load(params)
+ vnffg_body['vnffg']['attributes']['param_values'] = params_dict
+ if vnffgd_id is not None:
+ vnffg_body['vnffg']['vnffgd_id'] = vnffgd_id
+ else:
+ if vnffgd_name is None:
+ raise Exception('vnffgd id or vnffgd name is required')
+ vnffg_body['vnffg']['vnffgd_id'] = get_vnffgd_id(tacker_client,
+ vnffgd_name)
+ return tacker_client.create_vnffg(body=vnffg_body)
+ except Exception, e:
+ logger.error("error [create_vnffg(tacker_client,"
+ " '%s', '%s', '%s')]: %s"
+ % (vnffg_name, vnffgd_id, vnffgd_name, e))
+ return None
+
+
+def list_vnffgds(tacker_client, verbose=False):
+ try:
+ vnffgds = tacker_client.list_vnffgds(retrieve_all=True)
+ if not verbose:
+ vnffgds = [vnffgd['id'] for vnffgd in vnffgds['vnffgds']]
+ return vnffgds
+ except Exception, e:
+ logger.error("Error [list_vnffgds(tacker_client)]: %s" % e)
+ return None
+
+
+def list_vnffgs(tacker_client, verbose=False):
+ try:
+ vnffgs = tacker_client.list_vnffgs(retrieve_all=True)
+ if not verbose:
+ vnffgs = [vnffg['id'] for vnffg in vnffgs['vnffgs']]
+ return vnffgs
+ except Exception, e:
+ logger.error("Error [list_vnffgs(tacker_client)]: %s" % e)
+ return None
+
+
+def delete_vnffg(tacker_client, vnffg_id=None, vnffg_name=None):
+ try:
+ vnffg = vnffg_id
+ if vnffg is None:
+ if vnffg_name is None:
+ raise Exception('You need to provide a VNFFG id or name')
+ vnffg = get_vnffg_id(tacker_client, vnffg_name)
+ return tacker_client.delete_vnffg(vnffg)
+ except Exception, e:
+ logger.error("Error [delete_vnffg(tacker_client, '%s', '%s')]: %s"
+ % (vnffg_id, vnffg_name, e))
+ return None
+
+
+def delete_vnffgd(tacker_client, vnffgd_id=None, vnffgd_name=None):
+ try:
+ vnffgd = vnffgd_id
+ if vnffgd is None:
+ if vnffgd_name is None:
+ raise Exception('You need to provide VNFFGD id or VNFFGD name')
+ vnffgd = get_vnffgd_id(tacker_client, vnffgd_name)
+ return tacker_client.delete_vnffgd(vnffgd)
+ except Exception, e:
+ logger.error("Error [delete_vnffgd(tacker_client, '%s', '%s')]: %s"
+ % (vnffgd_id, vnffgd_name, e))
+ return None
+
+
+def list_vims(tacker_client, verbose=False):
+ try:
+ vims = tacker_client.list_vims(retrieve_all=True)
+ if not verbose:
+ vims = [vim['id'] for vim in vims['vims']]
+ return vims
+ except Exception, e:
+ logger.error("Error [list_vims(tacker_client)]: %s" % e)
+ return None
+
+
+def delete_vim(tacker_client, vim_id=None, vim_name=None):
+ try:
+ vim = vim_id
+ if vim is None:
+ if vim_name is None:
+ raise Exception('You need to provide VIM id or VIM name')
+ vim = get_vim_id(tacker_client, vim_name)
+ return tacker_client.delete_vim(vim)
+ except Exception, e:
+ logger.error("Error [delete_vim(tacker_client, '%s', '%s')]: %s"
+ % (vim_id, vim_name, e))
+ return None
+
+
+def get_tacker_items():
+ tacker_client = get_tacker_client()
+ logger.debug("VIMs: %s" % list_vims(tacker_client))
+ logger.debug("VNFDs: %s" % list_vnfds(tacker_client))
+ logger.debug("VNFs: %s" % list_vnfs(tacker_client))
+ logger.debug("VNFFGDs: %s" % list_vnffgds(tacker_client))
+ logger.debug("VNFFGs: %s" % list_vnffgs(tacker_client))
+
+
+def register_vim(tacker_client, vim_file=None):
+ tmp_file = '/tmp/register-vim.json'
+ if vim_file is not None:
+ with open(vim_file) as f:
+ json_dict = json.load(f)
+
+ json_dict['vim']['auth_url'] = CONST.__getattribute__('OS_AUTH_URL')
+ json_dict['vim']['auth_cred']['password'] = CONST.__getattribute__(
+ 'OS_PASSWORD')
+
+ json.dump(json_dict, open(tmp_file, 'w'))
+
+ create_vim(tacker_client, vim_file=tmp_file)
+
+
+def create_vnf_in_av_zone(
+ tacker_client,
+ vnf_name,
+ vnfd_name,
+ vim_name,
+ default_param_file,
+ av_zone=None):
+ param_file = default_param_file
+
+ if av_zone is not None or av_zone != 'nova':
+ param_file = os.path.join(
+ '/tmp',
+ 'param_{0}.json'.format(av_zone.replace('::', '_')))
+ data = {
+ 'zone': av_zone
+ }
+ with open(param_file, 'w+') as f:
+ json.dump(data, f)
+ create_vnf(tacker_client,
+ vnf_name,
+ vnfd_name=vnfd_name,
+ vim_name=vim_name,
+ param_file=param_file)
+
+
+def create_vnffg_with_param_file(tacker_client, vnffgd_name, vnffg_name,
+ default_param_file, neutron_port):
+ param_file = default_param_file
+
+ if neutron_port is not None:
+ param_file = os.path.join(
+ '/tmp',
+ 'param_{0}.json'.format(neutron_port))
+ data = {
+ 'net_src_port_id': neutron_port
+ }
+ with open(param_file, 'w+') as f:
+ json.dump(data, f)
+ create_vnffg(tacker_client,
+ vnffgd_name=vnffgd_name,
+ vnffg_name=vnffg_name,
+ param_file=param_file)