[Openstack]client api源码学习

来源:互联网 发布:我国教育经费机制知 编辑:程序博客网 时间:2024/04/26 11:31

       Openstack提供了一个rest形式的web api接口供外部用户调用,为了方便对他的使用,openstack提供了一个可以被python直接调用的封装过的官方client api(如novaclient,glanceclient),在openstack的项目中,一些跨项目的服务的调用就是使用client api,在安装openstack时这些api必须要被安装的。

      各个client可能因为开发的人员不同实现起来是有差异的,这里就以比较有代表性的novaclient为例进行学习。client api基本的调用方法如下:

   """    创建一个client 实例:        >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL)    调用其中managers中的方法        >>> client.servers.list()        ...        >>> client.flavors.list()        ...    """

       Client类实际继承于HTTPClient类,HTTPClient类实现通过keystone的验证功能,实现对应于特定endpoint 的含有验证信息的get,post,patch,head,delete, rest方式的方法,方便对于api的访问。

  # FIXME(jesse): project_id isn't required to authenticate    def __init__(self, username, api_key, project_id, auth_url=None,                  insecure=False, timeout=None, proxy_tenant_id=None,                  proxy_token=None, region_name=None,                  endpoint_type='publicURL', extensions=None,                  service_type='compute', service_name=None,                  volume_service_name=None, timings=False,                  bypass_url=None, os_cache=False, no_cache=True,                  http_log_debug=False, auth_system='keystone',                  auth_plugin=None,                  cacert=None):        # FIXME(comstud): Rename the api_key argument above when we        # know it's not being used as keyword argument        password = api_key        self.project_id = project_id        self.flavors = flavors.FlavorManager(self)        self.flavor_access = flavor_access.FlavorAccessManager(self)        self.images = images.ImageManager(self)        self.limits = limits.LimitsManager(self)        self.servers = servers.ServerManager(self)        # extensions        self.agents = agents.AgentsManager(self)        self.dns_domains = floating_ip_dns.FloatingIPDNSDomainManager(self)        self.dns_entries = floating_ip_dns.FloatingIPDNSEntryManager(self)        self.cloudpipe = cloudpipe.CloudpipeManager(self)        self.certs = certs.CertificateManager(self)        self.floating_ips = floating_ips.FloatingIPManager(self)        self.floating_ip_pools = floating_ip_pools.FloatingIPPoolManager(self)        self.fping = fping.FpingManager(self)        self.volumes = volumes.VolumeManager(self)        self.volume_snapshots = volume_snapshots.SnapshotManager(self)        self.volume_types = volume_types.VolumeTypeManager(self)        self.keypairs = keypairs.KeypairManager(self)        self.networks = networks.NetworkManager(self)        self.quota_classes = quota_classes.QuotaClassSetManager(self)        self.quotas = quotas.QuotaSetManager(self)        self.security_groups = security_groups.SecurityGroupManager(self)        self.security_group_rules = \            security_group_rules.SecurityGroupRuleManager(self)        self.usage = usage.UsageManager(self)        self.virtual_interfaces = \            virtual_interfaces.VirtualInterfaceManager(self)        self.aggregates = aggregates.AggregateManager(self)        self.hosts = hosts.HostManager(self)        self.hypervisors = hypervisors.HypervisorManager(self)        self.services = services.ServiceManager(self)        self.fixed_ips = fixed_ips.FixedIPsManager(self)        self.floating_ips_bulk = floating_ips_bulk.FloatingIPBulkManager(self)        self.os_cache = os_cache or not no_cache        self.coverage = coverage_ext.CoverageManager(self)        self.availability_zones = \            availability_zones.AvailabilityZoneManager(self)        # Add in any extensions...        if extensions:            for extension in extensions:                if extension.manager_class:                    setattr(self, extension.name,                            extension.manager_class(self))        self.client = client.HTTPClient(username,                                    password,                                    project_id,                                    auth_url,                                    insecure=insecure,                                    timeout=timeout,                                    auth_system=auth_system,                                    auth_plugin=auth_plugin,                                    proxy_token=proxy_token,                                    proxy_tenant_id=proxy_tenant_id,                                    region_name=region_name,                                    endpoint_type=endpoint_type,                                    service_type=service_type,                                    service_name=service_name,                                    volume_service_name=volume_service_name,                                    timings=timings,                                    bypass_url=bypass_url,                                    os_cache=self.os_cache,                                    http_log_debug=http_log_debug,                                    cacert=cacert)

  在其init 函数中注册各功能相关的Manager,这些Manager实际完成了对api的调用过程。这些Manager实际继承于base.py中的Manager类,该类中包含_list,_get,_head,_create,_delete,_update等基本方法,该类中有一个很重要的类属性resource_class,
class Flavor(base.Resource):    """    A flavor is an available hardware configuration for a server.    """    HUMAN_ID = True    def __repr__(self):        return "<Flavor: %s>" % self.name...........
class FlavorManager(base.ManagerWithFind):    """    Manage :class:`Flavor` resources.    """    resource_class = Flavor    is_alphanum_id_allowed = True    def list(self, detailed=True, is_public=True):        """        Get a list of all flavors.        :rtype: list of :class:`Flavor`.        """        qparams = {}        # is_public is ternary - None means give all flavors.        # By default Nova assumes True and gives admins public flavors        # and flavors from their own projects only.        if not is_public:            qparams['is_public'] = is_public        query_string = "?%s" % urllib.urlencode(qparams) if qparams else ""        detail = ""        if detailed:            detail = "/detail"        return self._list("/flavors%s%s" % (detail, query_string), "flavors")    def get(self, flavor):        """        Get a specific flavor.        :param flavor: The ID of the :class:`Flavor` to get.        :rtype: :class:`Flavor`        """        return self._get("/flavors/%s" % base.getid(flavor), "flavor")    def delete(self, flavor):        """        Delete a specific flavor.        :param flavor: The ID of the :class:`Flavor` to get.        :param purge: Whether to purge record from the database        """        self._delete("/flavors/%s" % base.getid(flavor))    def create(self, name, ram, vcpus, disk, flavorid=None,               ephemeral=0, swap=0, rxtx_factor=1.0, is_public=True):        """        Create (allocate) a  floating ip for a tenant        :param name: Descriptive name of the flavor        :param ram: Memory in MB for the flavor        :param vcpu: Number of VCPUs for the flavor        :param disk: Size of local disk in GB        :param flavorid: ID for the flavor (optional). You can use the reserved                         value ``"auto"`` to have Nova generate a UUID for the                         flavor in cases where you cannot simply pass ``None``.        :param swap: Swap space in MB        :param rxtx_factor: RX/TX factor        :rtype: :class:`Flavor`        """        try:            ram = int(ram)        except (TypeError, ValueError):            raise exceptions.CommandError("Ram must be an integer.")        try:            vcpus = int(vcpus)        except (TypeError, ValueError):            raise exceptions.CommandError("VCPUs must be an integer.")        try:            disk = int(disk)        except (TypeError, ValueError):            raise exceptions.CommandError("Disk must be an integer.")        if flavorid == "auto":            flavorid = None        elif not utils.is_uuid_like(flavorid):            try:                flavorid = int(flavorid)            except (TypeError, ValueError):                raise exceptions.CommandError("Flavor ID must be an integer "                                              "or a UUID or auto.")        try:            swap = int(swap)        except (TypeError, ValueError):            raise exceptions.CommandError("Swap must be an integer.")        try:            ephemeral = int(ephemeral)        except (TypeError, ValueError):            raise exceptions.CommandError("Ephemeral must be an integer.")        try:            rxtx_factor = float(rxtx_factor)        except (TypeError, ValueError):            raise exceptions.CommandError("rxtx_factor must be a float.")        try:            is_public = utils.bool_from_str(is_public)        except:            raise exceptions.CommandError("is_public must be a boolean.")        body = {            "flavor": {                "name": name,                "ram": ram,                "vcpus": vcpus,                "disk": disk,                "id": flavorid,                "swap": swap,                "OS-FLV-EXT-DATA:ephemeral": ephemeral,                "rxtx_factor": rxtx_factor,                "os-flavor-access:is_public": is_public,            }        }        return self._create("/flavors", body, "flavor")

他将会在子类中被赋一个继承于base.Resource类的类的值,他实际代表调用方法返回的对象。如client.flavors.get(…)实际返回是一个Flavor对象的。Flavor对象的实际属性,是通过
    def _add_details(self, info):        for (k, v) in info.iteritems():            try:                setattr(self, k, v)                self._info[k] = v            except AttributeError:                # In this case we already defined the attribute on the class                pass

    def __getattr__(self, k):        if k not in self.__dict__:            #NOTE(bcwaldon): disallow lazy-loading if already loaded once            if not self.is_loaded():                self.get()                return self.__getattr__(k)            raise AttributeError(k)        else:            return self.__dict__[k]

将通过返回的body类型的数据,转化为flavor对象的实际属性,可被实际调用如flavor.name, flavor.ram。


原创粉丝点击