设为首页 收藏本站
查看: 1014|回复: 0

[经验分享] how to read openstack code : stevedore

[复制链接]

尚未签到

发表于 2017-6-26 21:14:34 | 显示全部楼层 |阅读模式
  学习了WSGI/Paste deploy后,还需要对一些在openstack中一些package有一些了解,才能更好的理解openstack的代码

What is stevedore
  我们在写代码的时候通常把一个一个的功能块独立编写,甚至发布一定的规则和接口由第三方编写,然后在运行时根据实际情况来选择加载哪些功能模块。这样的好处是松耦合,灵活,而且便于协作。
  stevedore就是一个很好的帮助动态加载代码的工具,openstack中很多plugin就是通过stevedore加载的。
  下面通过一些应用场景看一下如何使用stevedore。

Driver 场景
  假设我们有一个开源系统,我们不希望限定用户对数据库的选择。用户可以选择mysql/oracle/mongo等任意数据库。这就意味着我们需要对很多种数据库提供接口。这个工作量很大,并且要求开发人员对每一种数据库非常熟悉。最好的做法就是把数据库的接口设计成driver,并发布出该driver所需要实现的接口。这样任何第三方的组织或个人都可以参与开发。
  比如,我们定义下面的接口。任何开发者,只要编写一个实现了下列接口的class,就可以实现一个driver
  

@six.add_metaclass(abc.ABCMeta)  
class Base(object):
  """
  The Base>  """
  

  @abc.abstractmethod
  def create_user(self, user_name):
  """
  

  :param user_name:
  The name of user
  :return:
  A user object
  """
  

  基于上述接口,我们尝试开发一个mysql的driver如下:
  

# mysql_driver.py  
import base
  

  

  
class MysqlDriver(base.Base):
  def create_user(self, user_name):
  return "create user %s in mysql" % user_name
  

  该package的目录结构如下
  

[iyunv@controller POTUS]# tree MysqlDriver/  
MysqlDriver/
  
├── mysql_driver
  
│   ├── __init__.py
  
│   └── mysql_driver.py
  
└── setup.py
  

  要注意在mysql_driver/init.py中要有 import mysql_driver这句话
  setup.py的内容如下:
  

from setuptools import setup, find_packages  

  
setup(
  name='mysql_driver',
  version='1.0',
  

  packages=find_packages(),
  

  entry_points={
  'my_system.db_driver': [
  'mysql = mysql_driver.mysql_driver:MysqlDriver',
  ],
  },
  
)
  

  运行python setup.py install 后,会在系统目录中安装mysql_driver这个egg包,egg中有entry_point文件中记录了如下内容:
  

[my_system.db_driver]  

  
mysql = mysql_driver.mysql_driver:MysqlDriver
  

  my_system.db_driver是操作系统全局唯一的namespace。mysql是entry point的名字。通过这两个组合,可以定位到需要加载的代码,也就是我们的driver。
  我们同样的再开发一个oracle_driver,如下:
  

[iyunv@controller POTUS]# tree OracleDriver/  
OracleDriver/
  
├── oracle_driver
  
│   ├── __init__.py
  
│   └── oracle_driver.py
  
└── setup.py
  

  oracle_driver.py内容如下
  

# mysql_driver.py  
import base
  

  

  
class OracleDriver(base.Base):
  def create_user(self, user_name):
  return "create user %s in oracle" % user_name
  

  setup.py内容如下:
  

from setuptools import setup, find_packages  

  
setup(
  name='oracle_driver',
  version='1.0',
  packages=find_packages(),
  entry_points={
  'my_system.db_driver': [
  'oracle = oracle_driver.oracle_driver:OracleDriver',
  ],
  },
  
)
  

  不要忘了在__init__.py中加上 import oracle_driver。
  ok, 在python setup.py install 后,我们的系统中有了mysql_driver 和 oracle_driver 下面就是应用了。
  

>>> from stevedore import driver  
>>> mgr = driver.DriverManager(namespace = "my_system.db_driver", name = "mysql", invoke_on_load = True, invoke_args = ())
  
>>> mgr.driver.create_user('Tom')
  
'create user Tom in mysql'
  
>>> mgr = driver.DriverManager(namespace = "my_system.db_driver", name = "oracle", invoke_on_load = True, invoke_args = ())
  
>>> mgr.driver.create_user('Tom')
  
'create user Tom in oracle'
  

  可以看到,当name=mysql时,调用的是mysql_driver, 当name=oracle时,调用的是oracle_driver
  Driver的方式,让不同的driver开发过程相互独立,调用也更加灵活

Hook场景
  Hook 顾名思义,钩子。就像window开发中的hook一样,每一个hook都是一个功能组件,会被一定的事件驱动。一个事件可以驱动一个hook,也可以驱动多个hook。同一个hook可以被多个不同的事件驱动。
  假设我们开发了一个IDE工具,在鼠标点击关闭按钮的时候,会产生CLOSE_WINDOW EVENT, 这时候需要save_content组件保存内容。在鼠标点击commit按钮的时候,会产生COMMIT EVENT,这时候需要save_content发挥作用,也需要upload_content组件把内容上传到服务器。
  这种场景下,我们可以开发save_content, upload_content两个组件,并且把save_content注册在CLOSE_WINDOW 和 COMMIT 两个event下,把upload_content注册在COMMIT event下。开发过程如下:
  首先是save_content,
  

[iyunv@controller POTUS]# tree>
IDE_HOOKA/
  
├── save_content
  
│   ├── __init__.py
  
│   └── save_content.py
  
└── setup.py
  

  save_content 内容如下:
  

class SaveContent(object):  def __call__(self, *args, **kwargs):
  return "content are saved"
  

  setup.py 内容如下:
  

from setuptools import setup, find_packages  

  
setup(
  name='save_content',
  version='1.0',
  

  packages=find_packages(),
  

  entry_points={
  'ide.hooks': [
  'CLOSE_WINDOW = save_content.save_content:SaveContent',
  'COMMIT = save_content.save_content:SaveContent'
  ],
  },
  
)
  

  init.py中有import save_content这句不要落下。
  在python setup.py install 后,save_content.save_content:SaveContent 会被注册到CLOSE_WINDOW 和 COMMIT这两个event下。
  我们再开发一个upload_content 组件如下:
  

[iyunv@controller POTUS]# tree>
IDE_HOOKB/  
├── setup.py
  
└── upload_content
  ├── __init__.py
  └── upload_content.py
  

  主要看一下setup.py
  
from setuptools import setup, find_packages
  

setup(  name='upload_content',
  version='1.0',
  

  packages=find_packages(),
  

  entry_points={
  'ide.hooks': [
  'COMMIT = upload_content.upload_content:UploadContent'
  ],
  },
  
)
  

  这次,我们只注册了upload_content到COMMIT下。
  在安装后,我们有了save_content 注册在CLOSE_WINDOW 和 COMMIT下, upload_content注册在COMMIT下。这样,当COMMIT事件发生,系统会保存内容并upload内容到服务器,当CLOSE_WINDOW事件发生,会保存内容。
  运行如下:
  

# import hookmanager  
# 定义运行hook的函数
  
>>> from stevedore import HookManager
  
>>> def run(hook):
  
...     return (hook.name, hook.obj())
  
...
  

  
# 根据Close Window调用hook
  
>>> mgr = HookManager(namespace = "ide.hooks", name = "CLOSE_WINDOW", invoke_on_load = True, invoke_args = ())
  
>>> mgr.map(run)
  
[('CLOSE_WINDOW', 'content are saved')]
  

  
# 根据commit调用hook
  
>>> mgr = HookManager(namespace = "ide.hooks", name = "COMMIT", invoke_on_load = True, invoke_args = ())
  
>>> mgr.map(run)
  
[('COMMIT', 'content are saved'), ('COMMIT', 'content are uploaded')]
  

extension 场景
  对比driver hook,他们都是通过setuptools的entry point来调用代码。driver是根据一个名字加载一个driver, hook是根据一个名字加载多个组件,而extension则是不根据名字,把namespace下所有的组件加载。
  

[iyunv@controller POTUS]# tree EXT/  
EXT/
  
├── ext
  
│   ├── ext1.py
  
│   ├── ext2.py
  
│   ├── ext3.py
  
│   └── __init__.py
  
└── setup.py
  

  ext1的内容如下
  

class EXT1(object):  def __call__(self, *args, **kwargs):
  return "ext1 is called"
  

  setup.py内容如下:
  

from setuptools import setup, find_packages  

  
setup(
  name='ext',
  version='1.0',
  

  packages=find_packages(),
  

  entry_points={
  'my_namespace': [
  'ext1 = ext.ext1:EXT1',
  'ext2 = ext.ext2:EXT2',
  'ext3 = ext.ext3:EXT3'
  ],
  },
  
)
  

  按照完成后,ext1/ext2/ext3都被注册到my_namespace下。 调用方式如下:
  

>>> from stevedore import ExtensionManager  
>>> mgr=ExtensionManager(namespace='my_namespace', invoke_on_load=True, invoke_args=())
  
>>> def run(hook):
  
...     return (hook.name, hook.obj)
  
...
  
>>> mgr.map(run)
  
[('ext3', <ext.ext3.EXT3 object at 0xd62a50>), ('ext2', <ext.ext2.EXT2 object at 0x102b6d0>), ('ext1', <ext.ext1.EXT1 object at 0x1034c10>)]
  

  调用方式可以看到,通过namespace可以把其下所有组件加载
  以上是stevedore的主要用法,下一篇讲展示routes的用法

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-388400-1-1.html 上篇帖子: 使用devstack安装部署openstack 下篇帖子: OpenStack:安装Neutron与provider network
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表