nova的 microversion 实现

之前想写nova的policy的实现, 但是发现网上,有人写的很不错了。

但是个人认为存在一些问题。 ref:  http://www.cnblogs.com/shaohef/p/4527436.html

希望 microversion 还没有人写。

microversion实现

microversion实现的机制,就是在http的头部增加一个请求的小版本, nova的serve大家 搜索 new, type, metaclass, 都会介绍。r 根据这个小版本号,做相应的action。
实在是没有什么好介绍了。


这个我想像的microversion有些gap。 我开始以为是在url中指定版本号,而不是在head中。 


microversion的值得研究的是如下这段代码。

1 class subContorller(wsgi.Controller)
2     @wsgi.Controller.api_version("2.1", "2.3")
3     def my_api_method(self, req, id):
4         .... method_1 ...
5 
6     @wsgi.Controller.api_version("2.4") #noqa
7     def my_api_method(self, req, id):
8         .... method_2 ...

来自: http://docs.openstack.org/developer/nova/devref/api_microversions.html#changing-a-method-s-behaviour

这段代码,有点诡异,在一个类中实现了两个同名的属性,后面的那个将覆盖前面的那个。

如果是熟悉python的类和实例创建过程的,肯定认为很easy。

瞬间就能想到实现原理, 比如说 不正直的绅士, 他直接做过类似的代码。 

其实在IBM的kvm的team,做发行版本的同事,也能想到怎么实现的。

因为我之前给他们介绍个python的类和实例创建过程,其实google/so 一大堆。

我的介绍肯定不如他们自己学习效果好,我纯粹就是显摆而已。

我们要在magnum上,实现microversion,所以我我按照自己的思路尝试自己实现一个。

首先,在同一个类中,定义多个同名函数,最后一个函数, 会覆盖其他的。

怎么办呢?

跟大家一样,第一想法是重载 __getattribute__ 的类。

做法是重名的函数,想办法重新命名。 然后在__getattribute__中,知道期望的函数。

 发现,没有找到一个合适的位置,来hack重名的函数。

下面代码中的第5行,是我找到的唯一可能问位置,但是,这个代码只有类的实例才会调用。  ~~~~

 1 def operater(min, max):
 2     def operate(fn):
 3         def wraper(self, *args, **kv):
 4             if fn.func_name not in self.funs:
 5                 self.funs[fn.func_name] = [(fn.func_name+"_"+min+"_"+max, wraper)]
 6             else:
 7                 self.funs[fn.func_name].append((fn.func_name+"_"+min+"_"+max, wraper))
 8             print getattr(self, fn.func_name)
 9             print "begin decorate"
10             return fn(self, *args, **kv)
11             print "end decorate"
12             type(self)
13         return wraper
14     return operate
15 
16 
17 class Controller(object):
18     funs = {}
19     def __init__(self):
20         print self
21 
22     def __getattribute__(self, name):
23         if name in self.funs:
24             all_funs = self.funs[name]
25             fun = self.funs[0][1]
26             return fun
27         return object.__getattribute__(self, name)
28 
29     @operater("1.0", "1.5") # noqa
30     def fun1(self):
31         print self
32         print "inline fun1"
33 
34     @operater("1.0", "1.6") # noqa
35     def fun1(self):
36         print self
37         print "inline fun2"
38 
39 if __name__ == '__main__':
40     print "start main"
41     import pdb
42     pdb.set_trace()
43     i = Controller()    
44  print dir(i)
45     i.fun1()
View Code

大家可以尝试写一下, 不知道有没有实现的可能, 过程中可能会有不少坑。

实在是给大家做过太多的培训了,类合实例的创建过程,还是比较清楚的。

休息了一下,立马清醒。 发现自己比较傻逼,这个位置是创建类的时候。

大家 搜索 new, type, metaclass, 都会介绍。

查看nova的代码,果真如此。 

nova采用了six.add_metaclass 来构造类。把nova的相关代码摘取如下, 很简单,都不需要解释。

  1 import six
  2 
  3 class VersionedMethod(object):
  4 
  5     def __init__(self, name, start_version, end_version, func):
  6         self.name = name
  7         self.start_version = start_version
  8         self.end_version = end_version
  9         self.func = func
 10 
 11     def __str__(self):
 12         return ("Version Method %s: min: %s, max: %s"
 13                 % (self.name, self.start_version, self.end_version))
 14 
 15 
 16 VER_METHOD_ATTR = 'versioned_methods'
 17 obj_min_ver = "2.0"
 18 obj_max_ver = "2.3"
 19 
 20 
 21 class ControllerMetaclass(type):
 22     def __new__(mcs, name, bases, cls_dict):
 23         versioned_methods = None
 24         # start with wsgi actions from base classes
 25         for base in bases:
 26             # actions.update(getattr(base, 'wsgi_actions', {}))
 27 
 28             if base.__name__ == "Controller":
 29                 # NOTE(cyeoh): This resets the VER_METHOD_ATTR attribute
 30                 # between API controller class creations. This allows us
 31                 # to use a class decorator on the API methods that doesn't
 32                 # require naming explicitly what method is being versioned as
 33                 # it can be implicit based on the method decorated. It is a bit
 34                 # ugly.
 35                 print "+" * 80
 36                 print base.__dict__
 37                 if VER_METHOD_ATTR in base.__dict__:
 38                     versioned_methods = getattr(base, VER_METHOD_ATTR)
 39                     delattr(base, VER_METHOD_ATTR)
 40 
 41         for key, value in cls_dict.items():
 42             if not callable(value):
 43                 continue
 44         if versioned_methods:
 45             cls_dict[VER_METHOD_ATTR] = versioned_methods
 46 
 47         return super(ControllerMetaclass, mcs).__new__(mcs, name, bases,
 48                                                        cls_dict)
 49 
 50 
 51 class abc(object):
 52     pass
 53 
 54 @six.add_metaclass(ControllerMetaclass)
 55 class Controller(abc):
 56 
 57 
 58     def __getattribute__(self, key): 
 59         def version_select(*args, **kwargs):
 60             # The first arg to all versioned methods is always the request
 61             # object. The version for the request is attached to the
 62             # request object
 63             func_list = self.versioned_methods[key]
 64             print func_list
 65             for func in func_list:
 66                 print "^" * 80
 67                 return func.func(self, *args, **kwargs)
 68                 # print func.func_name, func.obj_min_ver, func.obj_max_ver
 69             return func.func(self, *args, **kwargs)
 70             # No version match
 71             raise exception.VersionNotFoundForAPIMethod(version=ver)
 72 
 73         try:
 74             # super(LockerDecorator,  self).__getattribute__(self, name)
 75             version_meth_dict = abc.__getattribute__(self, VER_METHOD_ATTR)
 76         except AttributeError:
 77             # No versioning on this class
 78             return abc.__getattribute__(self, key)
 79 
 80         if version_meth_dict and 
 81           key in abc.__getattribute__(self, VER_METHOD_ATTR):
 82             return version_select
 83 
 84         return abc.__getattribute__(self, key)
 85     
 86 
 87     # NOTE(cyeoh): This decorator MUST appear first (the outermost
 88     # decorator) on an API method for it to work correctly
 89     @classmethod
 90     def api_version(cls, min_ver, max_ver=None):
 91         def decorator(f):
 92     
 93             # Add to list of versioned methods registered
 94             func_name = f.__name__
 95             new_func = VersionedMethod(func_name, obj_min_ver, obj_max_ver, f)
 96     
 97             func_dict = getattr(cls, VER_METHOD_ATTR, {})
 98             if not func_dict:
 99                 setattr(cls, VER_METHOD_ATTR, func_dict)
100  
101             # global func_list 
102             func_list = func_dict.get(func_name, [])
103             if not func_list:
104                 func_dict[func_name] = func_list
105             func_list.append(new_func)
106             func_list.sort(key=lambda f: f.start_version, reverse=True)
107     
108             return f
109     
110         return decorator
111 
112 
113 class MyController(Controller):
114     @Controller.api_version("2.2") 
115     def create(self, req, body): 
116         print "create v2.2"
117  
118     @Controller.api_version("2.1", "2.1")  # noqa 
119     def create(self, req, body): 
120         print "create v2.1"
121 
122     def delete(self, req, body): 
123         print "delete no version"
124 
125 if __name__ == "__main__":
126     i = MyController()
127     i.create("1", "2")
128     i.delete("1", "2")
129     print "exit"
View Code

                                        

原文地址:https://www.cnblogs.com/shaohef/p/4527117.html