Exemplo n.º 1
0
    def guest_state_report_engine(self):
        """
        Guest 状态上报引擎
        """
        guest_state_mapping = dict()

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

            try:
                # 3 秒钟更新一次
                time.sleep(config['engine_cycle_interval'] * 3)
                threads_status['guest_state_report_engine'] = {
                    'timestamp': ji.Common.ts()
                }
                self.refresh_dom_mapping()

                for uuid, dom in self.dom_mapping_by_uuid.items():
                    state = Guest.get_state(dom=dom)

                    if uuid in guest_state_mapping and guest_state_mapping[
                            uuid] == state:
                        continue

                    guest_state_mapping[uuid] = state
                    Guest.guest_state_report(dom=dom)

            except:
                log_emit.warn(traceback.format_exc())
Exemplo n.º 2
0
    def host_performance_collection_engine(self):
        while True:
            if Utils.exit_flag:
                msg = 'Thread host_performance_collection_engine say bye-bye'
                print msg
                logger.info(msg=msg)
                return

            try:
                time.sleep(config['engine_cycle_interval'])
                threads_status['host_performance_collection_engine'] = {
                    'timestamp': ji.Common.ts()
                }
                self.ts = ji.Common.ts()

                if self.ts % self.interval != 0:
                    continue

                self.update_interfaces()
                self.update_disks()
                self.host_cpu_memory_performance_report()
                self.host_traffic_performance_report()
                self.host_disk_usage_io_performance_report()

            except:
                log_emit.warn(traceback.format_exc())
Exemplo n.º 3
0
    def update_ssh_key(dom=None, msg=None):
        assert isinstance(dom, libvirt.virDomain)
        assert isinstance(msg, dict)

        if not dom.isActive():
            log = u'欲更新 SSH-KEY 的目标虚拟机未处于活动状态。'
            log_emit.warn(log)
            return

        from utils import QGA

        libvirt_qemu.qemuAgentCommand(
            dom,
            json.dumps({
                'execute': 'guest-exec',
                'arguments': {
                    'path': 'mkdir',
                    'capture-output': False,
                    'arg': ['-p', '/root/.ssh']
                }
            }), 3, libvirt_qemu.VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT)

        redirection_symbol = '>'

        ret_s = list()

        for i, ssh_key in enumerate(msg['ssh_keys']):
            if i > 0:
                redirection_symbol = '>>'

            exec_ret = libvirt_qemu.qemuAgentCommand(
                dom,
                json.dumps({
                    'execute': 'guest-exec',
                    'arguments': {
                        'path':
                        '/bin/sh',
                        'capture-output':
                        True,
                        'arg': [
                            '-c', ' '.join([
                                'echo', '"' + ssh_key + '"',
                                redirection_symbol,
                                '/root/.ssh/authorized_keys'
                            ])
                        ]
                    }
                }), 3, libvirt_qemu.VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT)

            exec_ret = json.loads(exec_ret)
            status_ret = QGA.get_guest_exec_status(
                dom=dom, pid=exec_ret['return']['pid'])
            # exec_ret_str = base64.b64decode(json.loads(status_ret)['return']['out-data'])
            # ret_s.append(json.loads(exec_ret_str))
            ret_s.append(status_ret)

        return ret_s
Exemplo n.º 4
0
    def refresh_guest_state(self):
        try:
            self.refresh_dom_mapping()

            for dom in self.dom_mapping_by_uuid.values():
                Guest.guest_state_report(dom=dom)

        except:
            log_emit.warn(traceback.format_exc())
Exemplo n.º 5
0
    def guest_creating_progress_report_engine():
        """
        Guest 创建进度上报引擎
        """

        list_creating_guest = list()
        template_size = dict()

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

            try:
                try:
                    payload = q_creating_guest.get(
                        timeout=config['engine_cycle_interval'])
                    list_creating_guest.append(payload)
                    q_creating_guest.task_done()
                except Queue.Empty as e:
                    pass

                # 当有 Guest 被创建时,略微等待一下,避免复制模板的动作还没开始,就开始计算进度。这样会产生找不到镜像路径的异常。
                time.sleep(1)

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

                for i, guest in enumerate(list_creating_guest):

                    template_path = guest['template_path']

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

                    if template_path not in template_size:
                        template_size[template_path] = float(
                            storage.getsize(path=template_path))

                    system_image_size = storage.getsize(
                        path=guest['system_image_path'])
                    progress = system_image_size / template_size[template_path]

                    guest_event_emit.creating(uuid=guest['uuid'],
                                              progress=int(progress * 90))

                    if progress >= 1:
                        del list_creating_guest[i]

            except:
                log_emit.warn(traceback.format_exc())
Exemplo n.º 6
0
    def clear_scene(self):

        if self.dirty_scene:
            self.dirty_scene = False

            if self.guest.gf.exists(self.guest.system_image_path):
                self.guest.gf.remove(self.guest.system_image_path)

            else:
                log = u'清理现场失败: 不存在的路径 --> ' + self.guest.guest_dir
                logger.warn(msg=log)
                log_emit.warn(msg=log)
Exemplo n.º 7
0
    def guest_state_report(dom=None):
        try:
            _uuid = dom.UUIDString()
            state = Guest.get_state(dom=dom)

            log = u' '.join([u'域', dom.name(), u', UUID', _uuid, u'的状态改变为'])

            if state == GuestState.running.value:
                log += u' Running。'
                guest_event_emit.running(uuid=_uuid)

            elif state == GuestState.booting.value:
                log += u' Booting。'
                guest_event_emit.booting(uuid=_uuid)

            elif state == GuestState.blocked.value:
                log += u' Blocked。'
                guest_event_emit.blocked(uuid=_uuid)

            elif state == GuestState.paused.value:
                log += u' Paused。'
                guest_event_emit.paused(uuid=_uuid)

            elif state == GuestState.shutdown.value:
                log += u' Shutdown。'
                guest_event_emit.shutdown(uuid=_uuid)

            elif state == GuestState.shutoff.value:
                log += u' Shutoff。'
                guest_event_emit.shutoff(uuid=_uuid)

            elif state == GuestState.crashed.value:
                log += u' Crashed。'
                guest_event_emit.crashed(uuid=_uuid)

            elif state == GuestState.pm_suspended.value:
                log += u' PM_Suspended。'
                guest_event_emit.pm_suspended(uuid=_uuid)

            else:
                log += u' NO_State。'

                guest_event_emit.no_state(uuid=_uuid)

            log_emit.info(msg=log)

        except Exception as e:
            log_emit.warn(e.message)
Exemplo n.º 8
0
    def host_state_report_engine(self):
        """
        计算节点状态上报引擎
        """

        # 首次启动时,做数据初始化
        self.update_interfaces()
        self.update_disks()
        boot_time = ji.Common.ts()

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

            try:
                time.sleep(config['engine_cycle_interval'])

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

                # 一分钟做一次更新
                if ji.Common.ts() % 60 == 0:
                    self.update_interfaces()
                    self.update_disks()

                host_event_emit.heartbeat(
                    message={
                        'node_id': self.node_id,
                        'cpu': self.cpu,
                        'cpuinfo': self.cpuinfo,
                        'memory': self.memory,
                        'dmidecode': self.dmidecode,
                        'interfaces': self.interfaces,
                        'disks': self.disks,
                        'system_load': os.getloadavg(),
                        'boot_time': boot_time,
                        'memory_available': psutil.virtual_memory().available,
                        'threads_status': threads_status,
                        'version': self.version
                    })

            except:
                log_emit.warn(traceback.format_exc())
Exemplo n.º 9
0
    def guest_performance_collection_engine(self):

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

            try:
                time.sleep(config['engine_cycle_interval'])
                threads_status['guest_performance_collection_engine'] = {
                    'timestamp': ji.Common.ts()
                }
                self.ts = ji.Common.ts()

                if self.ts % self.interval != 0:
                    continue

                if self.ts % 3600 == 0:
                    # 一小时做一次 垃圾回收 操作
                    for k, v in self.last_guest_cpu_time.items():
                        if (self.ts - v['timestamp']) > self.interval * 2:
                            del self.last_guest_cpu_time[k]

                    for k, v in self.last_guest_traffic.items():
                        if (self.ts - v['timestamp']) > self.interval * 2:
                            del self.last_guest_traffic[k]

                    for k, v in self.last_guest_disk_io.items():
                        if (self.ts - v['timestamp']) > self.interval * 2:
                            del self.last_guest_disk_io[k]

                self.refresh_dom_mapping()

                self.guest_cpu_memory_performance_report()
                self.guest_traffic_performance_report()
                self.guest_disk_io_performance_report()

            except:
                log_emit.warn(traceback.format_exc())
Exemplo n.º 10
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'))
Exemplo n.º 11
0
    def migrate(dom=None, msg=None):
        assert isinstance(dom, libvirt.virDomain)
        assert isinstance(msg, dict)

        # 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(dom.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 dom.isActive():
                err = u'非共享存储不支持离线迁移。'
                log_emit.warn(err)
                raise RuntimeError(
                    'Nonsupport offline migrate with storage of non sharing mode.'
                )

            ssh_client = Utils.ssh_client(hostname=msg['duri'].split('/')[2],
                                          user='******')

            for _disk in root.findall('devices/disk'):
                _file_path = _disk.find('source').get('file')
                disk_info = Storage.image_info_by_local(path=_file_path)
                disk_size = disk_info['virtual-size']
                stdin, stdout, stderr = ssh_client.exec_command(' '.join([
                    'qemu-img', 'create', '-f', 'qcow2', _file_path,
                    str(disk_size)
                ]))

                for line in stdout:
                    log_emit.info(line)

                for line in stderr:
                    log_emit.error(line)

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

            else:
                flags |= libvirt.VIR_MIGRATE_OFFLINE

        # duri like qemu+ssh://destination_host/system
        if dom.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.')
Exemplo n.º 12
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'))