Пример #1
0
class OSClient(object):
    def __init__(self, testbed_name, testbed, tenant_name=None, project_id=None):
        self.testbed_name = testbed_name
        self.tenant_name = None
        self.project_id = None
        self.testbed = testbed
        self.project_domain_name = self.testbed.get('project_domain_name') or 'Default'
        self.user_domain_name = self.testbed.get('user_domain_name') or 'Default'
        self.api_version = self.testbed.get('api_version')
        self.username = self.testbed.get('username')
        self.password = self.testbed.get('password')
        self.auth_url = self.testbed.get("auth_url")
        if self.auth_url.endswith('/'):
            self.auth_url = self.auth_url[:-1]
        self.admin_tenant_name = self.testbed.get("admin_tenant_name")
        self.admin_project_id = self.testbed.get("admin_project_id")
        if not self.admin_tenant_name and not self.admin_project_id:
            raise OpenstackClientError("Missing both admin project id and admin tenant name")
        if self.api_version == 2 and not self.admin_tenant_name:
            raise OpenstackClientError("Missing tenant name required if using v2")
        if self.api_version == 3 and not self.admin_project_id:
            raise OpenstackClientError("Missing project id required if using v3")

        self.neutron = None
        self.nova = None
        self.glance = None
        self.keypair = None
        self.sec_group = None
        self.os_tenant_id = None

        # logger.debug("Log level is: %s and DEBUG is %s" % (logger.getEffectiveLevel(), logging.DEBUG))
        # if logger.getEffectiveLevel() == logging.DEBUG:
        #     logging.basicConfig(level=logging.DEBUG)

        if not tenant_name and not project_id:

            self.keystone = self._create_keystone_client()
            logger.debug("Created Keystone client %s" % self.keystone)
        else:
            self.tenant_name = tenant_name
            self.project_id = project_id

            if self.api_version == 2 and not self.tenant_name:
                raise OpenstackClientError("Missing tenant name required if using v2")
            if self.api_version == 3 and not self.project_id:
                raise OpenstackClientError("Missing project id required if using v3")

            logger.debug("Creating keystone client")
            if self.api_version == 3:
                self.keystone = self._create_keystone_client(project_id)
                self.os_tenant_id = project_id
            else:
                self.keystone = self._create_keystone_client(tenant_name)
                self.os_tenant_id = self.project_id = self._get_tenant_id_from_name(tenant_name)

            logger.debug("Created Keystone client %s" % self.keystone)
            self.set_nova(self.os_tenant_id)
            self.set_neutron(self.os_tenant_id)
            self.set_glance(self.os_tenant_id)

    def _create_keystone_client(self, project_id=None):
        if self.api_version == 3:
            return keystoneclient.v3.client.Client(session=self._get_session(project_id))
        elif self.api_version == 2:
            if not project_id:
                project_id = self.tenant_name or self.admin_tenant_name
            return keystoneclient.v2_0.client.Client(username=self.username,
                                                     password=self.password,
                                                     tenant_name=project_id,
                                                     auth_url=self.auth_url)

    def set_nova(self, os_tenant_id):
        self.nova = Nova('2.1', session=self._get_session(os_tenant_id))

    def _get_session(self, tenant_id=None):
        if self.api_version == 2:
            tenant_name = self.tenant_name or self.admin_tenant_name
            auth = v2.Password(auth_url=self.auth_url,
                               username=self.username,
                               password=self.password,
                               tenant_name=tenant_name)
        elif self.api_version == 3:
            p_id = tenant_id or self.project_id or self.admin_project_id
            auth = v3.Password(auth_url=self.auth_url,
                               username=self.username,
                               password=self.password,
                               project_id=p_id,
                               project_domain_name=self.project_domain_name,
                               user_domain_name=self.user_domain_name)
        else:
            msg = "Wrong api version: %s" % self.api_version
            logger.error(msg)
            raise OpenstackClientError(msg)
        return session.Session(auth=auth)

    def set_neutron(self, os_tenant_id):
        # self.os_tenant_id = os_tenant_id
        if not self.neutron:
            self.neutron = Neutron(session=self._get_session(os_tenant_id))

    def get_user(self, username=None):
        users = self.list_users()
        if username:
            un = username
        else:
            un = self.username
        for user in users:
            if user.name == un:
                return user

    def get_role(self, role_to_find):
        roles = self.list_roles()
        for role in roles:
            if role.name == role_to_find:
                return role

    def list_roles(self):
        return self.keystone.roles.list()

    def list_tenants(self):
        if self.api_version == 3:
            return self.keystone.projects.list()
        else:
            return self.keystone.tenants.list()

    def create_tenant(self, tenant_name, description):
        self.tenant_name = tenant_name
        if self.api_version == 2:
            return self.keystone.tenants.create(tenant_name=tenant_name, description=description)
        else:
            return self.keystone.projects.create(name=tenant_name, description=description,
                                                 domain=self.user_domain_name.lower())

    def add_user_role(self, user, role, tenant):
        if self.api_version == 2:
            try:
                return self.keystone.roles.add_user_role(user=user, role=role, tenant=tenant)
            except Conflict as c:
                if c.http_status == 409:  # role already assigned to user
                    return
                raise c
        else:
            return self.keystone.roles.grant(user=user, role=role, project=tenant)

    def import_keypair(self, key_file, os_tenant_id=None):
        if not self.nova and not os_tenant_id:
            raise OpenstackClientError("Both os_tenant_id and nova obj are None")
        if not self.nova:
            self.set_nova(os_tenant_id=os_tenant_id)
        keypair_name = "softfire-key"
        self.keypair = keypair_name
        for keypair in self.list_keypairs(os_tenant_id):
            if keypair.name == keypair_name:
                return keypair
        if os.path.isfile(key_file):
            with open(key_file, "r") as sosftfire_ssh_pub_key:
                kargs = {"name": keypair_name,
                         "public_key": sosftfire_ssh_pub_key.read()}
                return self.nova.keypairs.create(**kargs)
        else:
            kargs = {"name": keypair_name,
                     "public_key": key_file}
            return self.nova.keypairs.create(**kargs)

    def get_ext_net(self, ext_net_name='softfire-network'):
        return [ext_net for ext_net in self.neutron.list_networks()['networks'] if
                ext_net['router:external'] and ext_net['name'] == ext_net_name][0]

    def allocate_floating_ips(self, ext_net, fip_num=0):
        body = {
            "floatingip": {
                "floating_network_id": ext_net['id']
            }
        }
        for i in range(fip_num):
            try:
                self.neutron.create_floatingip(body=body)
            except IpAddressGenerationFailureClient as e:
                logger.error("Not able to allocate floatingips :(")
                raise OpenstackClientError("Not able to allocate floatingips :(")

    def create_networks_and_subnets(self, ext_net, router_name='ob_router'):
        networks = []
        subnets = []
        ports = []
        router_id = None
        exist_net = [network for network in self.neutron.list_networks()['networks']]
        exist_net_names = [network['name'] for network in exist_net]
        net_name_to_create = [net for net in NETWORKS if net not in exist_net_names]
        networks.extend(network for network in exist_net if network['name'] in NETWORKS)
        index = 1
        for net in net_name_to_create:
            kwargs = {'network': {
                'name': net,
                'shared': False,
                'admin_state_up': True
            }}
            logger.debug("Creating net %s" % net)
            network_ = self.neutron.create_network(body=kwargs)['network']
            networks.append(network_)
            kwargs = {
                'subnets': [
                    {
                        'name': "subnet_%s" % net,
                        'cidr': "192.%s.%s.0/24" % ((get_username_hash(self.username) % 254) + 1, index),
                        'gateway_ip': '192.%s.%s.1' % ((get_username_hash(self.username) % 254) + 1, index),
                        'ip_version': '4',
                        'enable_dhcp': True,
                        'dns_nameservers': ['8.8.8.8'],
                        'network_id': network_['id']
                    }
                ]
            }
            logger.debug("Creating subnet subnet_%s" % net)
            subnet = self.neutron.create_subnet(body=kwargs)
            subnets.append(subnet)

            router = self.get_router_from_name(router_name, ext_net)
            router_id = router['router']['id']

            body_value = {
                'subnet_id': subnet['subnets'][0]['id'],
            }
            try:
                ports.append(self.neutron.add_interface_router(router=router_id, body=body_value))
            except Exception as e:
                pass
            index += 1

        return networks, subnets, router_id

    def get_router_from_name(self, router_name, ext_net):
        for router in self.neutron.list_routers()['routers']:
            if router['name'] == router_name:
                return self.neutron.show_router(router['id'])
        request = {'router': {'name': router_name, 'admin_state_up': True}}
        router = self.neutron.create_router(request)
        body_value = {"network_id": ext_net['id']}
        self.neutron.add_gateway_router(router=router['router']['id'], body=body_value)
        return router

    def create_rule(self, sec_group, protocol):
        body = {"security_group_rule": {
            "direction": "ingress",
            "port_range_min": "1",
            "port_range_max": "65535",
            # "name": sec_group['security_group']['name'],
            "security_group_id": sec_group['security_group']['id'],
            "remote_ip_prefix": "0.0.0.0/0",
            "protocol": protocol,
        }}
        if protocol == 'icmp':
            body['security_group_rule'].pop('port_range_min', None)
            body['security_group_rule'].pop('port_range_max', None)
        try:
            self.neutron.create_security_group_rule(body=body)
        except neutronclient.common.exceptions.Conflict as e:
            logger.error("error while creating a rule: %s" % e.message)
            pass

    def create_security_group(self, project_id, sec_g_name=None):
        if not sec_g_name:
            sec_g_name = sec_group_name
        sec_group = {}
        for sg in self.list_sec_group(project_id):
            if sg['name'] == sec_g_name:
                sec_group['security_group'] = sg
                break
        if len(sec_group) == 0:
            body = dict(security_group=dict(name=sec_g_name, description="openbaton security group"),
                        project_id=project_id, tenant_id=project_id)
            sec_group = self.neutron.create_security_group(body=body)
            self.create_rule(sec_group, 'tcp')
            self.create_rule(sec_group, 'udp')
            self.create_rule(sec_group, 'icmp')
        self.sec_group = sec_group['security_group']
        return self.sec_group


    def list_sec_group(self, os_project_id):
        if not self.neutron:
            self.set_neutron(os_project_id)
        return [sec for sec in self.neutron.list_security_groups()['security_groups'] if
                (sec.get('tenant_id') is not None and sec.get('tenant_id') == os_project_id) or (
                        sec.get('project_id') is not None and sec.get('project_id') == os_project_id)]

    def get_vim_instance(self, tenant_name, username=None, password=None):
        if username:
            un = username
        else:
            un = self.username
        if password:
            pwd = password
        else:
            pwd = self.password

        logger.debug("Using tenant id: %s " % tenant_name)

        return {
            "name": "vim-instance-%s" % self.testbed_name,
            "authUrl": self.auth_url,
            "tenant": tenant_name,
            "username": un,
            "password": pwd,
            "securityGroups": [
                'default', sec_group_name
            ],
            "type": "openstack",
            "location": {
                "name": "Berlin",
                "latitude": "52.525876",
                "longitude": "13.314400"
            }
        }

    def upload_image(self, name, path, container_format="bare", disk_format="qcow2", visibility="public"):
        # image = self.glance.images.create(name=name)
        # self.glance.images.upload(image.id, open(path, 'rb'))

        with open(path, 'rb') as fimage:
            img = self.glance.images.create(name=name,
                                            # is_public="True",
                                            # public="True",
                                            visibility=visibility,
                                            disk_format=disk_format,
                                            container_format=container_format,
                                            data=path)
            # print(dir(self.glance.images))
            self.glance.images.upload(img.id, fimage)

    def list_images(self, tenant_id=None):
        if not self.nova:
            if not tenant_id:
                logger.error("Missing tenant_id!")
                raise OpenstackClientError('Missing tenant_id!')
            self.set_nova(tenant_id)
        try:
            imgs = self.nova.images.list()
            return imgs
        except:
            self.set_glance(tenant_id)
            return self.glance.images.list()

    def _get_tenant_id_from_name(self, tenant_name):
        if self.api_version == 2:
            tenants_list = self.keystone.tenants.list()
        else:
            tenants_list = self.keystone.projects.list()
        for tenant in tenants_list:
            if tenant.name == tenant_name:
                return tenant.id

    def set_glance(self, os_tenant_id):
        self.os_tenant_id = os_tenant_id
        self.glance = Glance('2', session=self._get_session(os_tenant_id))

    def _get_tenant_name_from_id(self, os_tenant_id):
        for t in self.list_tenants():
            if t.id == os_tenant_id:
                return t.name

    def create_user(self, username, password=None, tenant_id=None):
        for u in self.list_users():
            if hasattr(u, 'username'):
                u_username = u.username
            else:
                u_username = u.name
            if u_username == username:
                return u
        if not password:
            raise OpenstackClientError("Paswsord is needed to create user")
        if self.api_version == 2:
            return self.keystone.users.create(username, password, tenant_id=tenant_id)
        else:
            return self.keystone.users.create(name=username, password=password,
                                              project=self.get_project_from_id(tenant_id))

    def list_users(self):
        return self.keystone.users.list()

    def list_networks(self, project_id=None):
        if not self.neutron:
            if not project_id:
                raise OpenstackClientError("Missing project_id!")
            self.set_neutron(project_id)
        return self.neutron.list_networks(tenant_id=project_id)

    def list_subnets(self, project_id):
        if not self.neutron:
            if not project_id:
                raise OpenstackClientError("Missing project_id!")
            self.set_neutron(project_id)
        return self.neutron.list_subnets(tenant_id=project_id)

    def list_floatingips(self, project_id):
        if not self.neutron:
            if not project_id:
                raise OpenstackClientError("Missing project_id!")
            self.set_neutron(project_id)
        return self.neutron.list_floatingips(tenant_id=project_id)

    def list_routers(self, project_id):
        if not self.neutron:
            if not project_id:
                raise OpenstackClientError("Missing project_id!")
            self.set_neutron(project_id)
        return self.neutron.list_routers(tenant_id=project_id)

    def list_ports(self, project_id):
        if not self.neutron:
            if not project_id:
                raise OpenstackClientError("Missing project_id!")
            self.set_neutron(project_id)
        return self.neutron.list_ports(tenant_id=project_id)

    def list_keypairs(self, os_project_id=None):
        if not self.nova:
            if not os_project_id:
                raise OpenstackClientError("Missing project_id!")
            self.set_nova(os_project_id)
        return self.nova.keypairs.list()

    def list_domains(self):
        return self.keystone.domains.list()

    def get_project_from_id(self, tenant_id):
        for p in self.list_tenants():
            if p.id == tenant_id:
                return p
        raise OpenstackClientError("Project with id %s not found")

    def delete_user(self, username):
        try:
            self.keystone.users.delete(self.create_user(username=username))
        except:
            traceback.print_exc()
            logger.error("Not Able to delete user %s" % username)

    def delete_project(self, project_id):
        try:
            if self.api_version == 2:
                self.keystone.tenants.delete(project_id)
            else:
                self.keystone.projects.delete(project_id)
        except:
            traceback.print_exc()
            logger.error("Not Able to delete project %s" % project_id)

    def release_floating_ips(self, project_id, keep_fip_id_list=list()):
        fips = self.list_floatingips(project_id).get('floatingips')
        for fip in fips:
            if fip.get('id') in keep_fip_id_list:
                logger.debug("Not relasing floating ip: %s" % fip)
            else:
                self.neutron.delete_floatingip(fip.get('id'))

    def delete_ports(self, project_id):
        ports = self.list_ports(project_id).get('ports')
        for port in ports:
            try:
                self.neutron.delete_port(port.get('id'))
            except Exception as e:
                pass

    def remove_gateway_routers(self, project_id):
        routers = self.list_routers(project_id).get('routers')
        for router in routers:
            self.neutron.remove_gateway_router(router.get('id'))

    def remove_interface_routers(self, project_id):
        routers = self.list_routers(project_id).get('routers')
        subnets = self.list_subnets(project_id).get('subnets')
        for router in routers:
            for subnet in subnets:
                body_value = {
                    'subnet_id': subnet.get('id'),
                }
                try:
                    self.neutron.remove_interface_router(router.get('id'), body_value)
                    break
                except Exception as e:
                    pass
            else:
                logger.warning('No subnet found that is associated to router {}'.format(router.get('id')))

    def delete_routers(self, project_id):
        routers = self.list_routers(project_id).get('routers')
        for router in routers:
            self.neutron.delete_router(router.get('id'))

    def delete_networks(self, project_id):
        networks = self.list_networks(project_id).get('networks')
        for nw in networks:
            self.neutron.delete_network(nw.get('id'))

    def delete_security_groups(self, project_id):
        sec_groups = self.list_sec_group(project_id)
        for sec_group in sec_groups:
            self.neutron.delete_security_group(sec_group.get('id'))