您的位置:首页 > 运维架构

openstack源码解析之虚机修改密码

2017-07-04 10:23 204 查看
本次以修改centos操作系统虚机密码为例,给大家从源码级别讲解整个流程,希望对大家有所帮助。

修改密码的本质,就是修改对应的passwd和shadow文件,并且将修改后的文件注入到实例的镜像中。

来看下具体流程:

1,请求提交到了nova-api(nova/api/openstack/compute/server.py):

      def _action_change_password(self, req, id, body):

            ...

            self.compute_api.set_admin_password(context, server,

                                                    password, meta_item)     ##请求发到对应的compute节点上执行set_admin_password操作

2,compute节点执行重置密码操作(nova/compute/manager.py):

      def set_admin_password(self, context, instance, new_pass):

            ...  ##获取信息,并且设置下数据库状态

            self.driver.set_admin_password(context,

                      instance, network_info, new_pass)   ##告诉driver,执行set_admin_password操作。

            if current_power_state == power_state.RUNNING and \

                            inst_power_state == power_state.SHUTDOWN:

                        self._power_on(context, instance)      ##判断执行的实例是否已经关机,关机的话,执行开机操作。

                        ....

3,驱动执行设置密码,这里我用的是libvirt (nova/virt/libvirt/driver.py)

     def set_admin_password(self, context, instance, network_info, admin_pass):

           if instance.power_state != power_state.SHUTDOWN:

                self.shutdown(instance)     ##首先要执行关机操作,文件注入,需要关机。

           ...

           ##获取镜像路径。这里是injection_path = instance_path + '/disk'

           disk.inject_data(image=injection_path,

                             admin_password=admin_pass,

                             partition=target_partition,

                             use_cow=CONF.use_cow_images,

                             mandatory=('files',))   ##执行注入操作

            ...

 4,执行具体的操作 (nova/virt/disk/api.py)

      def inject_data(image, key=None, net=None, metadata=None, admin_password=None,

                files=None, partition=None, use_cow=False, mandatory=()):

            ...

             fs = vfs.VFS.instance_for_image(image, fmt, partition)

       首先来看语句fs = vfs.VFS.instance_for_image(image, fmt, partition),方法instance_for_image实现了尝试导入guestfs模块;如果guestfs模块导入成功,则继而导入类nova.virt.disk.vfs.guestfs.VFSGuestFS;如果guestfs模块导入不成功,则继而导入类nova.virt.disk.vfs.localfs.VFSLocalFS;从而实现了选择不同的文件系统类型(本地文件系统或来宾文件系统)对磁盘镜像的操作实现类;具体来看方法instance_for_image的代码实现

           fs.setup()   

 ==========================================================

        这里我们来看下VFSGuestFS:

        def setup(self):

               self.handle = tpool.Proxy(guestfs.GuestFS(close_on_exit=False))
               ## 其中guestfs.GuestFS是一个c接口,作用是对一个image的操作,对image的读写,即对文件系统文件的读写。初始化的时候加载的
guestfs = __import__('guestfs')
               
        我们接下来看下inject_data_into_fs这个方法
        for inject in ('key', 'net', 'metadata', 'admin_password', 'files'):

                    inject_val = locals()[inject]

                    inject_func = globals()['_inject_%s_into_fs' % inject]

                    if inject_val:

                    try:

                          inject_func(inject_val, fs)  ###直接跳转到def _inject_admin_password_into_fs(admin_passwd, fs)这个方法

                   
注:这里采用字符串匹配的方式,分不同的情况调用不同的方法,来实现不同类型文件的注入,调用的具体方法有:

        _inject_files_into_fs

       _inject_metadata_into_fs

       _inject_key_into_fs

       _inject_net_into_fs

       _inject_admin_password_into_fs
 
这里我们传入admin_password,所以调用的是_inject_admin_password_into_fs

5, 看下_inject_admin_password_into_fs

                   passwd_path = os.path.join('etc', 'passwd')

                   shadow_path = os.path.join('etc', 'shadow')

                   passwd_data = fs.read_file(passwd_path)

                   shadow_data = fs.read_file(shadow_path)

                    new_shadow_data = _set_passwd(admin_user, admin_passwd,

                                  passwd_data, shadow_data)
            ### 读取/etc/passwd和/etc/shadow的内容,针对新的用户密码,替换之前的数据。
6,设置密码的方式
     在现在的linux和unix系统中,用户的密码都保存在shadow文件中,因为密码关系到系统的安全,所以只有root用户才有读shadow文件的权
              限。shadow中存放的内容是有着一定的格式的,看如下例子:

           root:$1$v2wT9rQF$XSpGgoB93STC4EFSlgpjg1:14181:0:99999:7:::

           冒号是分割符,分别代表着,每个字段分别代表着:

           :用户名

           :密码hash值

           :密码修改距离1970年1月1日的时间

           :密码将被允许修改之前的天数(0 表示“可在任何时间修改”)

           :系统将强制用户修改为新密码之前的天数(1 表示“永远都不能修改”)

           :密码过期之前,用户将被警告过期的天数(-1 表示“没有警告”)

           :密码过期之后,系统自动禁用帐户的天数(-1 表示“永远不会禁用”)

           :该帐户被禁用的天数(-1 表示“该帐户被启用”)

7,根据算法生成了新的数据,调用fs.replace_file(shadow_path,
new_shadow_data)

 
          

              /nova/virt/disk/vfs/guestfs.py  

              def replace_file(self, path, content):  

                  LOG.debug(_("Replace file path=%(path)s") % locals())  

                  path = self._canonicalize_path(path)  

              self.handle.write(path, content)  

       ##将内容写入对应文件。还有一个方法是append,是将内容写到最末尾,叫append_file。

8,如果出现重置密码失败,分享一个有用的方法:

     
        guestmount -a  disk -m /dev/vda1 --rw /mnt/   ##将系统映射到文件中
        chroot /mnt/ /bin/sh    ##会出来一个命令行,直接可以执行passwd .
        umount /mnt/ 

好了,分享到此结束,大家有什么疑问,可以评论中提出,我会尽早回复大家!

       
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐