Example #1
0
class VmAPI(object):
    def __init__(self,
                 manager=None,
                 user_api=None,
                 network_api=None,
                 image_api=None,
                 group_api=None,
                 host_api=None):
        if not manager:
            self.manager = VMManager()
        else:
            self.manager = manager
        if not user_api:
            self.user_api = UserAPI()
        else:
            self.user_api = user_api
        if not network_api:
            self.network_api = NetworkAPI()
        else:
            self.network_api = network_api
        if not image_api:
            self.image_api = ImageAPI()
        else:
            self.image_api = image_api
        if not group_api:
            self.group_api = GroupAPI()
        else:
            self.group_api = group_api
        if not host_api:
            self.host_api = HostAPI()
        else:
            self.host_api = host_api

    def get_vm_by_uuid(self, vm_uuid):
        return self.manager.get_vm_by_uuid(vm_uuid)

    def get_vm_list_by_group_id(self,
                                group_id,
                                host_id=None,
                                order=None,
                                ha_monitored=None):
        return self.manager.get_vm_list_by_group_id(group_id, host_id, order,
                                                    ha_monitored)

    def vm_uuid_exists(self, vm_uuid):
        return self.manager.vm_uuid_exists(vm_uuid)

    def attach_device(self, vm_uuid, xml):
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        return vm.attach_device(xml)

    def detach_device(self, vm_uuid, xml):
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        return vm.detach_device(xml)

    def set_creator(self, vm_uuid, username):
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        return vm.set_creator(str(username))

    def set_ha_monitored(self, vm_uuid, ha_monitored=True):
        '''设置虚拟机的[高可用]属性'''
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        return vm.set_ha_monitored(ha_monitored)

    def create_vm(self,
                  image_id,
                  vcpu,
                  mem,
                  group_id=None,
                  host_id=None,
                  net_type_id=None,
                  vlan_id=None,
                  diskname=None,
                  vm_uuid=None,
                  remarks=None,
                  ipv4=None):
        '''
        参数说明:
            image_id, vcpu, mem 为必要参数。
            group_id, host_id 两者至少必须有其一, host_id存在时指定相应宿主机,
            无host_id但有group_id时在指定集群筛选宿主机。
            net_type_id, vlan_id 两者至少必须有其一,有vlan_id时使用指定的网段,
            无vlan_id但有net_type_id时在指定网络类型中筛选网段。
            其他参数都是可选参数。
        '''
        vcpu = int(vcpu)
        mem = int(mem)

        if not vm_uuid:
            vm_uuid = self.manager.create_vm_uuid()
        if not diskname:
            diskname = vm_uuid

        image = self.image_api.get_image_by_id(image_id)
        if not image.enable:
            raise Error(ERR_IMAGE_ID)

        image_info = self.image_api.get_image_info_by_id(image_id)

        if host_id:
            host_list = [self.host_api.get_host_by_id(host_id)]
        elif group_id:
            host_list = self.host_api.get_host_list_by_group_id(group_id)
        else:
            raise Error(ERR_VM_CREATE_ARGS_HOST)

        available_host_list = []
        for host in host_list:
            if host.enable and not host.exceed_vm_limit(
            ) and not host.exceed_mem_limit(mem):
                available_host_list.append(host)

        if not available_host_list:
            raise Error(ERR_HOST_FILTER_NONE)

        if settings.DEBUG: print('[create_vm]', '可用主机列表', available_host_list)

        if vlan_id:
            vlan_list = [self.network_api.get_vlan_by_id(vlan_id)]
        elif net_type_id:
            vlan_list = self.network_api.get_vlan_list_by_type_id(net_type_id)
        else:
            raise Error(ERR_VM_CREATE_ARGS_VLAN)

        available_vlan_list = []
        for vlan in vlan_list:
            if vlan.enable and vlan.has_free_ip():
                available_vlan_list.append(vlan)

        if not available_vlan_list:
            raise Error(ERR_VLAN_FILTER_NONE)

        if settings.DEBUG: print('[create_vm]', '网段列表', vlan_list)

        while available_host_list:
            mac_claimed = False
            disk_created = False
            vm_db = None

            host = self.host_api.host_filter(available_host_list,
                                             vcpu,
                                             mem,
                                             claim=True)
            if not host:
                raise Error(ERR_VM_HOST_FILTER)
            available_host_list.remove(host)

            if not host.alive():
                raise Error(ERR_HOST_CONNECTION)

            host_available_vlan_id_list = self.host_api.get_vlan_id_list_of_host(
                host.id)
            if not host_available_vlan_id_list:
                raise Error(ERR_VLAN_FILTER_NONE)

            for vlan in available_vlan_list:
                try:
                    if not vlan.id in host_available_vlan_id_list:
                        continue
                    if settings.DEBUG: print('[create_vm]', '开始创建', host, vlan)
                    mac_claimed = False
                    disk_created = False

                    mac = self.network_api.mac_claim(vlan.id,
                                                     vm_uuid,
                                                     ipv4=ipv4)
                    if mac:
                        mac_claimed = True

                    if mac_claimed:
                        if self.image_api.init_disk(image_id, diskname):
                            disk_created = True
                        else:
                            raise Error(ERR_VM_CREATE_DISK)
                        # image_info = self.image_api.get_image_info_by_id(image_id)

                        xml_tpl = self.image_api.get_xml_tpl(image_id)
                        print(xml_tpl)
                        xml_desc = xml_tpl % {
                            'name': vm_uuid,
                            'uuid': vm_uuid,
                            'mem': mem,
                            'vcpu': vcpu,
                            'ceph_uuid': image_info['ceph_uuid'],
                            'ceph_pool': image_info['ceph_pool'],
                            'diskname': diskname,
                            'ceph_host': image_info['ceph_host'],
                            'ceph_port': image_info['ceph_port'],
                            'ceph_username': image_info['ceph_username'],
                            'ceph_hosts_xml': image_info['ceph_hosts_xml'],
                            'mac': mac,
                            'bridge': vlan.br
                        }
                        print(xml_desc)
                        net_info = self.network_api.get_net_info_by_vmuuid(
                            vm_uuid)

                        vm_db = self.manager.create_vm_db({
                            'host_id':
                            host.id,
                            'image_id':
                            image_id,
                            'image_snap':
                            image_info['image_snap'],
                            'image':
                            image_info['image_name'],
                            'uuid':
                            vm_uuid,
                            'name':
                            vm_uuid,
                            'mem':
                            mem,
                            'vcpu':
                            vcpu,
                            'disk':
                            diskname,
                            'remarks':
                            remarks,
                            'ceph_id':
                            image_info['ceph_id'],
                            'ceph_host':
                            image_info['ceph_host'],
                            'ceph_pool':
                            image_info['ceph_pool'],
                            'ceph_uuid':
                            image_info['ceph_uuid'],
                            'ceph_port':
                            image_info['ceph_port'],
                            'ceph_username':
                            image_info['ceph_username'],
                            'vlan_id':
                            net_info['vlan_id'],
                            'vlan_name':
                            net_info['vlan_name'],
                            'ipv4':
                            net_info['ipv4'],
                            'mac':
                            net_info['mac'],
                            'br':
                            net_info['br']
                        })

                        if vm_db:
                            dom = self.manager.define(host.ipv4, xml_desc)
                            return VM(vm_db)
                        else:
                            raise Error(ERR_VM_CREATE_DB)
                except Error as e:
                    if settings.DEBUG:
                        print('[create_vm]', '创建失败', 'mac_claimed',
                              mac_claimed, 'disk_created', disk_created)

                    if mac_claimed:
                        mac_released = self.network_api.mac_release(
                            mac, vm_uuid)
                        if settings.DEBUG:
                            print('[create_vm]', 'IP地址释放', mac_released)
                    if disk_created:
                        disk_deleted = self.image_api.rm_disk(
                            image_id, diskname)
                        if settings.DEBUG:
                            print('[create_vm]', '磁盘释放', disk_deleted)
                    if vm_db:
                        host_released = self.host_api.host_release(
                            host, vcpu, mem)
                        if settings.DEBUG:
                            print('[create_vm]', '释放宿主机资源', host_released)
                        vm_db_deleted = vm_db.delete()
                        if settings.DEBUG:
                            print('[create_vm]', '删除数据库记录', vm_db_deleted)
                    if settings.DEBUG: print(e)
                    raise e
                except Exception as e:
                    if settings.DEBUG:
                        print('[create_vm]', '创建失败', 'mac_claimed',
                              mac_claimed, 'disk_created', disk_created)

                    if mac_claimed:
                        mac_released = self.network_api.mac_release(
                            mac, vm_uuid)
                        if settings.DEBUG:
                            print('[create_vm]', 'IP地址释放', mac_released)
                    if disk_created:
                        disk_deleted = self.image_api.rm_disk(
                            image_id, diskname)
                        if settings.DEBUG:
                            print('[create_vm]', '磁盘释放', disk_deleted)
                    if vm_db:
                        host_released = self.host_api.host_release(
                            host, vcpu, mem)
                        if settings.DEBUG:
                            print('[create_vm]', '释放宿主机资源', host_released)
                        vm_db_deleted = vm_db.delete()
                        if settings.DEBUG:
                            print('[create_vm]', '删除数据库记录', vm_db_deleted)
                    if settings.DEBUG: print(e)

        return False

    def delete_vm(self, vm_uuid, force=False):
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        image_id = vm.image_id
        diskname = vm.disk
        host_id = vm.host_id
        vcpu = vm.vcpu
        mem = vm.mem
        mac = vm.mac
        ceph_pool_id = vm.ceph_id

        from device.api import GPUAPI
        from volume.api import VolumeAPI
        archive_disk_name = ''
        try:
            gpuapi = GPUAPI()
            if len(gpuapi.get_gpu_list_by_vm_uuid(vm_uuid)) > 0:
                raise Error(ERR_VM_DEL_GPU_MOUNTED)

            volumeapi = VolumeAPI()
            if len(volumeapi.get_volume_list_by_vm_uuid(vm_uuid)) > 0:
                raise Error(ERR_VM_DEL_VOL_MOUNTED)

            deletion_permitted = False

            if self.image_api.disk_exists(image_id,
                                          diskname,
                                          cephpool_id=ceph_pool_id):
                archive_disk_name = self.image_api.archive_disk(
                    image_id, diskname, cephpool_id=ceph_pool_id)
                if archive_disk_name != False:
                    deletion_permitted = True
            else:
                deletion_permitted = True
        except Exception as e:
            if not force:  # 不强制删除的话抛出异常
                raise e
            else:  # 强制删除,不管其他操作是否成功,都删除虚拟机记录
                deletion_permitted = True

        if deletion_permitted:
            if vm.delete(archive_disk_name, force=force):
                if not self.host_api.host_release(host_id, vcpu, mem):
                    print('[delete_vm]', '释放宿主机资源失败')
                if not self.network_api.mac_release(mac, vm_uuid):
                    print('[delete_vm]', '释放IP地址失败')
                return True
        return False

    # def reset_vm(self, vm_uuid):
    #     vm = self.manager.get_vm_by_uuid(vm_uuid)
    #     if vm.is_running():
    #         raise Error(ERR_VM_RESET_LIVING)
    #     archive_disk_name = self.image_api.archive_disk(vm.image_id, vm.disk)
    #     if archive_disk_name != False:
    #         init_disk_success = self.image_api.init_disk(vm.image_id, vm.disk)
    #         if init_disk_success:
    #             if vm.start():
    #                 return True
    #             self.image_api.rm_disk(vm.image_id, vm.disk)
    #         self.image_api.restore_disk(vm.image_id, archive_disk_name)
    #     return False

    def edit_vm(self, vm_uuid, vcpu=None, mem=None, remarks=None):
        vm = self.get_vm_by_uuid(vm_uuid)

        if (vcpu != None or mem != None) and vm.is_running():
            raise Error(ERR_VM_EDIT_LIVING)

        if vcpu and not type(vcpu) == int:
            raise Error(ERR_VM_VCPU)

        if mem and not type(mem) == int:
            raise Error(ERR_VM_MEM)

        failed = False
        org_remarks = vm.remarks
        org_vcpu = vm.vcpu
        org_mem = vm.mem

        if not failed and remarks:
            if not vm.set_remarks(remarks):
                failed = True

        if not failed and vcpu:
            if vcpu > vm.vcpu:
                vcpu_claimed = self.host_api.host_claim(
                    vm.host_id, vcpu - vm.vcpu, 0, 0)
            else:
                vcpu_claimed = self.host_api.host_release(
                    vm.host_id, vm.vcpu - vcpu, 0, 0)
        else:
            vcpu_claimed = True

        if not failed and mem:
            if mem > vm.mem:
                mem_claimed = self.host_api.host_claim(vm.host_id, 0,
                                                       mem - vm.mem, 0)
            else:
                mem_claimed = self.host_api.host_release(
                    vm.host_id, 0, vm.mem - mem, 0)
        else:
            mem_claimed = True

        if not failed and vcpu_claimed and mem_claimed:
            if not self.manager.set_vm_configuration(vm_uuid, vcpu, mem):
                failed = True

        if failed:
            if remarks:
                vm.set_remarks(org_remarks)
            if vcpu or mem:
                self.manager.set_vm_configuration(vm_uuid, org_vcpu, org_mem)
                if vcpu and vcpu_claimed:
                    if vcpu > org_vcpu:
                        self.host_api.host_release(vm.host_id, vcpu - org_vcpu,
                                                   0, 0)
                    else:
                        self.host_api.host_claim(vm.host_id, org_vcpu - vcpu,
                                                 0, 0)
                if mem and mem_claimed:
                    if mem > org_mem:
                        self.host_api.host_release(vm.host_id, 0,
                                                   mem - org_mem, 0)
                    else:
                        self.host_api.host_claim(vm.host_id, 0, org_mem - mem,
                                                 0)

            return False

        return True

    def migrate_vm(self, vm_uuid, host_id):
        #参数验证
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        host = self.host_api.get_host_by_id(host_id)
        src_host_alive = vm.is_host_connected

        from device.api import GPUAPI
        from volume.api import VolumeAPI
        gpuapi = GPUAPI()
        volumeapi = VolumeAPI()
        gpu_list = gpuapi.get_gpu_list_by_vm_uuid(vm_uuid)
        volume_list = volumeapi.get_volume_list_by_vm_uuid(vm_uuid)

        #判断是否在同一个center
        if vm.center_id != host.center_id:
            raise Error(ERR_VM_MIGRATE_DIFF_CEPH)

        #是否关机
        if vm.is_running():
            raise Error(ERR_VM_MIGRATE_LIVING)

        #在新宿主机上创建
        image_info = self.image_api.get_image_info_by_id(vm.image_id)

        xml_tpl = self.image_api.get_xml_tpl(vm.image_id)
        xml_desc = xml_tpl % {
            'name': vm_uuid,
            'uuid': vm_uuid,
            'mem': vm.mem,
            'vcpu': vm.vcpu,
            'ceph_uuid': image_info['ceph_uuid'],
            'ceph_pool': image_info['ceph_pool'],
            'diskname': vm.disk,
            'ceph_host': image_info['ceph_host'],
            'ceph_port': image_info['ceph_port'],
            'ceph_username': image_info['ceph_username'],
            'ceph_hosts_xml': image_info['ceph_hosts_xml'],
            'mac': vm.mac,
            'bridge': vm.br
        }

        migrate_res = False
        if self.host_api.host_claim(host_id, vm.vcpu, vm.mem, 1):
            try:
                if src_host_alive:
                    for gpu1 in gpu_list:
                        r1 = vm.detach_device(gpu1.xml_desc)
                        if settings.DEBUG:
                            print('[migrate_vm]', 'detach gpu ', gpu1.id, r1)
                    for volume1 in volume_list:
                        r1 = vm.detach_device(volume1.xml_desc)
                        if settings.DEBUG:
                            print('[migrate_vm]', 'detach volume ', volume1.id,
                                  r1)

                old_host_id = vm.host_id
                if self.manager.migrate(vm_uuid,
                                        host_id,
                                        host.ipv4,
                                        xml_desc,
                                        old_host_alive=src_host_alive):
                    migrate_res = True
                    old_res = self.host_api.host_release(
                        old_host_id, vm.vcpu, vm.mem, 1)
                    if settings.DEBUG:
                        print('[migrate_vm]', '释放原宿主机资源 ', old_res)
                    #重新attach device(只有云硬盘)
                    for volume1 in volume_list:
                        r1 = vm.attach_device(volume1.xml_desc)
                        if settings.DEBUG:
                            print('[migrate_vm]', 'attach volume ', volume1.id,
                                  r1)
            except Exception as e:
                if type(e) == Error:
                    raise e
            finally:
                if not migrate_res:
                    new_res = self.host_api.host_release(
                        host_id, vm.vcpu, vm.mem, 1)
                    if settings.DEBUG:
                        print('[migrate_vm]', '迁移失败,释放新宿主机资源 ', new_res)

        return migrate_res

    def reset_vm(self, vm_uuid, new_image_id=None):
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        if new_image_id == None:
            new_image_id = vm.image_id
        host = vm.host
        old_image_id = vm.image_id
        old_diskname = vm.disk
        old_ceph_pool_id = vm.ceph_id

        if vm.is_running():
            raise Error(ERR_VM_RESET_LIVING)

        new_image_info = self.image_api.get_image_info_by_id(new_image_id)
        if not new_image_info:
            raise Error(ERR_IMAGE_INFO)

        from device.api import GPUAPI
        from volume.api import VolumeAPI
        gpuapi = GPUAPI()
        volumeapi = VolumeAPI()
        gpu_list = gpuapi.get_gpu_list_by_vm_uuid(vm_uuid)
        volume_list = volumeapi.get_volume_list_by_vm_uuid(vm_uuid)

        archive_disk_name = ''
        old_image_dict = {
            'image_id': old_image_id,
            'image_snap': vm.image_snap,
            'image_name': vm.image,
            'ceph_id': vm.ceph_id,
            'ceph_host': vm.ceph_host,
            'ceph_pool': vm.ceph_pool,
            'ceph_uuid': vm.ceph_uuid,
            'ceph_port': vm.ceph_port,
            'ceph_username': vm.ceph_username
        }
        old_xml_desc = vm.xml_desc

        res = False
        try:
            #先detach设备(gpu,volume)
            for gpu1 in gpu_list:
                r1 = vm.detach_device(gpu1.xml_desc)
                if settings.DEBUG:
                    print('[reset_vm]', 'detach gpu ', gpu1.id, r1)
            for volume1 in volume_list:
                r1 = vm.detach_device(volume1.xml_desc)
                if settings.DEBUG:
                    print('[reset_vm]', 'detach volume ', volume1.id, r1)

            #dom的destroy和undefine操作
            dom_undefine_res = False
            if self.manager.domain_exists(vm.host_ipv4, vm.uuid):
                dom = self.manager.get_domain(vm.host_ipv4, vm.uuid)
                try:
                    dom.destroy()
                except:
                    pass
                dom.undefine()
            if not self.manager.domain_exists(vm.host_ipv4, vm.uuid):
                dom_undefine_res = True
            if dom_undefine_res:
                archive_disk_name = self.image_api.archive_disk(
                    old_image_id, vm.disk)

            if archive_disk_name:
                init_disk_success = False
                if dom_undefine_res:
                    init_disk_success = self.image_api.init_disk(
                        new_image_id, vm.disk)
                if init_disk_success:
                    #更新vm_db相关image信息
                    vm.db_obj.image_id = new_image_id
                    vm.db_obj.image_snap = new_image_info['image_snap']
                    vm.db_obj.image = new_image_info['image_name']
                    vm.db_obj.ceph_id = new_image_info['ceph_id']
                    vm.db_obj.ceph_host = new_image_info['ceph_host']
                    vm.db_obj.ceph_pool = new_image_info['ceph_pool']
                    vm.db_obj.ceph_uuid = new_image_info['ceph_uuid']
                    vm.db_obj.ceph_port = new_image_info['ceph_port']
                    vm.db_obj.ceph_username = new_image_info['ceph_username']

                    vm.db_obj.save()

                    xml_tpl = self.image_api.get_xml_tpl(new_image_id)
                    xml_desc = xml_tpl % {
                        'name': vm.uuid,
                        'uuid': vm.uuid,
                        'mem': vm.mem,
                        'vcpu': vm.vcpu,
                        'ceph_uuid': new_image_info['ceph_uuid'],
                        'ceph_pool': new_image_info['ceph_pool'],
                        'diskname': vm.disk,
                        'ceph_host': new_image_info['ceph_host'],
                        'ceph_port': new_image_info['ceph_port'],
                        'ceph_username': new_image_info['ceph_username'],
                        'ceph_hosts_xml': new_image_info['ceph_hosts_xml'],
                        'mac': vm.mac,
                        'bridge': vm.br
                    }
                    dom = self.manager.define(host.ipv4, xml_desc)
                    res = True
        except Exception as e:
            if settings.DEBUG: print('[reset_vm]', '重置镜像失败', e)
            res = False
            if archive_disk_name:  #已经归档成功,重新恢复
                self.image_api.restore_disk(vm.image_id, archive_disk_name)

                vm.db_obj.image_id = old_image_dict['image_id']
                vm.db_obj.image_snap = old_image_dict['image_snap']
                vm.db_obj.image = old_image_dict['image_name']
                vm.db_obj.ceph_id = old_image_dict['ceph_id']
                vm.db_obj.ceph_host = old_image_dict['ceph_host']
                vm.db_obj.ceph_pool = old_image_dict['ceph_pool']
                vm.db_obj.ceph_uuid = old_image_dict['ceph_uuid']
                vm.db_obj.ceph_port = old_image_dict['ceph_port']
                vm.db_obj.ceph_username = old_image_dict['ceph_username']
                vm.db_obj.save()

                dom = self.manager.define(host.ipv4, old_xml_desc)
            if settings.DEBUG: print('[reset_vm]', '回滚成功')
        finally:
            for gpu1 in gpu_list:
                r1 = vm.attach_device(gpu1.xml_desc)
                if settings.DEBUG:
                    print('[reset_vm]', 'attach gpu ', gpu1.id, r1)
            for volume1 in volume_list:
                r1 = vm.attach_device(volume1.xml_desc)
                if settings.DEBUG:
                    print('[reset_vm]', 'attach volume ', volume1.id, r1)

        return res

    def get_vm_disk_snap_list(self, vm_uuid):
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        return self.image_api.get_disk_snap_list_by_disk(vm.disk)

    def create_vm_disk_snap(self, vm_uuid, remarks=None):
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        res = self.image_api.create_disk_snap(vm.ceph_id,
                                              vm.disk,
                                              vm.image_id,
                                              vm_uuid,
                                              remarks=remarks)
        if res:
            return True
        else:
            return False

    def rollback_vm_disk_snap(self, vm_uuid, snap_id):
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        if vm.is_running():
            if settings.DEBUG: print('[rollback_vm_disk_snap]', '运行状态无法回滚')
            raise Error(ERR_VM_CREATE_SNAP_LIVING)
        if settings.DEBUG: print('[rollback_vm_disk_snap]', '快照回滚:开始执行')
        res = self.image_api.rollback_disk_snap(vm.disk, snap_id)
        if settings.DEBUG: print('[rollback_vm_disk_snap]', '快照回滚:结束', res)
        if res:
            return True
        else:
            return False

    def delete_vm_disk_snap(self, vm_uuid, snap_id):
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        res = self.image_api.delete_disk_snap_by_id(vm.disk, snap_id)
        if settings.DEBUG: print('[delete_vm_disk_snap]', '快照删除', res)
        if res:
            return True
        else:
            return False

    def set_vm_disk_snap_remarks(self, vm_uuid, snap_id, remarks=None):
        vm = self.manager.get_vm_by_uuid(vm_uuid)
        res = self.image_api.set_disk_snap_remarks(vm.disk, snap_id, remarks)
        if res:
            return True
        else:
            return False