diff options
author | Manuel Buil <mbuil@suse.com> | 2017-12-12 12:15:00 +0100 |
---|---|---|
committer | Manuel Buil <mbuil@suse.com> | 2017-12-12 15:40:50 +0100 |
commit | d4757d239155cbd21ce45b656469c10d67cdf569 (patch) | |
tree | 5192ead08005f563cfe7d85147ef176d1939d9e7 /sfc/lib/openstack_utils.py | |
parent | 8814891148390d427c22e583b2d7451dbd600197 (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.py | 616 |
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 00000000..2d4ecff3 --- /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) |