2014.10.08

本文深入探讨了OpenStack中实例元数据的存储方式,以及Nova命令如rename、root-password、get-password的实现原理。同时,解释了Python wsgi服务器的工作机制,包括如何使用eventlet库实现多线程服务。最后,详细介绍了装饰器在Python中的应用,包括其产生的原因、基本用法以及如何处理带参数的装饰器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

和妹子聊天聊得不咋地,有一句没一句的,哎。。。这事还得坚持.....不提妹子的事,长期斗争,还得慢慢来。。。必须要有耐心。。。不扯这个了,扯点有用的

1. meta_data保存在什么地方?

经过今天的研究,我发现,meta_data的所有数据都存在数据库中,有这么几张相关的表:instances, instance_metadata,instance_system_metadata;

instances中有:

instance_metadata保存的是meta数据,也就是键值对,我通过实验发现,调用nova meta <instance-id> set/delete XXX=YYY后,实例(虚机)通过169.254.169.254:80/openstack/latest/meta_data.json能得到meta的键值对,也就是说,只要调用nova meta成功,键值对立刻有了,或者删除掉了。

某一实例:rhel-169-2
原本:
没有meta-data
在宿主机上输入
[ root@BC-EC-85-50 ~(keystone_admin)]# nova meta eb937974-61ec-4067-a2b9-c336701f9523 set lihao=1 bbcc=234
实例rhel-169-2就有数据了:




instance_system_metadata:

1. image_min_disk 磁盘大小
2. 内存大小
3. 交换区大小
4. vcpu的数量
5. 模板id
6. 模板大小
7. ephemeral_gb "短暂的" 临时磁盘
8. rxtx_factor   Optional property allows created servers to have a different bandwidth cap than that defined in the network they are attached to. This factor is multiplied by the rxtx_base property of the network. Default value is 1.0 (that is, the same as attached network).
9.模板大小
10. Container Format :
            bare
            ovf open virtualization format  
        The container format refers to whether the virtual machine image is in a file format that also contains metadata about the actual virtual machine.
Note that the container format string is not currently used by Glance or other OpenStack components, so it is safe to simply specify   bare   as the container format if you are unsure.
11. disk format  qcow2等等
12. cpu数量
13. image_ref 镜像的对应id
14. 是否有网络
15. clean _attempts  不知道
如果实例(虚机)有过更新,比如执行过rebuild之类的,还会多出来一些内容,不再多说

总之,meta_data的内容就存在这几个数据库表中,因此,访问169.254获得的任何内容都可以从数据库表中获得,因此我大胆得出结论,meta_data server就是将数据库中的metadata数据组织一下,然后发给提出申请的虚机的。就这么简单,但是不提供几个数据:
1.admin_pass
2.注入的文件


2. nova 命令中,rename 命令实际上是修改display_name而不是hostname,因此hostname显然不可能修改
rename:
nova api,最终调用update,
最终的结果如下:
   1)nova/api/openstack/compute/servers.py   1030 def update(self, req, id, body):

   2) 从上文中的url中提取数据,比如name

#从数据库中的数据中得到一个虚机的实例

       instance = self.compute_api.get(ctxt, id,

                                            want_objects=True)

#允许api方法从一个db查询中存储一个对象,这个db查询方法是被api extensions使用的,并且是重复的api请求

#我猜测就是,第一次查询比较慢,之后这个object保存在数据库中,不用重新生成,而是直接使用,相当于一个cache缓存

            req.cache_db_instance(instance)

#不详

            policy.enforce(ctxt, 'compute:update', instance)

#最终调用 nova/objects/instance.py  的父类 base.NovaObject方法update,就是把instance(字典(各种键值对的集合))displayname = 新名字

            instance.update(update_dict)

#调用 nova/objects/instance.py 中的save方法:

# 基本上就是将变化保存数据库

            instance.save()


#总结,rename只是修改display_name而不是hostname,因此虚机中的名字当然不会变



3. nova root-password 对于libvirt,不支持,因此kvm的机器不支持
nova root-password <server-name>

    @wsgi.response(202)
    @wsgi.serializers(xml=FullServerTemplate)
    @wsgi.deserializers(xml=ActionDeserializer)
    @wsgi.action('changePassword')
    def _action_change_password(self, req, id, body):
        context = req.environ['nova.context']
        if ('changePassword' not in body
                or 'adminPass' not in body['changePassword']):
            msg = _("No adminPass was specified")
            raise exc.HTTPBadRequest(explanation=msg)
        password = self._get_server_admin_password(body['changePassword'])

        server = self._get_server(context, req, id)
        try:

#libvirt并没有实现

            self.compute_api.set_admin_password(context, server, password)
        except NotImplementedError:
            msg = _("Unable to set password on instance")
            raise exc.HTTPNotImplemented(explanation=msg)
        return webob.Response(status_int=202) 





4. nova get-password 这个好像也不行,具体什么我忘了,反正是不行
nova get-password <server-name>

novaclient/v1_1/shell.py 2033
def do_get_password(cs, args):
    """Get password for a server."""s
    #先调用restapi获得server 实例的信息
    server = _find_server(cs, args.server)
    #/servers/%s/os-server-password     为空 
    data = server.get_password(args.private_key)
    print(data)



例子:nova boot --flavor 2 --image 45ad7f2a-2c94-4558-96d8-62960e1828a7 --meta bcec=1 --meta lihao=234 --user-data /opt/stack/devstack/file1 --availability-zone nova --security-groups default --nic net-id=9525e327-2d95-40ed-82a7-8f0b3b1b9aa0,v4-fixed-ip=10.190.82.200 rhel






关于python语法:
1. 作为一个wsgi,是对外提供服务的,首先nova api并没有需要tomcat,阿帕奇,nginx,因此我认为它是自带的server,并不需呀中间件


2. 如果wsgi或者说是nova api是server,那么我认为它不太需要创建socket,bind,listen,但是事实上,他就是这样的?莫非是用socket通信?可能,但是走的上层协议是http协议。
    并没有看到server有任何accept的过程,这和传统的c编程是完全不一样的,觉得很懊糟。。。


3. 服务端程序必须是多线程的,否则无法给多个连接做服务,因此有几个问题:
    1)有一篇文章提到:wsgiserver和server是不一样的,wsgiserver 使用的是eventlet协程,用spawn去孵化一个协程执行程序,而server使用os.fork来创建传统的进程,最后调用server.start去执行。


4. eventlet协程和线程的最大区别是多个协程同一时间只有一个能被执行,不存在多个线程并行而出现一些问题。

eventlet.spawn(func,*args,**kw) 开一个greenthread跑func

eventlet.spawn_n(func,*args,**kw)开一个greenthread跑func,但不知道greenthread何时终止,运行速度快

eventlet.spawn_after(seconds,func,*args,**kw)开一个greenthread跑func

eventlet.connect(addr, family=2, bind=None) 客户端sockets,连接对象

eventlet.listen(addr,family=2, backlog=50) 服务端sockes,监听对象

eventlet.wrap_ssl(sock,*a, **kw) 配置ssl,通过PyOpenssl

classeventlet.GreenPool 线程池管理并发线程

eventlet.serve(sock,handle, concurrency=1000) 建立服务器,scck为listen对象,handle为处理函数,最后一个为最大并发支持数

有accept方法;

eventlet.import_patched(modulename,*additional_modules,**kw_additional_modules) 向线程导入标准模块的绿色版本

eventlet.monkey_patch(all=True,os=False,select=False,socket=False,thread=False,time=False) 全局向线程导入系统模块


仔细一分析,得到如下结论:对于服务端程序,先listen,后创建greenpoll(所谓的线程池),最后调用serve,创建服务器,这就解决了上面的2所提出的问题。


5. 一个服务端eventlet如何服务多个地址?

    1)一般都是一个server监听一个地址:端口,也就是一个套接字,但是,这是C的一些习惯,比如我要调用:localhost:8090/server/create与localhost:8090/server/update,返回的结果就会不同,那么这个server是如何处理的呢?

    让我们来猜一下:

    http协议中必然有一个字段是标示要访问的具体地址,根据这个地址的不同,调用不同的处理函数进行处理,并返回结果。

    也就是说,可能在server中,有一个dispatcher,它能够将消息发给不同的处理程序    

    我们揭晓谜底,看看别人wsgi是怎么做的:

    最简单的:

                a. server = listen      //socket

                b. pool = GreenPool //线程池

                c. new_sock, address = server.accept    //与client通信的sock, 与client sock地址

                d. pool.spawn_n(handle,new_sock)        //handle是处理函数

完整的代码:

  1. import eventlet  
  2. c=eventlet.connect(('127.0.0.1', 6000))  
  3. while True:  
  4.     data=raw_input('Enter data:')  
  5.     c.sendall(data)  
  6.     rc=c.recv(1024)  
  7.     print rc  
  8.   
  9. import eventlet  
  10. def handle(client):  
  11.     while True:  
  12.         c = client.recv(1024)  
  13.         print c  
  14.         client.sendall(c)  
  15. server = eventlet.listen(('0.0.0.0', 6000))  
  16. pool = eventlet.GreenPool(10000)  
  17. while True:  
  18.     new_sock, address = server.accept()  
  19.     pool.spawn_n(handle, new_sock)


这个程序一定是可以多线程服务的,但是这个却没有用http协议,我看行~


那么上述提出的问题如何解决呢?例子是socket通信,但是我们要走http协议,那很简单,其实就是约定一下收发包的格式就行了~

wsgi服务器:

    wsgi.server()这个方法中,第一个参数就是socket,就是所谓的listen socket,第二个就是得到连接后的处理函数,其他都是optional的,因此,我们有理由认为,wsgi.server完全是封装了eventlet中的accept,spawn等等函数

    但是处理函数的写法是有一定之归的:

    def XXX(env, start_response):

            必须先start_response(code,type)

            return

    

    在这个处理函数中,就可以对/server/update或者/server/create进行处理了

    

    if env['PATH_INFO'] != '/':

            start_response('404 Not Found',[('Content-Type','text/plain')])

            return ['Not Found\r\n']

    eventlet.import_patched('httplib2')  //所谓的打补丁??

    eventlet.monkey_patch(socket=True,select=True)

    start_response('200 OK',[('Content-Type','text/plain')])

    return ['Hello World!\r\n']


wsgi.server(eventlent.listen(('',8090)),hello_world)



基本上可以这么说,eventlet  spawn和wsgi.serve其实是相对应的,差不多~



wrap的内容

所谓的装饰器,这个我今天仔细看过一边,看看是否能够说得出来

1. 产生的原因:

    def hello()

        print "hello world"

    但是 想知道这个函数print执行所需的时间,因此


   def hello():

    time1 = time.time()

    print "hello world"

    time2 = time.time()

    print time2 - time1

每个函数都需要进行一次计算执行时间的运算,结果就是,所有的函数都在函数前和尾加上这些代码,效率太低了,是否有解决的办法?

    

 def hello():

     print "hello world"

def timefunc(func):

    time1 = time.time()

    func()

    time2 = time.time()

    print time2-time1

timefunc(hello)


但是,还是觉得不方便,因为写代码的时候要处处都写timefunc,而且可能还得传不同的参数,觉得很麻烦

有没有一种方法,它能够不执行这个语句,而是将制作好的函数返还给原函数,从而实现对原函数的改造?


def timefunc(func):

    def wrapper():

        time1 = time.time()

        func()

        time2 = time.time()

        print time2-time1

    return wrapper

hello = timefunc(hello)

hello()

将timefunc当成了一个创造函数的函数,而不是执行函数的函数


python更彻底的解决了这个问题:

@timefunc


def timefunc(func):

    def wrapper():

        time1 = time.time()

        func()

        time2 = time.time()

        print time2-time1

    return wrapper

@timefunc

def hello():

     print "hello world"

hello()

实际上,@timefunc相当于:hello = timefunc(hello)


那么对于有参数的怎么办呢?我们再进一步

1)hello函数带有参数

def timefunc(func): 

     def wrapper(word):

        time1 = time.time()

        func(word)

        time2 = time.time()

        print time2-time1

    return wrapper

@timefunc

def hello(word):

     print word

hello("lihao")

方法是,在wrapper后面要跟参数,和hello的一样,毕竟其实是hello = timefunc(hello)   hello = wrapper


2) 多个wrapper怎么破?


def timefunc2(func):

    def wrapper2(word):

        func(word)

        print "i dont know"

    return wrapper2

def timefunc(func):

    def wrapper(word):

        time1 = time.time()

        func(word)

        time2 = time.time()

        print time2-time1

    return wrapper

@timefunc2

@timefunc

def hello(word):

     print word

hello("lihao")

#结果

lihao

0.0

i dont know


先执行timefunc再执行timefunc2?这种说法不正确,应该这么说,

timefunc2在外层,timefunc在里层,因此,并不是timefunc先执行,而是说,反而timefunc2先执行,因为timefunc返回的是一个方法,而不是执行方法,相当于


@timefunc2

@timefunc 

hello =  timefunc2(timefunc(hello)) 先执行timefunc(hello),但是只是返回一个函数指针,正在执行是在timefunc2中才被执行的!!


3.装饰器中本身有参数







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值