Beispiel #1
0
    def delete_snapshot(dom=None, msg=None):
        extend_data = dict()

        try:
            assert isinstance(dom, libvirt.virDomain)
            assert isinstance(msg, dict)

            snapshot = dom.snapshotLookupByName(name=msg['snapshot_id'])
            snapshot.delete()

            response_emit.success(
                _object=msg['_object'],
                action=msg['action'],
                uuid=msg['uuid'],
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))

        except:
            log_emit.error(traceback.format_exc())
            response_emit.failure(
                _object=msg['_object'],
                action=msg.get('action'),
                uuid=msg.get('uuid'),
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))
Beispiel #2
0
    def create_snapshot(guest=None, msg=None):
        extend_data = dict()

        try:
            assert isinstance(guest, libvirt.virDomain)
            assert isinstance(msg, dict)
            snap_xml = """
                <domainsnapshot>
                </domainsnapshot>
            """

            snap_flags = 0
            snap_flags |= libvirt.VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC

            ret = guest.snapshotCreateXML(xmlDesc=snap_xml, flags=snap_flags)

            parent_id = ''

            try:
                parent = ret.getParent()
                parent_id = parent.getName()

            except libvirt.libvirtError, e:
                if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN_SNAPSHOT:
                    parent_id = '-'

            extend_data.update({'snapshot_id': ret.getName(), 'parent_id': parent_id, 'xml': ret.getXMLDesc()})

            response_emit.success(_object=msg['_object'], action=msg['action'], uuid=msg['uuid'],
                                  data=extend_data, passback_parameters=msg.get('passback_parameters'))
Beispiel #3
0
    def adjust_ability(dom=None, msg=None):
        extend_data = dict()

        try:
            assert isinstance(dom, libvirt.virDomain)
            assert isinstance(msg, dict)

            cpu = msg['cpu'].__str__()
            memory = msg['memory'].__str__()

            xml = ET.fromstring(dom.XMLDesc())

            origin_ability = xml.find('vcpu').text + '核' + (
                int(xml.find('memory').text) / 1024**2).__str__() + 'GiB'
            new_ability = cpu + '核' + memory + 'GiB'

            xml.find('vcpu').text = cpu

            xml.find('memory').set('unit', 'GiB')
            xml.find('memory').text = memory

            xml.find('currentMemory').set('unit', 'GiB')
            xml.find('currentMemory').text = memory

            xml_str = ET.tostring(xml, encoding='utf8', method='xml')

            if dom.isActive():
                raise RuntimeError(u'虚拟机非关闭状态。')

            else:
                if dom.connect().defineXML(xml=xml_str):
                    log = u' '.join([
                        u'域',
                        dom.name(), u', UUID',
                        dom.UUIDString(), u'配置从', origin_ability, '变更为',
                        new_ability
                    ])
                    log_emit.info(msg=log)

                else:
                    raise RuntimeError(u'变更配置失败。')

            response_emit.success(
                _object=msg['_object'],
                action=msg['action'],
                uuid=msg['uuid'],
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))

        except:
            log_emit.error(traceback.format_exc())
            response_emit.failure(
                _object=msg['_object'],
                action=msg.get('action'),
                uuid=msg.get('uuid'),
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))
Beispiel #4
0
    def allocate_bandwidth(dom=None, msg=None):
        extend_data = dict()
        """
        https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainModificationImpact
        """

        try:
            assert isinstance(dom, libvirt.virDomain)
            assert isinstance(msg, dict)

            bandwidth = msg['bandwidth'] / 1000 / 8
            mac = ET.fromstring(dom.XMLDesc()).findall(
                'devices/interface')[0].find('mac').attrib['address']

            interface_bandwidth = dom.interfaceParameters(mac, 0)
            interface_bandwidth['inbound.average'] = bandwidth
            interface_bandwidth['outbound.average'] = bandwidth

            dom.setInterfaceParameters(mac, interface_bandwidth,
                                       libvirt.VIR_DOMAIN_AFFECT_CONFIG)

            if dom.isActive():
                dom.setInterfaceParameters(mac, interface_bandwidth,
                                           libvirt.VIR_DOMAIN_AFFECT_LIVE)

            response_emit.success(
                _object=msg['_object'],
                action=msg['action'],
                uuid=msg['uuid'],
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))

        except:
            log_emit.error(traceback.format_exc())
            response_emit.failure(
                _object=msg['_object'],
                action=msg.get('action'),
                uuid=msg.get('uuid'),
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))
Beispiel #5
0
    def revert_snapshot(dom=None, msg=None):
        extend_data = dict()

        try:
            assert isinstance(dom, libvirt.virDomain)
            assert isinstance(msg, dict)

            snap_flags = 0
            snap_flags |= libvirt.VIR_DOMAIN_SNAPSHOT_REVERT_FORCE
            snapshot = dom.snapshotLookupByName(name=msg['snapshot_id'])

            try:
                dom.revertToSnapshot(snap=snapshot, flags=0)

            except libvirt.libvirtError, e:
                # 给予一次重新恢复的机会
                if e.get_error_code() == libvirt.VIR_ERR_SYSTEM_ERROR:
                    dom.revertToSnapshot(snap=snapshot, flags=snap_flags)

            # 如果恢复后的 Guest 为 Running 状态,则同步其系统时间。
            if dom.isActive():
                # https://qemu.weilnetz.de/doc/qemu-ga-ref.html#index-guest_002dset_002dtime
                libvirt_qemu.qemuAgentCommand(
                    dom,
                    json.dumps({
                        'execute': 'guest-set-time',
                        'arguments': {
                            'time': int(ji.Common.ts() * (10**9))
                        }
                    }), 3, libvirt_qemu.VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT)

            response_emit.success(
                _object=msg['_object'],
                action=msg['action'],
                uuid=msg['uuid'],
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))
Beispiel #6
0
    def instruction_process_engine(self):
        self.init_conn()

        ps = r.pubsub(ignore_subscribe_messages=False)
        ps.subscribe(config['instruction_channel'])

        while True:
            if Utils.exit_flag:
                msg = 'Thread instruction_process_engine say bye-bye'
                print msg
                logger.info(msg=msg)
                return

            threads_status['instruction_process_engine'] = dict()
            threads_status['instruction_process_engine']['timestamp'] = ji.Common.ts()

            # noinspection PyBroadException
            try:
                msg = ps.get_message(timeout=config['engine_cycle_interval'])

                if msg is None or 'data' not in msg or not isinstance(msg['data'], basestring):
                    continue

                try:
                    msg = json.loads(msg['data'])

                    if msg['action'] == 'pong':
                        continue

                    if msg['action'] == 'ping':
                        # 通过 ping pong 来刷存在感。因为经过实际测试发现,当订阅频道长时间没有数据来往,那么订阅者会被自动退出。
                        r.publish(config['instruction_channel'], message=json.dumps({'action': 'pong'}))
                        continue

                except ValueError as e:
                    logger.error(e.message)
                    log_emit.error(e.message)
                    continue

                if 'node_id' in msg and int(msg['node_id']) != self.node_id:
                    continue

                # 下列语句繁琐写法如 <code>if 'action' not in msg or 'uuid' not in msg:</code>
                if not all([key in msg for key in ['_object', 'action']]):
                    continue

                extend_data = dict()

                if msg['_object'] == 'guest':

                    self.refresh_guest_mapping()
                    if msg['action'] not in ['create']:

                        if msg['uuid'] not in self.guest_mapping_by_uuid:

                            if config['DEBUG']:
                                _log = u' '.join([u'uuid', msg['uuid'], u'在计算节点', self.hostname, u'中未找到.'])
                                logger.debug(_log)
                                log_emit.debug(_log)

                            raise RuntimeError('The uuid ' + msg['uuid'] + ' not found in current domains list.')

                        self.guest = self.guest_mapping_by_uuid[msg['uuid']]
                        if not isinstance(self.guest, libvirt.virDomain):
                            raise RuntimeError('Guest ' + msg['uuid'] + ' is not a domain.')

                    if msg['action'] == 'create':
                        t = threading.Thread(target=Guest.create, args=(self.conn, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'reboot':
                        if self.guest.reboot() != 0:
                            raise RuntimeError('Guest reboot failure.')

                    elif msg['action'] == 'force_reboot':
                        self.guest.destroy()
                        self.guest.create()
                        Guest.quota(guest=self.guest, msg=msg)

                    elif msg['action'] == 'shutdown':
                        if self.guest.shutdown() != 0:
                            raise RuntimeError('Guest shutdown failure.')

                    elif msg['action'] == 'force_shutdown':
                        if self.guest.destroy() != 0:
                            raise RuntimeError('Guest force shutdown failure.')

                    elif msg['action'] == 'boot':
                        if not self.guest.isActive():

                            if self.guest.create() != 0:
                                raise RuntimeError('Guest boot failure.')

                            Guest.quota(guest=self.guest, msg=msg)

                    elif msg['action'] == 'suspend':
                        if self.guest.suspend() != 0:
                            raise RuntimeError('Guest suspend failure.')

                    elif msg['action'] == 'resume':
                        if self.guest.resume() != 0:
                            raise RuntimeError('Guest resume failure.')

                    elif msg['action'] == 'delete':
                        root = ET.fromstring(self.guest.XMLDesc())

                        if self.guest.isActive():
                            self.guest.destroy()

                        self.guest.undefine()

                        system_disk = None

                        for _disk in root.findall('devices/disk'):
                            if 'vda' == _disk.find('target').get('dev'):
                                system_disk = _disk

                        if msg['storage_mode'] in [StorageMode.ceph.value, StorageMode.glusterfs.value]:
                            # 签出系统镜像路径
                            path_list = system_disk.find('source').attrib['name'].split('/')

                        if msg['storage_mode'] == StorageMode.glusterfs.value:
                            Guest.dfs_volume = path_list[0]
                            Guest.init_gfapi()

                            try:
                                Guest.gf.remove('/'.join(path_list[1:]))
                            except OSError:
                                pass

                        elif msg['storage_mode'] in [StorageMode.local.value, StorageMode.shared_mount.value]:
                            file_path = system_disk.find('source').attrib['file']
                            try:
                                os.remove(file_path)
                            except OSError:
                                pass

                    elif msg['action'] == 'reset_password':
                        if self.guest.setUserPassword(msg['user'], msg['password']) != 0:
                            raise RuntimeError('Guest reset password failure.')

                    elif msg['action'] == 'attach_disk':

                        if 'xml' not in msg:
                            _log = u'添加磁盘缺少 xml 参数'
                            raise KeyError(_log)

                        flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG
                        if self.guest.isActive():
                            flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE

                        # 添加磁盘成功返回时,ret值为0。可参考 Linux 命令返回值规范?
                        if self.guest.attachDeviceFlags(xml=msg['xml'], flags=flags) != 0:
                            raise RuntimeError('Attack disk failure.')

                        Guest.quota(guest=self.guest, msg=msg)

                    elif msg['action'] == 'detach_disk':

                        if 'xml' not in msg:
                            _log = u'分离磁盘缺少 xml 参数'
                            raise KeyError(_log)

                        flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG
                        if self.guest.isActive():
                            flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE

                        if self.guest.detachDeviceFlags(xml=msg['xml'], flags=flags) != 0:
                            raise RuntimeError('Detach disk failure.')

                    elif msg['action'] == 'update_ssh_key':
                        if not self.guest.isActive():
                            _log = u'欲更新 SSH-KEY 的目标虚拟机未处于活动状态。'
                            logger.warning(_log)
                            log_emit.warn(_log)
                            continue

                        ret = Guest.update_ssh_key(guest=self.guest, msg=msg)

                        logger.info(json.dumps(ret, ensure_ascii=False))

                    elif msg['action'] == 'allocate_bandwidth':
                        t = threading.Thread(target=Guest.allocate_bandwidth, args=(self.guest, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'adjust_ability':
                        t = threading.Thread(target=Guest.adjust_ability, args=(self.conn, self.guest, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'migrate':

                        # duri like qemu+ssh://destination_host/system
                        if 'duri' not in msg:
                            _log = u'迁移操作缺少 duri 参数'
                            raise KeyError(_log)

                        # https://rk4n.github.io/2016/08/10/qemu-post-copy-and-auto-converge-features/
                        flags = libvirt.VIR_MIGRATE_PERSIST_DEST | \
                            libvirt.VIR_MIGRATE_UNDEFINE_SOURCE | \
                            libvirt.VIR_MIGRATE_COMPRESSED | \
                            libvirt.VIR_MIGRATE_PEER2PEER | \
                            libvirt.VIR_MIGRATE_AUTO_CONVERGE

                        root = ET.fromstring(self.guest.XMLDesc())

                        if msg['storage_mode'] == StorageMode.local.value:
                            # 需要把磁盘存放路径加入到两边宿主机的存储池中
                            # 不然将会报 no storage pool with matching target path '/opt/Images' 错误
                            flags |= libvirt.VIR_MIGRATE_NON_SHARED_DISK
                            flags |= libvirt.VIR_MIGRATE_LIVE

                            if not self.guest.isActive():
                                _log = u'非共享存储不支持离线迁移。'
                                logger.error(_log)
                                log_emit.error(_log)
                                raise RuntimeError('Nonsupport online migrate with storage of non sharing mode.')

                            if self.init_ssh_client(hostname=msg['duri'].split('/')[2], user='******'):
                                for _disk in root.findall('devices/disk'):
                                    _file_path = _disk.find('source').get('file')
                                    disk_info = Disk.disk_info_by_local(image_path=_file_path)
                                    disk_size = disk_info['virtual-size']
                                    stdin, stdout, stderr = self.ssh_client.exec_command(
                                        ' '.join(['qemu-img', 'create', '-f', 'qcow2', _file_path, str(disk_size)]))

                                    for line in stdout:
                                        logger.info(line)
                                        log_emit.info(line)

                                    for line in stderr:
                                        logger.error(line)
                                        log_emit.error(line)

                        elif msg['storage_mode'] in [StorageMode.shared_mount.value, StorageMode.ceph.value,
                                                     StorageMode.glusterfs.value]:
                            if self.guest.isActive():
                                flags |= libvirt.VIR_MIGRATE_LIVE
                                flags |= libvirt.VIR_MIGRATE_TUNNELLED

                            else:
                                flags |= libvirt.VIR_MIGRATE_OFFLINE

                        if self.guest.migrateToURI(duri=msg['duri'], flags=flags) == 0:
                            if msg['storage_mode'] == StorageMode.local.value:
                                for _disk in root.findall('devices/disk'):
                                    _file_path = _disk.find('source').get('file')
                                    if _file_path is not None:
                                        os.remove(_file_path)

                        else:
                            raise RuntimeError('Unknown storage mode.')

                elif msg['_object'] == 'disk':
                    if msg['action'] == 'create':

                        if msg['storage_mode'] == StorageMode.glusterfs.value:
                            Guest.dfs_volume = msg['dfs_volume']
                            Guest.init_gfapi()

                            if not Disk.make_qemu_image_by_glusterfs(gf=Guest.gf, dfs_volume=msg['dfs_volume'],
                                                                     image_path=msg['image_path'], size=msg['size']):
                                raise RuntimeError('Create disk failure with glusterfs.')

                        elif msg['storage_mode'] in [StorageMode.local.value, StorageMode.shared_mount.value]:
                            if not Disk.make_qemu_image_by_local(image_path=msg['image_path'], size=msg['size']):
                                raise RuntimeError('Create disk failure with local storage mode.')

                    elif msg['action'] == 'delete':

                        if msg['storage_mode'] == StorageMode.glusterfs.value:
                            Guest.dfs_volume = msg['dfs_volume']
                            Guest.init_gfapi()

                            if Disk.delete_qemu_image_by_glusterfs(gf=Guest.gf, image_path=msg['image_path']) \
                                    is not None:
                                raise RuntimeError('Delete disk failure with glusterfs.')

                        elif msg['storage_mode'] in [StorageMode.local.value, StorageMode.shared_mount.value]:
                            if Disk.delete_qemu_image_by_local(image_path=msg['image_path']) is not None:
                                raise RuntimeError('Delete disk failure with local storage mode.')

                    elif msg['action'] == 'resize':

                        if 'size' not in msg:
                            _log = u'添加磁盘缺少 disk 或 disk["size"] 参数'
                            raise KeyError(_log)

                        used = False

                        if msg['guest_uuid'].__len__() == 36:
                            used = True

                        if used:
                            self.refresh_guest_mapping()

                            if msg['guest_uuid'] not in self.guest_mapping_by_uuid:

                                if config['DEBUG']:
                                    _log = u' '.join([u'uuid', msg['uuid'], u'在计算节点', self.hostname, u'中未找到.'])
                                    logger.debug(_log)
                                    log_emit.debug(_log)

                                raise RuntimeError('Resize disk failure, because the uuid ' + msg['guest_uuid'] +
                                                   ' not found in current domains.')

                            self.guest = self.guest_mapping_by_uuid[msg['guest_uuid']]
                            if not isinstance(self.guest, libvirt.virDomain):
                                raise RuntimeError('Resize disk failure, because the guest is not a domain.')

                        # 在线磁盘扩容
                        if used and self.guest.isActive():
                                if 'device_node' not in msg:
                                    _log = u'添加磁盘缺少 disk 或 disk["device_node|size"] 参数'
                                    raise KeyError(_log)

                                # 磁盘大小默认单位为KB,乘以两个 1024,使其单位达到GB
                                msg['size'] = int(msg['size']) * 1024 * 1024

                                if self.guest.blockResize(disk=msg['device_node'], size=msg['size']) != 0:
                                    raise RuntimeError('Online resize disk failure in blockResize method.')

                                Guest.quota(guest=self.guest, msg=msg)

                        # 离线磁盘扩容
                        else:
                            if not all([key in msg for key in ['storage_mode', 'dfs_volume', 'image_path']]):
                                _log = u'添加磁盘缺少 disk 或 disk["storage_mode|dfs_volume|image_path|size"] 参数'
                                raise KeyError(_log)

                            if msg['storage_mode'] == StorageMode.glusterfs.value:
                                if not Disk.resize_qemu_image_by_glusterfs(dfs_volume=msg['dfs_volume'],
                                                                           image_path=msg['image_path'],
                                                                           size=msg['size']):
                                    raise RuntimeError('Offline resize disk failure with glusterfs.')

                            elif msg['storage_mode'] in [StorageMode.local.value, StorageMode.shared_mount.value]:
                                if not Disk.resize_qemu_image_by_local(image_path=msg['image_path'], size=msg['size']):
                                    raise RuntimeError('Offline resize disk failure with local storage mode.')

                    elif msg['action'] == 'quota':
                        self.refresh_guest_mapping()
                        if msg['guest_uuid'] not in self.guest_mapping_by_uuid:

                            if config['DEBUG']:
                                _log = u' '.join([u'uuid', msg['guest_uuid'], u'在计算节点', self.hostname, u'中未找到.'])
                                logger.debug(_log)
                                log_emit.debug(_log)

                            raise RuntimeError('Disk quota failure, because the uuid ' + msg['guest_uuid'] +
                                               ' not found in current domains.')

                        self.guest = self.guest_mapping_by_uuid[msg['guest_uuid']]
                        if not isinstance(self.guest, libvirt.virDomain):
                            raise RuntimeError('Disk quota failure, because the guest is not a domain.')

                        if not self.guest.isActive():
                            _log = u'磁盘 ' + msg['uuid'] + u' 所属虚拟机未处于活动状态。'
                            logger.warning(_log)
                            log_emit.warn(_log)
                            continue

                        Guest.quota(guest=self.guest, msg=msg)

                elif msg['_object'] == 'snapshot':

                    self.refresh_guest_mapping()
                    if msg['uuid'] not in self.guest_mapping_by_uuid:

                        if config['DEBUG']:
                            _log = u' '.join([u'uuid', msg['uuid'], u'在计算节点', self.hostname, u'中未找到.'])
                            logger.debug(_log)
                            log_emit.debug(_log)

                            raise RuntimeError('Snapshot ' + msg['action'] + ' failure, because the uuid ' +
                                               msg['uuid'] + ' not found in current domains.')

                    self.guest = self.guest_mapping_by_uuid[msg['uuid']]

                    if not isinstance(self.guest, libvirt.virDomain):
                        raise RuntimeError('Snapshot ' + msg['action'] + ' failure, because the guest is not a domain.')

                    if msg['action'] == 'create':

                        t = threading.Thread(target=Guest.create_snapshot, args=(self.guest, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'delete':

                        t = threading.Thread(target=Guest.delete_snapshot, args=(self.guest, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'revert':

                        t = threading.Thread(target=Guest.revert_snapshot, args=(self.guest, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'convert':

                        t = threading.Thread(target=Guest.convert_snapshot, args=(msg,))
                        t.setDaemon(False)
                        t.start()
                        continue

                elif msg['_object'] == 'os_template_image':

                    if msg['action'] == 'delete':
                        if msg['storage_mode'] == StorageMode.glusterfs.value:
                            Guest.dfs_volume = msg['dfs_volume']
                            Guest.init_gfapi()

                            try:
                                Guest.gf.remove(msg['template_path'])
                            except OSError:
                                pass

                        elif msg['storage_mode'] in [StorageMode.local.value, StorageMode.shared_mount.value]:
                            try:
                                os.remove(msg['template_path'])
                            except OSError:
                                pass

                elif msg['_object'] == 'global':
                    if msg['action'] == 'refresh_guest_state':
                        host_use_for_refresh_guest_state = Host()
                        t = threading.Thread(target=host_use_for_refresh_guest_state.refresh_guest_state, args=())
                        t.setDaemon(False)
                        t.start()
                        continue

                else:
                    _log = u'未支持的 _object:' + msg['_object']
                    logger.error(_log)
                    log_emit.error(_log)

                response_emit.success(_object=msg['_object'], action=msg['action'], uuid=msg['uuid'],
                                      data=extend_data, passback_parameters=msg.get('passback_parameters'))

            except redis.exceptions.ConnectionError as e:
                logger.error(traceback.format_exc())
                # 防止循环线程,在redis连接断开时,混水写入日志
                time.sleep(5)

            except:
                # 防止循环线程,在redis连接断开时,混水写入日志
                time.sleep(5)
                logger.error(traceback.format_exc())
                log_emit.error(traceback.format_exc())
                response_emit.failure(_object=msg['_object'], action=msg.get('action'), uuid=msg.get('uuid'),
                                      passback_parameters=msg.get('passback_parameters'))
Beispiel #7
0
    def convert_snapshot(msg=None):

        pattern_progress = re.compile(r'\((\d+(\.\d+)?)/100%\)')

        extend_data = dict()

        try:
            assert isinstance(msg, dict)

            snapshot_path = msg['snapshot_path']
            template_path = msg['template_path']

            if msg['storage_mode'] == StorageMode.glusterfs.value:

                storage = Storage(storage_mode=msg['storage_mode'],
                                  dfs_volume=msg['dfs_volume'])

                if not storage.gf.isdir(os.path.dirname(template_path)):
                    storage.gf.makedirs(os.path.dirname(template_path), 0755)

                snapshot_path = '/'.join(
                    ['gluster://127.0.0.1', msg['dfs_volume'], snapshot_path])
                template_path = '/'.join(
                    ['gluster://127.0.0.1', msg['dfs_volume'], template_path])

            cmd = ' '.join([
                '/usr/bin/qemu-img', 'convert', '--force-share', '-O', 'qcow2',
                '-s', msg['snapshot_id'], snapshot_path, template_path
            ])

            qemu_img_convert = subprocess.Popen(cmd,
                                                shell=True,
                                                stdout=subprocess.PIPE,
                                                stderr=subprocess.STDOUT)

            fcntl.fcntl(
                qemu_img_convert.stdout, fcntl.F_SETFL,
                fcntl.fcntl(qemu_img_convert.stdout, fcntl.F_GETFL)
                | os.O_NONBLOCK)

            while qemu_img_convert.returncode is None:
                line = None

                try:
                    line = qemu_img_convert.stdout.readline()
                except IOError as e:
                    pass

                if line is not None:
                    p = pattern_progress.match(line.strip())

                    if p is not None:
                        fields = p.groups()
                        guest_event_emit.snapshot_converting(
                            uuid=msg['uuid'],
                            os_template_image_id=msg['os_template_image_id'],
                            progress=int(fields[0].split('.')[0]))

                time.sleep(0.5)
                qemu_img_convert.send_signal(signal.SIGUSR1)
                qemu_img_convert.poll()

            if qemu_img_convert.returncode != 0:
                raise CommandExecFailed(u'创建自定义模板失败,命令执行退出异常。')

            response_emit.success(
                _object=msg['_object'],
                action=msg['action'],
                uuid=msg['uuid'],
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))

        except:
            log_emit.error(traceback.format_exc())
            response_emit.failure(
                _object=msg['_object'],
                action=msg.get('action'),
                uuid=msg.get('uuid'),
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))
Beispiel #8
0
    def create(conn, msg):
        try:
            guest = Guest(uuid=msg['uuid'],
                          name=msg['name'],
                          template_path=msg['template_path'],
                          disk=msg['disks'][0],
                          xml=msg['xml'],
                          storage_mode=msg['storage_mode'],
                          dfs_volume=msg['dfs_volume'])

            q_creating_guest.put({
                'storage_mode': guest.storage.storage_mode,
                'dfs_volume': guest.storage.dfs_volume,
                'uuid': guest.uuid,
                'template_path': guest.template_path,
                'system_image_path': guest.system_image_path
            })

            guest.generate_system_image()

            dom = guest.define_by_xml(conn=conn)
            assert isinstance(dom, libvirt.virDomain)

            log = u' '.join(
                [u'域', guest.name, u', UUID', guest.uuid, u'定义成功.'])
            log_emit.info(msg=log)

            guest_event_emit.creating(uuid=guest.uuid, progress=92)

            disk_info = guest.storage.image_info(path=guest.system_image_path)

            # 由该线程最顶层的异常捕获机制,处理其抛出的异常
            guest.execute_os_template_initialize_operates(
                dom=conn.lookupByUUIDString(uuidstr=guest.uuid),
                os_template_initialize_operates=msg[
                    'os_template_initialize_operates'],
                os_type=msg['os_type'])

            extend_data = dict()
            extend_data.update({'disk_info': disk_info})

            guest_event_emit.creating(uuid=guest.uuid, progress=97)

            dom.create()
            log = u' '.join(
                [u'域', guest.name, u', UUID', guest.uuid, u'启动成功.'])
            log_emit.info(msg=log)

            Guest.quota(dom=dom, msg=msg)

            response_emit.success(
                _object=msg['_object'],
                action=msg['action'],
                uuid=msg['uuid'],
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))

        except:
            log_emit.error(traceback.format_exc())
            response_emit.failure(
                _object=msg['_object'],
                action=msg.get('action'),
                uuid=msg.get('uuid'),
                passback_parameters=msg.get('passback_parameters'))
Beispiel #9
0
    def create(cls, conn, msg):

        try:
            Guest.storage_mode = msg['storage_mode']

            guest = Guest(uuid=msg['uuid'],
                          name=msg['name'],
                          template_path=msg['template_path'],
                          disk=msg['disks'][0],
                          xml=msg['xml'])

            if Guest.storage_mode == StorageMode.glusterfs.value:
                Guest.dfs_volume = msg['dfs_volume']
                Guest.init_gfapi()

            guest.system_image_path = guest.disk['path']

            q_creating_guest.put({
                'storage_mode': Guest.storage_mode,
                'dfs_volume': Guest.dfs_volume,
                'uuid': guest.uuid,
                'template_path': guest.template_path,
                'system_image_path': guest.system_image_path
            })

            if not guest.generate_system_image():
                raise RuntimeError('System image generate failure.')

            if not guest.define_by_xml(conn=conn):
                raise RuntimeError(
                    'Define the instance of virtual machine by xml failure.')

            guest_event_emit.creating(uuid=guest.uuid, progress=92)

            disk_info = dict()

            if Guest.storage_mode == StorageMode.glusterfs.value:
                disk_info = Disk.disk_info_by_glusterfs(
                    dfs_volume=guest.dfs_volume,
                    image_path=guest.system_image_path)

            elif Guest.storage_mode in [
                    StorageMode.local.value, StorageMode.shared_mount.value
            ]:
                disk_info = Disk.disk_info_by_local(
                    image_path=guest.system_image_path)

            # 由该线程最顶层的异常捕获机制,处理其抛出的异常
            guest.execute_os_template_initialize_operates(
                guest=conn.lookupByUUIDString(uuidstr=guest.uuid),
                os_template_initialize_operates=msg[
                    'os_template_initialize_operates'],
                os_type=msg['os_type'])

            extend_data = dict()
            extend_data.update({'disk_info': disk_info})

            guest_event_emit.creating(uuid=guest.uuid, progress=97)

            if not guest.start_by_uuid(conn=conn):
                raise RuntimeError(
                    'Start the instance of virtual machine by uuid failure.')

            cls.quota(guest=conn.lookupByUUIDString(uuidstr=guest.uuid),
                      msg=msg)

            response_emit.success(
                _object=msg['_object'],
                action=msg['action'],
                uuid=msg['uuid'],
                data=extend_data,
                passback_parameters=msg.get('passback_parameters'))

        except:
            logger.error(traceback.format_exc())
            log_emit.error(traceback.format_exc())
            response_emit.failure(
                _object=msg['_object'],
                action=msg.get('action'),
                uuid=msg.get('uuid'),
                passback_parameters=msg.get('passback_parameters'))
Beispiel #10
0
                # https://qemu.weilnetz.de/doc/qemu-ga-ref.html#index-guest_002dset_002dtime
                try:
                    libvirt_qemu.qemuAgentCommand(guest, json.dumps({
                            'execute': 'guest-set-time',
                            'arguments': {
                                'time': int(ji.Common.ts() * (10**9))
                            }
                        }),
                        3,
                        libvirt_qemu.VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT)

                except libvirt.libvirtError, e:
                    logger.error(e.message)
                    log_emit.error(e.message)

            response_emit.success(_object=msg['_object'], action=msg['action'], uuid=msg['uuid'],
                                  data=extend_data, passback_parameters=msg.get('passback_parameters'))

        except:
            logger.error(traceback.format_exc())
            log_emit.error(traceback.format_exc())
            response_emit.failure(_object=msg['_object'], action=msg.get('action'), uuid=msg.get('uuid'),
                                  data=extend_data, passback_parameters=msg.get('passback_parameters'))

    @staticmethod
    def convert_snapshot(msg=None):

        pattern_progress = re.compile(r'\((\d+(\.\d+)?)/100%\)')

        extend_data = dict()

        try:
Beispiel #11
0
    def instruction_process_engine(self):

        ps = r.pubsub(ignore_subscribe_messages=False)
        ps.subscribe(config['instruction_channel'])

        while True:
            if Utils.exit_flag:
                msg = 'Thread instruction_process_engine say bye-bye'
                print msg
                logger.info(msg=msg)
                return

            threads_status['instruction_process_engine'] = {
                'timestamp': ji.Common.ts()
            }

            msg = dict()
            extend_data = dict()

            try:
                msg = ps.get_message(timeout=config['engine_cycle_interval'])

                if msg is None or 'data' not in msg or not isinstance(
                        msg['data'], basestring):
                    continue

                try:
                    msg = json.loads(msg['data'])

                    if msg['action'] == 'pong':
                        continue

                    if msg['action'] == 'ping':
                        # 通过 ping pong 来刷存在感。因为经过实际测试发现,当订阅频道长时间没有数据来往,那么订阅者会被自动退出。
                        r.publish(config['instruction_channel'],
                                  message=json.dumps({'action': 'pong'}))
                        continue

                except ValueError as e:
                    log_emit.error(e.message)
                    continue

                if 'node_id' in msg and int(msg['node_id']) != self.node_id:
                    continue

                # 下列语句繁琐写法如 <code>if '_object' not in msg or 'action' not in msg:</code>
                if not all([key in msg for key in ['_object', 'action']]):
                    continue

                logger.info(msg=msg)
                if msg['_object'] == 'guest':

                    self.refresh_dom_mapping()
                    if msg['action'] not in ['create']:
                        self.dom = self.dom_mapping_by_uuid[msg['uuid']]
                        assert isinstance(self.dom, libvirt.virDomain)

                    if msg['action'] == 'create':
                        t = threading.Thread(target=Guest.create,
                                             args=(self.conn, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'reboot':
                        Guest.reboot(dom=self.dom)

                    elif msg['action'] == 'force_reboot':
                        Guest.force_reboot(dom=self.dom, msg=msg)

                    elif msg['action'] == 'shutdown':
                        Guest.shutdown(dom=self.dom)

                    elif msg['action'] == 'force_shutdown':
                        Guest.force_shutdown(dom=self.dom)

                    elif msg['action'] == 'boot':
                        Guest.boot(dom=self.dom, msg=msg)

                    elif msg['action'] == 'suspend':
                        Guest.suspend(dom=self.dom)

                    elif msg['action'] == 'resume':
                        Guest.resume(dom=self.dom)

                    elif msg['action'] == 'delete':
                        Guest.delete(dom=self.dom, msg=msg)

                    elif msg['action'] == 'reset_password':
                        Guest.reset_password(dom=self.dom, msg=msg)

                    elif msg['action'] == 'attach_disk':
                        Guest.attach_disk(dom=self.dom, msg=msg)

                    elif msg['action'] == 'detach_disk':
                        Guest.detach_disk(dom=self.dom, msg=msg)

                    elif msg['action'] == 'update_ssh_key':
                        Guest.update_ssh_key(dom=self.dom, msg=msg)

                    elif msg['action'] == 'allocate_bandwidth':
                        t = threading.Thread(target=Guest.allocate_bandwidth,
                                             args=(self.dom, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'adjust_ability':
                        t = threading.Thread(target=Guest.adjust_ability,
                                             args=(self.dom, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'migrate':
                        Guest().migrate(dom=self.dom, msg=msg)

                elif msg['_object'] == 'disk':

                    if msg['action'] == 'create':
                        Storage(storage_mode=msg['storage_mode'],
                                dfs_volume=msg['dfs_volume']).make_image(
                                    path=msg['image_path'], size=msg['size'])

                    elif msg['action'] == 'delete':
                        Storage(storage_mode=msg['storage_mode'],
                                dfs_volume=msg['dfs_volume']).delete_image(
                                    path=msg['image_path'])

                    elif msg['action'] == 'resize':
                        mounted = True if msg['guest_uuid'].__len__(
                        ) == 36 else False

                        if mounted:
                            self.refresh_dom_mapping()
                            self.dom = self.dom_mapping_by_uuid[
                                msg['guest_uuid']]

                        # 在线磁盘扩容
                        if mounted and self.dom.isActive():
                            # 磁盘大小默认单位为KB,乘以两个 1024,使其单位达到 GiB
                            msg['size'] = int(msg['size']) * 1024 * 1024

                            # https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainBlockResize
                            self.dom.blockResize(disk=msg['device_node'],
                                                 size=msg['size'])
                            Guest.quota(dom=self.dom, msg=msg)

                        # 离线磁盘扩容
                        else:
                            Storage(storage_mode=msg['storage_mode'],
                                    dfs_volume=msg['dfs_volume']).resize_image(
                                        path=msg['image_path'],
                                        size=msg['size'])

                    elif msg['action'] == 'quota':
                        self.refresh_dom_mapping()
                        self.dom = self.dom_mapping_by_uuid[msg['guest_uuid']]
                        Guest.quota(dom=self.dom, msg=msg)

                elif msg['_object'] == 'snapshot':

                    self.refresh_dom_mapping()
                    self.dom = self.dom_mapping_by_uuid[msg['uuid']]

                    if msg['action'] == 'create':
                        t = threading.Thread(target=Guest.create_snapshot,
                                             args=(self.dom, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'delete':
                        t = threading.Thread(target=Guest.delete_snapshot,
                                             args=(self.dom, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'revert':
                        t = threading.Thread(target=Guest.revert_snapshot,
                                             args=(self.dom, msg))
                        t.setDaemon(False)
                        t.start()
                        continue

                    elif msg['action'] == 'convert':
                        t = threading.Thread(target=Guest.convert_snapshot,
                                             args=(msg, ))
                        t.setDaemon(False)
                        t.start()
                        continue

                elif msg['_object'] == 'os_template_image':
                    if msg['action'] == 'delete':
                        Storage(storage_mode=msg['storage_mode'],
                                dfs_volume=msg['dfs_volume']).delete_image(
                                    path=msg['template_path'])

                elif msg['_object'] == 'global':
                    if msg['action'] == 'refresh_guest_state':
                        t = threading.Thread(target=Host().refresh_guest_state,
                                             args=())
                        t.setDaemon(False)
                        t.start()
                        continue

                    if msg['action'] == 'upgrade':
                        try:
                            log = self.upgrade(msg['url'])
                            log_emit.info(msg=log)

                        except subprocess.CalledProcessError as e:
                            log_emit.warn(e.output)
                            self.rollback()
                            continue

                        log = self.restart()
                        log_emit.info(msg=log)

                    if msg['action'] == 'restart':
                        log = self.restart()
                        log_emit.info(msg=log)

                else:
                    err = u'未支持的 _object:' + msg['_object']
                    log_emit.error(err)

                response_emit.success(
                    _object=msg['_object'],
                    action=msg['action'],
                    uuid=msg['uuid'],
                    data=extend_data,
                    passback_parameters=msg.get('passback_parameters'))

            except KeyError as e:
                log_emit.warn(e.message)
                if msg['_object'] == 'guest':
                    if msg['action'] == 'delete':
                        response_emit.success(
                            _object=msg['_object'],
                            action=msg['action'],
                            uuid=msg['uuid'],
                            data=extend_data,
                            passback_parameters=msg.get('passback_parameters'))

            except:
                # 防止循环线程,在redis连接断开时,混水写入日志
                time.sleep(5)
                log_emit.error(traceback.format_exc())
                response_emit.failure(
                    _object=msg['_object'],
                    action=msg.get('action'),
                    uuid=msg.get('uuid'),
                    passback_parameters=msg.get('passback_parameters'))