diff --git a/.gitignore b/.gitignore index 7c9d0f2ebe2b0f9c6cf7986c8a2ec63695701469..0e052ff83a67a8892f09aa357b01f78ee7c80b43 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ share/python-wheels/ .installed.cfg *.egg MANIFEST +.DS_Store # PyInstaller # Usually these files are written by a python script from a template diff --git a/etc/.DS_Store b/etc/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8cd44faa73ca32b3980f166a74c56d9b17e076bd Binary files /dev/null and b/etc/.DS_Store differ diff --git a/etc/bin/.DS_Store b/etc/bin/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c420a9f7e7adf7bdbd5c5621ba1585807ca01a56 Binary files /dev/null and b/etc/bin/.DS_Store differ diff --git a/etc/eulerlauncher.conf b/etc/eulerlauncher.conf index a101a510a7e4508fa09797c0573d0299cf152f04..4c70880acb27c4705d43479772b93e552e60e7dd 100644 --- a/etc/eulerlauncher.conf +++ b/etc/eulerlauncher.conf @@ -1,8 +1,8 @@ -[default] -log_dir = -work_dir = -wget_dir = -qemu_dir = +[default] +log_dir = /Users/dengzhihang/Documents/workplace/eulerlauncher/log_dir +work_dir = /Users/dengzhihang/Documents/workplace/eulerlauncher/work_dir +wget_dir = /opt/homebrew/bin/wget +qemu_dir = /opt/homebrew/bin/qemu-system-aarch64 debug = True [vm] diff --git a/etc/supported_images.json b/etc/supported_images.json index 91d1c3adf141164a6bfced355dd09ffaeb65f11b..0d306db40d1db9c4dc161dc2c6dc05de54aa8e09 100644 --- a/etc/supported_images.json +++ b/etc/supported_images.json @@ -1,14 +1,14 @@ { "x86_64": { + "20.03-LTS": "https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-20.03-LTS/virtual_machine_img/x86_64/openEuler-20.03-LTS-x86_64.qcow2.xz", "22.03-LTS": "https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-22.03-LTS/virtual_machine_img/x86_64/openEuler-22.03-LTS-x86_64.qcow2.xz", - "22.09": "https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-22.09/virtual_machine_img/x86_64/openEuler-22.09-x86_64.qcow2.xz", - "23.03": "https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-23.03/virtual_machine_img/x86_64/openEuler-23.03-x86_64.qcow2.xz" + "24.03-LTS": "https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-24.03/virtual_machine_img/x86_64/openEuler-24.03-LTS-x86_64.qcow2.xz" }, "aarch64": { + "20.03-LTS": "https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-20.03-LTS/virtual_machine_img/aarch64/openEuler-20.03-LTS-aarch64.qcow2.xz", "22.03-LTS": "https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-22.03-LTS/virtual_machine_img/aarch64/openEuler-22.03-LTS-aarch64.qcow2.xz", - "22.09": "https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-22.09/virtual_machine_img/aarch64/openEuler-22.09-aarch64.qcow2.xz", - "23.03": "https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-23.03/virtual_machine_img/aarch64/openEuler-23.03-aarch64.qcow2.xz" + "24.03-LTS": "https://mirrors.tuna.tsinghua.edu.cn/openeuler/openEuler-24.03-LTS/virtual_machine_img/aarch64/openEuler-24.03-LTS-aarch64.qcow2.xz" } } diff --git a/eulerlauncher/.DS_Store b/eulerlauncher/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2e811fe9c885d47640a8cac3dcd204a4500adde7 Binary files /dev/null and b/eulerlauncher/.DS_Store differ diff --git a/eulerlauncher/backends/mac/image_handler.py b/eulerlauncher/backends/mac/image_handler.py index f9c8ec6753466f9183fc2d0a6a2fde0e08ac2f1e..2e10103af2b6aa54abdf562d7fa09f9a6b3a500f 100644 --- a/eulerlauncher/backends/mac/image_handler.py +++ b/eulerlauncher/backends/mac/image_handler.py @@ -1,4 +1,3 @@ -import copy import lzma import wget import os @@ -7,8 +6,7 @@ import shutil import ssl from eulerlauncher.utils import constants -from eulerlauncher.utils import utils as omni_utils -from eulerlauncher.utils import objs +from eulerlauncher.utils import utils ssl._create_default_https_context = ssl._create_unverified_context @@ -16,103 +14,124 @@ ssl._create_default_https_context = ssl._create_unverified_context class MacImageHandler(object): - def __init__(self, conf, work_dir, image_dir, image_record_file, - logger, base_dir) -> None: - self.conf = conf + def __init__(self, CONF, work_dir, image_dir, LOG) -> None: + self.conf = CONF self.work_dir = work_dir self.image_dir = image_dir - self.image_record_file = image_record_file - self.base_dir = base_dir - self.wget_bin = conf.conf.get('default', 'wget_dir') - self.LOG = logger + self.image_record_file = os.path.join(image_dir, 'images.json') + self.LOG = LOG - def download_and_transform(self, images, img_to_download): - - # Download the image - img_name = wget.filename_from_url(images['remote'][img_to_download]['path']) - img_dict = copy.deepcopy(images['remote'][img_to_download]) - - if not os.path.exists(os.path.join(self.image_dir, img_name)): - self.LOG.debug(f'Downloading image: {img_to_download} from remote repo ...') - img_dict['location'] = constants.IMAGE_LOCATION_LOCAL - img_dict['status'] = constants.IMAGE_STATUS_DOWNLOADING - images['local'][img_to_download] = img_dict - omni_utils.save_json_data(self.image_record_file, images) + def list_images(self): + image_record = utils.load_json_data(self.image_record_file) + all_images = list(image_record["remote"].values()) + list(image_record["local"].values()) + return all_images + - download_cmd = [self.wget_bin, images['remote'][img_to_download]['path'], - '-O', os.path.join(self.image_dir, img_name), '--no-check-certificate'] + def download_image(self, name): + image_record = utils.load_json_data(self.image_record_file) + if name not in image_record['remote'].keys(): + self.LOG.debug(f'Image: {name} not valid for download') + return 1 + + @utils.asyncwrapper + def download_and_transform(name): + image_record = utils.load_json_data(self.image_record_file) + image_url = image_record['remote'][name]['path'] + image_file = wget.filename_from_url(image_url) + + # Download the image + self.LOG.debug(f'Downloading image: {name} from remote repo ...') + image_record['local'][name] = { + 'name': name, + 'location': constants.IMAGE_LOCATION_LOCAL, + 'status': constants.IMAGE_STATUS_DOWNLOADING, + 'path': image_url + } + utils.save_json_data(self.image_record_file, image_record) + wget_bin = self.conf.conf.get('default', 'wget_dir') + download_cmd = [wget_bin, image_url, + '-O', os.path.join(self.image_dir, image_file), + '--no-check-certificate', + '--user-agent', 'Mozilla'] self.LOG.debug(' '.join(download_cmd)) subprocess.call(' '.join(download_cmd), shell=True) - #wget.download(url=images['remote'][img_to_download]['path'], out=os.path.join(self.image_dir, img_name), bar=None) - self.LOG.debug(f'Image: {img_to_download} succesfully downloaded from remote repo ...') - - # Decompress the image - self.LOG.debug(f'Decompressing image file: {img_name} ...') - qcow2_name = img_name[:-3] - with open(os.path.join(self.image_dir, img_name), 'rb') as pr, open(os.path.join(self.image_dir, qcow2_name), 'wb') as pw: - data = pr.read() - data_dec = lzma.decompress(data) - pw.write(data_dec) - - self.LOG.debug(f'Cleanup temp files ...') - os.remove(os.path.join(self.image_dir, img_name)) + self.LOG.debug(f'Image: {name} succesfully downloaded from remote repo ...') - # Record local image - img_dict['status'] = constants.IMAGE_STATUS_READY - img_dict['path'] = os.path.join(self.image_dir, qcow2_name) - images['local'][img_to_download] = img_dict - omni_utils.save_json_data(self.image_record_file, images) - self.LOG.debug(f'Image: {img_to_download} is ready ...') + # Decompress the image + self.LOG.debug(f'Decompressing image: {image_file} ...') + with open(os.path.join(self.image_dir, image_file), 'rb') as pr, open(os.path.join(self.image_dir, name), 'wb') as pw: + data = pr.read() + data_dec = lzma.decompress(data) + pw.write(data_dec) + + self.LOG.debug(f'Cleanup temp files ...') + os.remove(os.path.join(self.image_dir, image_file)) + + # Record local image + image_record = utils.load_json_data(self.image_record_file) + image_record['local'][name]['status'] = constants.IMAGE_STATUS_READY + image_record['local'][name]['path'] = os.path.join(self.image_dir, name) + utils.save_json_data(self.image_record_file, image_record) + self.LOG.debug(f'Image: {name} is ready ...') + + download_and_transform(name) + return 0 - def delete_image(self, images, img_to_delete): - if img_to_delete not in images['local'].keys(): + def delete_image(self, name): + image_record = utils.load_json_data(self.image_record_file) + if name not in image_record['local'].keys(): + self.LOG.debug(f'Image: {name} not valid for delete') return 1 - else: - return self._delete_image(images, img_to_delete) - - def _delete_image(self, images, img_to_delete): - img_path = images['local'][img_to_delete]['path'] - # TODO: Raise error message if image file not exists - if os.path.exists(img_path): - self.LOG.debug(f'Deleting: {img_path} ...') - os.remove(img_path) - - self.LOG.debug(f'Deleting: {img_to_delete} from image database ...') - del images['local'][img_to_delete] - omni_utils.save_json_data(self.image_record_file, images) + image_path = image_record['local'][name]['path'] + self.LOG.debug(f'Deleting: {name} from image database ...') + os.remove(image_path) + del image_record['local'][name] + utils.save_json_data(self.image_record_file, image_record) return 0 - def load_and_transform(self, images, img_to_load, path, fmt, update=False): + + def load_image(self, name, path): + if not os.path.exists(path): + self.LOG.debug(f'Image: {path} does not exist') + return 1 - if update: - self._delete_image(images, img_to_load) + supported, fmt = utils.check_file_tail(path, constants.IMAGE_LOAD_SUPPORTED_TYPES) + if not supported: + self.LOG.debug(f'Image: {name} not valid for load') + return 2 - image = objs.Image() - image.name = img_to_load - image.path = '' - image.location = constants.IMAGE_LOCATION_LOCAL - image.status = constants.IMAGE_STATUS_LOADING - images['local'][image.name] = image.to_dict() - omni_utils.save_json_data(self.image_record_file, images) - - if fmt == 'qcow2': - qcow2_name = f'{img_to_load}.qcow2' - shutil.copyfile(path, os.path.join(self.image_dir, qcow2_name)) - else: - # Decompress the image - self.LOG.debug(f'Decompressing image file: {path} ...') - qcow2_name = f'{img_to_load}.qcow2' - with open(path, 'rb') as pr, open(os.path.join(self.image_dir, qcow2_name), 'wb') as pw: - data = pr.read() - data_dec = lzma.decompress(data) - pw.write(data_dec) - - # Record local image - image.path = os.path.join(self.image_dir, qcow2_name) - image.status = constants.IMAGE_STATUS_READY - images['local'][image.name] = image.to_dict() - omni_utils.save_json_data(self.image_record_file, images) - self.LOG.debug(f'Image: {qcow2_name} is ready ...') + @utils.asyncwrapper + def load_and_transform(name, path): + image_record = utils.load_json_data(self.image_record_file) + supported, fmt = utils.check_file_tail(path, constants.IMAGE_LOAD_SUPPORTED_TYPES) + self.LOG.debug(f'Loading image: {name} from image file: {path} ...') + image_record['local'][name] = { + 'name': name, + 'location': constants.IMAGE_LOCATION_LOCAL, + 'status': constants.IMAGE_STATUS_LOADING, + 'path': '' + } + utils.save_json_data(self.image_record_file, image_record) + + if fmt == 'qcow2': + shutil.copyfile(path, os.path.join(self.image_dir, name)) + else: + # Decompress the image + self.LOG.debug(f'Decompressing image file: {path} ...') + with open(path, 'rb') as pr, open(os.path.join(self.image_dir, name), 'wb') as pw: + data = pr.read() + data_dec = lzma.decompress(data) + pw.write(data_dec) + + # Record local image + image_record = utils.load_json_data(self.image_record_file) + image_record['local'][name]['status'] = constants.IMAGE_STATUS_READY + image_record['local'][name]['path'] = os.path.join(self.image_dir, name) + utils.save_json_data(self.image_record_file, image_record) + self.LOG.debug(f'Image: {name} is ready ...') + + load_and_transform(name, path) + return 0 \ No newline at end of file diff --git a/eulerlauncher/backends/mac/instance_handler.py b/eulerlauncher/backends/mac/instance_handler.py index a53e78d81925ece983d717e04f3151860cc6b26e..c3a7be6708de761d833bb6de131a24fc6652a507 100644 --- a/eulerlauncher/backends/mac/instance_handler.py +++ b/eulerlauncher/backends/mac/instance_handler.py @@ -1,177 +1,106 @@ import os -import psutil +import libvirt import shutil -import signal -import subprocess -import sys -import time - -from oslo_utils import uuidutils +import platform from eulerlauncher.utils import constants -from eulerlauncher.utils import utils as omni_utils -from eulerlauncher.utils import objs -from eulerlauncher.backends.mac import qemu +from eulerlauncher.utils import utils class MacInstanceHandler(object): - def __init__(self, conf, work_dir, instance_dir, image_dir, - image_record_file, logger, base_dir) -> None: - self.conf = conf + def __init__(self, CONF, work_dir, instance_dir, image_dir, LOG) -> None: + self.conf = CONF self.work_dir = work_dir self.instance_dir = instance_dir self.instance_record_file = os.path.join(instance_dir, 'instances.json') self.image_dir = image_dir - self.image_record_file = image_record_file - self.driver = qemu.QemuDriver(self.conf, logger) - self.running_instances = {} - self.instance_pids = [] - self.base_dir = base_dir - self.LOG = logger + self.image_record_file = os.path.join(image_dir, 'images.json') + self.LOG = LOG def list_instances(self): - instances = omni_utils.load_json_data(self.instance_record_file)['instances'] - vm_list = [] - - for instance in instances.values(): - vm = objs.Instance(name=instance['name']) - vm.uuid = instance['uuid'] - vm.mac = instance['mac_address'] - vm.info = None - vm.vm_state = self._check_vm_state(instance) - if not instance['ip_address']: - ip_address = self._parse_ip_addr(vm.mac) - vm.ip = ip_address - else: - vm.ip = instance['ip_address'] - vm.image = instance['image'] - vm_list.append(vm) - - return vm_list - - def _check_vm_state(self, instance): - if instance['identification']['type'] == 'pid': - instance_pid = instance['identification']['id'] - if instance_pid in psutil.pids() and \ - psutil.Process(instance_pid).status() == 'running' and \ - psutil.Process(instance_pid).name().startswith('qemu'): - return constants.VM_STATE_MAP[2] - else: - return constants.VM_STATE_MAP[3] - else: - return constants.VM_STATE_MAP[99] - - def _parse_ip_addr(self, mac_addr): - ip = '' - cmd = 'arp -a' - start_time = time.time() - while(ip == '' and time.time() - start_time < 20): - pr = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE) - arp_result = pr.stdout.decode('utf-8').split('\n') - founded = False - for str in arp_result: - # The result for 'arp -a' in MacOS is different with Linux, it erase - # the first 0 if the first digit is 0 for this mac section, add it - # back before compare - try: - arp_ip = str.split(' ')[1].replace("(", "").replace(")", "") - mac = str.split(' ')[3].replace("(", "").replace(")", "") - except IndexError: - continue - mac_list = mac.split(':') - for i in range(0, len(mac_list)): - if len(mac_list[i]) == 1: - mac_list[i] = '0' + mac_list[i] - mac_0 = ':'.join(mac_list) - if mac_addr == mac_0: - ip = arp_ip - founded = True - break - if founded: - break - - return ip - - def check_names(self, name, all_instances): - try: - all_instances['instances'][name] + instance_record = utils.load_json_data(self.instance_record_file) + all_instances = list(instance_record.values()) + return all_instances + + def create_instance(self, name, image): + image_record = utils.load_json_data(self.image_record_file) + if image not in image_record['local'].keys(): + self.LOG.debug(f'Image: {image} is not available locally') return 1 - except KeyError: - return 0 - - def create_instance(self, name, image_id, instance_record, all_instances, all_images): - # Create dir for the instance - vm_uuid = uuidutils.generate_uuid() - vm_dict = { - 'name': name, - 'uuid': vm_uuid, - 'image': image_id, - 'vm_state': constants.VM_STATE_MAP[99], - 'ip_address': 'N/A', - 'mac_address': omni_utils.generate_mac(), - 'identification': { - 'type': 'pid', - 'id': None - } - } - + + instance_record = utils.load_json_data(self.instance_record_file) + if name in instance_record.keys(): + self.LOG.debug(f'Instance: {name} already exist') + return 2 + instance_path = os.path.join(self.instance_dir, name) os.makedirs(instance_path) - img_path = all_images['local'][image_id]['path'] - - root_disk_path = shutil.copyfile(img_path, os.path.join(instance_path, image_id + '.qcow2')) - - vm_process = self.driver.create_vm(name, vm_uuid, vm_dict['mac_address'], root_disk_path) - - self.running_instances[vm_process.pid] = vm_process - self.instance_pids.append(vm_process.pid) + image_path = image_record['local'][image]['path'] + disk_path = shutil.copyfile(image_path, os.path.join(instance_path, image)) + host_arch_raw = platform.uname().machine + host_arch = constants.ARCH_MAP[host_arch_raw] + xml_file = os.path.join('/Library/Application Support/org.openeuler.eulerlauncher/','libvirt-' + host_arch + '.xml') + xml = utils.load_xml_data(xml_file) + vcpu = self.conf.conf.get('vm', 'cpu_num') + ram = self.conf.conf.get('vm', 'memory') + + def xml_find_and_set(xml, xpath, attribute=None, value=None): + elements = xml.xpath(xpath) + if attribute is not None: + if value is not None: + elements[0].set(attribute, value) + return elements[0].get(attribute) + else: + if value is not None: + elements[0].text = value + return elements[0].text + + xml_find_and_set(xml, 'name', value=name) + xml_find_and_set(xml, 'vcpu', value=vcpu) + xml_find_and_set(xml, 'memory', value=ram) + xml_find_and_set(xml, 'devices/emulator', value=self.conf.conf.get('default', 'qemu_dir')) + xml_find_and_set(xml, 'devices/disk/source', 'file', disk_path) + xml_find_and_set(xml, 'devices/interface/mac', 'address', utils.generate_mac()) + utils.save_xml_data(xml_file, xml) - vm_dict['identification']['id'] = vm_process.pid - - vm_ip = self._parse_ip_addr(vm_dict['mac_address']) - vm_dict['ip_address'] = vm_ip - - instance_record_dict = { + conn = libvirt.open("qemu:///session") + with open(xml_file, 'r') as pr: + dom = conn.createLinux(pr.read()) + + instance_record[name] = { + 'id': dom.ID(), 'name': name, - 'uuid': vm_dict['uuid'], - 'image': image_id, - 'path': instance_path, - 'mac_address': vm_dict['mac_address'], - 'ip_address': vm_dict['ip_address'], - 'identification': vm_dict['identification'] + 'state': constants.INSTANCE_STATE_MAP[dom.state()[0]], + 'vcpu': dom.maxVcpus(), + 'ram': dom.maxMemory() // 1024, + 'image': image, + 'mac_address': '', + 'ip_address': 'N/A', + 'path': instance_path } + utils.save_json_data(self.instance_record_file, instance_record) + conn.close() + return 0 - all_instances['instances'][name] = instance_record_dict - omni_utils.save_json_data(instance_record, all_instances) + def delete_instance(self, name): + instance_record = utils.load_json_data(self.instance_record_file) - return { - 'name': name, - 'vm_state': self._check_vm_state(vm_dict), - 'image': image_id, - 'ip_address': vm_dict['ip_address'] - } + conn = libvirt.open("qemu:///session") + dom = conn.lookupByName(name) - def delete_instance(self, name, instance_record, all_instances): - # Delete instance process - instance = all_instances['instances'][name] - if instance['identification']['type'] == 'pid': - instance_pid = instance['identification']['id'] - if instance_pid in psutil.pids() and \ - psutil.Process(instance_pid).is_running(): - psutil.Process(instance_pid).kill() - self.LOG.debug(f'Instance: {name} with PID {instance_pid} succesfully killed ...') - else: - self.LOG.debug(f'Instance: {name} with PID {instance_pid} already stopped, skip ...') + if dom is not None: + dom.destroy() + self.LOG.debug(f'Instance: {name} succesfully killed ...') else: - self.LOG.debug(f'Instance: {name} unable to handled, skip ...') + self.LOG.debug(f'Instance: {name} already stopped, skip ...') # Cleanup files and records - instance_dir = instance['path'] - shutil.rmtree(instance_dir) - del all_instances['instances'][name] - - omni_utils.save_json_data(instance_record, all_instances) + instance_path = instance_record[name]['path'] + shutil.rmtree(instance_path) + del instance_record[name] + utils.save_json_data(self.instance_record_file, instance_record) + conn.close() return 0 diff --git a/eulerlauncher/backends/mac/qemu.py b/eulerlauncher/backends/mac/qemu.py deleted file mode 100644 index 2bdd900d0db4bd6a6f408db32a47875c535b12b2..0000000000000000000000000000000000000000 --- a/eulerlauncher/backends/mac/qemu.py +++ /dev/null @@ -1,30 +0,0 @@ -import platform -import subprocess -import os - -from eulerlauncher.utils import constants - - -class QemuDriver(object): - - def __init__(self, conf, logger) -> None: - host_arch_raw = platform.uname().machine - host_arch = constants.ARCH_MAP[host_arch_raw] - self.qemu_bin = conf.conf.get('default', 'qemu_dir') - self.uefi_file = os.path.join('/Library/Application\ Support/org.openeuler.eulerlauncher/','edk2-' + host_arch + '-code.fd') - self.uefi_params = ',if=pflash,format=raw,readonly=on' - self.vm_cpu = conf.conf.get('vm', 'cpu_num') - self.vm_ram = conf.conf.get('vm', 'memory') - self.LOG = logger - - def create_vm(self, vm_name, vm_uuid, vm_mac, vm_root_disk): - qemu_cmd = [ - self.qemu_bin, '-machine', 'virt,highmem=off', '-name', vm_name, '-uuid', vm_uuid, - '-accel hvf', '-drive', 'file=' + self.uefi_file + self.uefi_params, '-cpu host', - '-nic', 'vmnet-shared,model=virtio-net-pci,mac=' + vm_mac, - '-drive', 'file=' + vm_root_disk, '-device', 'virtio-scsi-pci,id=scsi0', - '-smp', self.vm_cpu, '-m', self.vm_ram + 'M', '-monitor none -chardev null,id=char0', - '-serial chardev:char0 -nographic'] - self.LOG.debug(' '.join(qemu_cmd)) - instance_process = subprocess.Popen(' '.join(qemu_cmd), shell=True) - return instance_process diff --git a/eulerlauncher/backends/win/image_handler.py b/eulerlauncher/backends/win/image_handler.py index 9f09f4d13bcea66cc39a418e200ccabfdcd582dd..56a0c2ea2e621fb518d04c7c4d8182005ef97eb3 100644 --- a/eulerlauncher/backends/win/image_handler.py +++ b/eulerlauncher/backends/win/image_handler.py @@ -7,7 +7,7 @@ import ssl from eulerlauncher.backends.win import powershell from eulerlauncher.utils import constants -from eulerlauncher.utils import utils as omni_utils +from eulerlauncher.utils import utils from eulerlauncher.utils import objs @@ -23,32 +23,32 @@ class WinImageHandler(object): self.image_record_file = image_record_file self.LOG = logger - def download_and_transform(self, images, img_to_download): + def download_and_transform(self, images, image_to_download): # Download the image - img_name = wget.filename_from_url(images['remote'][img_to_download]['path']) - img_dict = copy.deepcopy(images['remote'][img_to_download]) + image_name = wget.filename_from_url(images['remote'][image_to_download]['path']) + img_dict = copy.deepcopy(images['remote'][image_to_download]) - if not os.path.exists(os.path.join(self.image_dir, img_name)): - self.LOG.debug(f'Downloading image: {img_to_download} from remote repo ...') + if not os.path.exists(os.path.join(self.image_dir, image_name)): + self.LOG.debug(f'Downloading image: {image_to_download} from remote repo ...') img_dict['location'] = constants.IMAGE_LOCATION_LOCAL img_dict['status'] = constants.IMAGE_STATUS_DOWNLOADING - images['local'][img_to_download] = img_dict - omni_utils.save_json_data(self.image_record_file, images) - wget.download(url=images['remote'][img_to_download]['path'], out=os.path.join(self.image_dir, img_name), bar=None) - self.LOG.debug(f'Image: {img_to_download} succesfully downloaded from remote repo ...') + images['local'][image_to_download] = img_dict + utils.save_json_data(self.image_record_file, images) + wget.download(url=images['remote'][image_to_download]['path'], out=os.path.join(self.image_dir, image_name), bar=None) + self.LOG.debug(f'Image: {image_to_download} succesfully downloaded from remote repo ...') # Decompress the image - self.LOG.debug(f'Decompressing image file: {img_name} ...') - qcow2_name = img_name[:-3] - with open(os.path.join(self.image_dir, img_name), 'rb') as pr, open(os.path.join(self.image_dir, qcow2_name), 'wb') as pw: + self.LOG.debug(f'Decompressing image file: {image_name} ...') + qcow2_name = image_name[:-3] + with open(os.path.join(self.image_dir, image_name), 'rb') as pr, open(os.path.join(self.image_dir, qcow2_name), 'wb') as pw: data = pr.read() data_dec = lzma.decompress(data) pw.write(data_dec) # Convert the qcow2 img to vhdx - vhdx_name = img_to_download + '.vhdx' - self.LOG.debug(f'Converting image file: {img_name} to {vhdx_name} ...') + vhdx_name = image_to_download + '.vhdx' + self.LOG.debug(f'Converting image file: {image_name} to {vhdx_name} ...') with powershell.PowerShell('GBK') as ps: cmd = 'qemu-img convert -O vhdx {0} {1}' outs, errs = ps.run(cmd.format(os.path.join(self.image_dir, qcow2_name), os.path.join(self.image_dir, vhdx_name))) @@ -59,9 +59,9 @@ class WinImageHandler(object): # Record local image img_dict['status'] = constants.IMAGE_STATUS_READY img_dict['path'] = os.path.join(self.image_dir, vhdx_name) - images['local'][img_to_download] = img_dict - omni_utils.save_json_data(self.image_record_file, images) - self.LOG.debug(f'Image: {img_to_download} is ready ...') + images['local'][image_to_download] = img_dict + utils.save_json_data(self.image_record_file, images) + self.LOG.debug(f'Image: {image_to_download} is ready ...') def delete_image(self, images, img_to_delete): if img_to_delete not in images['local'].keys(): @@ -77,7 +77,7 @@ class WinImageHandler(object): self.LOG.debug(f'Deleting: {img_to_delete} from image database ...') del images['local'][img_to_delete] - omni_utils.save_json_data(self.image_record_file, images) + utils.save_json_data(self.image_record_file, images) return 0 @@ -92,7 +92,7 @@ class WinImageHandler(object): image.location = constants.IMAGE_LOCATION_LOCAL image.status = constants.IMAGE_STATUS_LOADING images['local'][image.name] = image.to_dict() - omni_utils.save_json_data(self.image_record_file, images) + utils.save_json_data(self.image_record_file, images) if fmt == 'qcow2': qcow2_name = f'{img_to_load}.qcow2' @@ -119,5 +119,5 @@ class WinImageHandler(object): image.path = os.path.join(self.image_dir, vhdx_name) image.status = constants.IMAGE_STATUS_READY images['local'][image.name] = image.to_dict() - omni_utils.save_json_data(self.image_record_file, images) + utils.save_json_data(self.image_record_file, images) self.LOG.debug(f'Image: {vhdx_name} is ready ...') diff --git a/eulerlauncher/backends/win/instance_handler.py b/eulerlauncher/backends/win/instance_handler.py index 7b27055300490a449c7efdfa61fdb997e03cc2c0..c8c975f388d2c6108352d600ec94a1959f3c820b 100644 --- a/eulerlauncher/backends/win/instance_handler.py +++ b/eulerlauncher/backends/win/instance_handler.py @@ -9,7 +9,7 @@ from os_win import exceptions as os_win_exc from eulerlauncher.backends.win import powershell from eulerlauncher.backends.win import vmops from eulerlauncher.utils import constants -from eulerlauncher.utils import utils as omni_utils +from eulerlauncher.utils import utils from eulerlauncher.utils import objs @@ -73,7 +73,7 @@ class WinInstanceHandler(object): } all_instances['instances'][name] = instance_record_dict - omni_utils.save_json_data(instance_record, all_instances) + utils.save_json_data(instance_record, all_instances) return { 'name': name, @@ -91,7 +91,7 @@ class WinInstanceHandler(object): shutil.rmtree(instance_dir) del all_instances['instances'][name] - omni_utils.save_json_data(instance_record, all_instances) + utils.save_json_data(instance_record, all_instances) return 0 diff --git a/eulerlauncher/backends/win/vmops.py b/eulerlauncher/backends/win/vmops.py index b8e4c75d4193bf3e7a807104b25b580bf98c27ae..618a8732f134cf17778f1cbd227d86ba5688c4d9 100644 --- a/eulerlauncher/backends/win/vmops.py +++ b/eulerlauncher/backends/win/vmops.py @@ -8,7 +8,7 @@ from oslo_utils import uuidutils from eulerlauncher.utils import objs from eulerlauncher.utils import constants -from eulerlauncher.utils import utils as omni_utils +from eulerlauncher.utils import utils from eulerlauncher.backends.win import powershell SWITCH_NAME = 'Default Switch' @@ -83,7 +83,7 @@ class VMOps(object): def get_instance_ip_addr(self, instance_name): nic_name = instance_name + '_eth0' nic = self.get_vm_nics(instance_name, nic_name) - mac_address = omni_utils.format_mac_addr(nic.Address) + mac_address = utils.format_mac_addr(nic.Address) with powershell.PowerShell('GBK') as ps: outs, errs = ps.run('arp -a | findstr /i {}'.format(mac_address)) ip_address = outs.strip(' ').split(' ')[0] diff --git a/eulerlauncher/cli.py b/eulerlauncher/cli.py index 56972a1573780b1c5660695f3e7a3cc55009d430..ecf86e78fa078987c4f0b478cd0d2f15700c4b75 100644 --- a/eulerlauncher/cli.py +++ b/eulerlauncher/cli.py @@ -110,20 +110,7 @@ def launch(vm_name, image): except Exception: print('Calling to EulerLauncherd daemon failed, please check EulerLauncherd daemon status ...') else: - - if ret['ret'] == 1: - tb = pt.PrettyTable() - tb.field_names = ["Name", "Image", "State", "IP"] - tb.add_row( - [ret['instance']['name'], - ret['instance']['image'], - ret['instance']['vmState'], - ret['instance']['ipAddress']]) - - print(tb) - - else: - print(ret['msg']) + print(ret['msg']) @click.group() diff --git a/eulerlauncher/eulerlauncherd.py b/eulerlauncher/eulerlauncherd.py index 4bd0be7542a1457d53209c23dc5428ae0fb9b252..b7099502f8947790664d8caa019ab62d8034dcf3 100644 --- a/eulerlauncher/eulerlauncherd.py +++ b/eulerlauncher/eulerlauncherd.py @@ -1,5 +1,4 @@ import argparse -from concurrent import futures import grpc import logging import os @@ -8,13 +7,14 @@ import platform import pystray import requests import signal -import subprocess import sys import time +from concurrent import futures + from eulerlauncher.grpcs.eulerlauncher_grpc import images_pb2, images_pb2_grpc from eulerlauncher.grpcs.eulerlauncher_grpc import instances_pb2, instances_pb2_grpc -from eulerlauncher.services import imager_service, instance_service +from eulerlauncher.services import image_service, instance_service from eulerlauncher.utils import constants from eulerlauncher.utils import objs from eulerlauncher.utils import utils @@ -29,10 +29,8 @@ if host_os_raw != 'Windows': parser = argparse.ArgumentParser() parser.add_argument('conf_file', help='Configuration file for the application', type=str) -parser.add_argument('base_dir', help='The base work directory of the daemon') - -def config_logging(config): +def init_log(config): log_dir = config.conf.get('default', 'log_dir') debug = config.conf.get('default', 'debug') @@ -50,26 +48,27 @@ def config_logging(config): filename=log_file, level=log_level, filemode='a+') -def init(arch, config, LOG): +def init_workdir(arch, config, LOG): work_dir = config.conf.get('default', 'work_dir') image_dir = os.path.join(work_dir, 'images') instance_dir = os.path.join(work_dir, 'instances') instance_record_file = os.path.join(instance_dir, 'instances.json') - img_record_file = os.path.join(image_dir, 'images.json') + image_record_file = os.path.join(image_dir, 'images.json') LOG.debug('Initializing EulerLauncherd ...') LOG.debug('Checking for work directory ...') if not os.path.exists(work_dir): LOG.debug('Create %s as working directory ...' % work_dir) os.makedirs(work_dir) - LOG.debug('Checking for instances directory ...') + + LOG.debug('Checking for instance directory ...') if not os.path.exists(instance_dir): - LOG.debug('Create %s as working directory ...' % work_dir) + LOG.debug('Create %s as instance directory ...' % instance_dir) os.makedirs(instance_dir) LOG.debug('Checking for instance database ...') if not os.path.exists(instance_record_file): + LOG.debug('Create %s as instance database ...' % instance_record_file) instances = { - 'instances': {} } utils.save_json_data(instance_record_file, instances) @@ -77,36 +76,34 @@ def init(arch, config, LOG): if not os.path.exists(image_dir): LOG.debug('Create %s as image directory ...' % image_dir) os.makedirs(image_dir) - LOG.debug('Checking for image database ...') - remote_img_resp = requests.get(IMG_URL, verify=False) - remote_imgs = remote_img_resp.json()[arch] - if not os.path.exists(img_record_file): - images = {} - for name, path in remote_imgs.items(): - image = objs.Image() - image.name = name - image.path = path - image.location = constants.IMAGE_LOCATION_REMOTE - image.status = constants.IMAGE_STATUS_DOWLOADABLE - images[image.name] = image.to_dict() - - image_body = { - 'remote': images, + if not os.path.exists(image_record_file): + LOG.debug('Create %s as image database ...' % image_record_file) + remote_image_resp = requests.get(IMG_URL, verify=False) + remote_images = remote_image_resp.json()[arch] + image_record = { + 'remote': {}, 'local': {} } - utils.save_json_data(img_record_file, image_body) - -def serve(arch, host_os, CONF, LOG, base_dir): + for name, path in remote_images.items(): + image_record['remote'][name] = { + 'name': name, + 'path': path, + 'location': constants.IMAGE_LOCATION_REMOTE, + 'status': constants.IMAGE_STATUS_DOWLOADABLE + } + utils.save_json_data(image_record_file, image_record) + +def serve(host_arch, host_os, CONF, LOG): ''' - Run the EulerLauncherd Service + Run the EulerLauncherd service ''' server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) - images_pb2_grpc.add_ImageGrpcServiceServicer_to_server(imager_service.ImagerService(arch, host_os, CONF, base_dir), server) - instances_pb2_grpc.add_InstanceGrpcServiceServicer_to_server(instance_service.InstanceService(arch, host_os, CONF, base_dir), server) - server.add_insecure_port('[::]:50052') + images_pb2_grpc.add_ImageGrpcServiceServicer_to_server(image_service.ImageService(host_arch, host_os, CONF, LOG), server) + instances_pb2_grpc.add_InstanceGrpcServiceServicer_to_server(instance_service.InstanceService(host_arch, host_os, CONF, LOG), server) + server.add_insecure_port('localhost:50052') server.start() - LOG.debug('EulerLauncherd Service Started ...') + LOG.debug('EulerLauncherd service started ...') if host_os == 'Win': return server @@ -121,10 +118,10 @@ def serve(arch, host_os, CONF, LOG, base_dir): while True: time.sleep(1) -def init_launcherd(conf, base_dir): - CONF = objs.Conf(conf) +def init_launcherd(conf_file): + CONF = objs.Conf(conf_file) - config_logging(CONF) + init_log(CONF) LOG = logging.getLogger(__name__) host_arch_raw = platform.uname().machine @@ -133,13 +130,9 @@ def init_launcherd(conf, base_dir): host_arch = constants.ARCH_MAP[host_arch_raw] host_os = constants.OS_MAP[host_os_raw] - try: - init(host_arch, CONF, LOG) - except Exception as e: - LOG.debug('Error: ' + str(e)) - return str(e) - else: - return serve(host_arch, host_os, CONF, LOG, base_dir) + init_workdir(host_arch, CONF, LOG) + + return serve(host_arch, host_os, CONF, LOG) if __name__ == '__main__': @@ -147,35 +140,29 @@ if __name__ == '__main__': if host_os_raw != 'Windows': args = parser.parse_args() conf_file = args.conf_file - base_dir = args.base_dir else: conf_file = os.path.join(os.getcwd(), 'etc', 'eulerlauncher.conf') - base_dir = None - try: - pass - except Exception as e: - print('Error: ' + str(e)) + + if host_os_raw != 'Windows': + init_launcherd(conf_file) else: - if host_os_raw != 'Windows': - init_launcherd(conf_file, base_dir) - else: - try: - logo = PIL.Image.open(os.path.join(os.getcwd(), 'etc', 'favicon.png')) - - def on_clicked(icon, item): - icon.stop() - - icon = pystray.Icon('EulerLauncher', logo, menu=pystray.Menu( - pystray.MenuItem('Exit EulerLauncher', on_clicked) - )) + try: + logo = PIL.Image.open(os.path.join(os.getcwd(), 'etc', 'favicon.png')) - except Exception as e: - print('Error: ' + str(e)) - sys.exit(0) - - server = init_launcherd(conf_file, base_dir) + def on_clicked(icon, item): + icon.stop() + + icon = pystray.Icon('EulerLauncher', logo, menu=pystray.Menu( + pystray.MenuItem('Exit EulerLauncher', on_clicked) + )) - icon.run() - server.stop(None) + except Exception as e: + print('Error: ' + str(e)) sys.exit(0) + + server = init_launcherd(conf_file) + + icon.run() + server.stop(None) + sys.exit(0) diff --git a/eulerlauncher/grpcs/eulerlauncher_grpc/images.proto b/eulerlauncher/grpcs/eulerlauncher_grpc/images.proto index 26996d9dfd1c61817d7d3fc9fbc077fe3b02136b..4812b707c06d520ae09f976a4382e8a110223bd4 100644 --- a/eulerlauncher/grpcs/eulerlauncher_grpc/images.proto +++ b/eulerlauncher/grpcs/eulerlauncher_grpc/images.proto @@ -22,7 +22,6 @@ message Image { message ListImageRequest { } - message ListImageResponse { repeated Image images = 1; } diff --git a/eulerlauncher/grpcs/eulerlauncher_grpc/images_pb2.py b/eulerlauncher/grpcs/eulerlauncher_grpc/images_pb2.py index 6b2f860ebc20149da3530d08e18adeca7a89c574..e42523ea849787a0178bb3c0befe05ce804a3812 100644 --- a/eulerlauncher/grpcs/eulerlauncher_grpc/images_pb2.py +++ b/eulerlauncher/grpcs/eulerlauncher_grpc/images_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: images.proto +# Protobuf Python Version: 5.28.1 """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 28, + 1, + '', + 'images.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -15,26 +26,26 @@ _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cimages.proto\x12\x08omnivirt\"7\n\x05Image\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x10\n\x08location\x18\x02 \x01(\t\x12\x0e\n\x06status\x18\x03 \x01(\t\"\x12\n\x10ListImageRequest\"4\n\x11ListImageResponse\x12\x1f\n\x06images\x18\x01 \x03(\x0b\x32\x0f.omnivirt.Image\"$\n\x14\x44ownloadImageRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\".\n\x10LoadImageRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\t\"\"\n\x12\x44\x65leteImageRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"0\n\x14GeneralImageResponse\x12\x0b\n\x03ret\x18\x01 \x01(\r\x12\x0b\n\x03msg\x18\x02 \x01(\t2\xcc\x02\n\x10ImageGrpcService\x12H\n\x0blist_images\x12\x1a.omnivirt.ListImageRequest\x1a\x1b.omnivirt.ListImageResponse\"\x00\x12R\n\x0e\x64ownload_image\x12\x1e.omnivirt.DownloadImageRequest\x1a\x1e.omnivirt.GeneralImageResponse\"\x00\x12J\n\nload_image\x12\x1a.omnivirt.LoadImageRequest\x1a\x1e.omnivirt.GeneralImageResponse\"\x00\x12N\n\x0c\x64\x65lete_image\x12\x1c.omnivirt.DeleteImageRequest\x1a\x1e.omnivirt.GeneralImageResponse\"\x00\x42\x03\x80\x01\x01\x62\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'images_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\200\001\001' - _IMAGE._serialized_start=26 - _IMAGE._serialized_end=81 - _LISTIMAGEREQUEST._serialized_start=83 - _LISTIMAGEREQUEST._serialized_end=101 - _LISTIMAGERESPONSE._serialized_start=103 - _LISTIMAGERESPONSE._serialized_end=155 - _DOWNLOADIMAGEREQUEST._serialized_start=157 - _DOWNLOADIMAGEREQUEST._serialized_end=193 - _LOADIMAGEREQUEST._serialized_start=195 - _LOADIMAGEREQUEST._serialized_end=241 - _DELETEIMAGEREQUEST._serialized_start=243 - _DELETEIMAGEREQUEST._serialized_end=277 - _GENERALIMAGERESPONSE._serialized_start=279 - _GENERALIMAGERESPONSE._serialized_end=327 - _IMAGEGRPCSERVICE._serialized_start=330 - _IMAGEGRPCSERVICE._serialized_end=662 +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'images_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\200\001\001' + _globals['_IMAGE']._serialized_start=26 + _globals['_IMAGE']._serialized_end=81 + _globals['_LISTIMAGEREQUEST']._serialized_start=83 + _globals['_LISTIMAGEREQUEST']._serialized_end=101 + _globals['_LISTIMAGERESPONSE']._serialized_start=103 + _globals['_LISTIMAGERESPONSE']._serialized_end=155 + _globals['_DOWNLOADIMAGEREQUEST']._serialized_start=157 + _globals['_DOWNLOADIMAGEREQUEST']._serialized_end=193 + _globals['_LOADIMAGEREQUEST']._serialized_start=195 + _globals['_LOADIMAGEREQUEST']._serialized_end=241 + _globals['_DELETEIMAGEREQUEST']._serialized_start=243 + _globals['_DELETEIMAGEREQUEST']._serialized_end=277 + _globals['_GENERALIMAGERESPONSE']._serialized_start=279 + _globals['_GENERALIMAGERESPONSE']._serialized_end=327 + _globals['_IMAGEGRPCSERVICE']._serialized_start=330 + _globals['_IMAGEGRPCSERVICE']._serialized_end=662 # @@protoc_insertion_point(module_scope) diff --git a/eulerlauncher/grpcs/eulerlauncher_grpc/images_pb2_grpc.py b/eulerlauncher/grpcs/eulerlauncher_grpc/images_pb2_grpc.py index 28eb2df9c2085b818c685fe2958f4ffc8bc90904..ad44875a6cfc29f7b8a4cc02331806443ed57cbc 100644 --- a/eulerlauncher/grpcs/eulerlauncher_grpc/images_pb2_grpc.py +++ b/eulerlauncher/grpcs/eulerlauncher_grpc/images_pb2_grpc.py @@ -4,6 +4,25 @@ import grpc from eulerlauncher.grpcs.eulerlauncher_grpc import images_pb2 as images__pb2 +GRPC_GENERATED_VERSION = '1.68.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in images_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + class ImageGrpcServiceStub(object): """Missing associated documentation comment in .proto file.""" @@ -18,22 +37,22 @@ class ImageGrpcServiceStub(object): '/omnivirt.ImageGrpcService/list_images', request_serializer=images__pb2.ListImageRequest.SerializeToString, response_deserializer=images__pb2.ListImageResponse.FromString, - ) + _registered_method=True) self.download_image = channel.unary_unary( '/omnivirt.ImageGrpcService/download_image', request_serializer=images__pb2.DownloadImageRequest.SerializeToString, response_deserializer=images__pb2.GeneralImageResponse.FromString, - ) + _registered_method=True) self.load_image = channel.unary_unary( '/omnivirt.ImageGrpcService/load_image', request_serializer=images__pb2.LoadImageRequest.SerializeToString, response_deserializer=images__pb2.GeneralImageResponse.FromString, - ) + _registered_method=True) self.delete_image = channel.unary_unary( '/omnivirt.ImageGrpcService/delete_image', request_serializer=images__pb2.DeleteImageRequest.SerializeToString, response_deserializer=images__pb2.GeneralImageResponse.FromString, - ) + _registered_method=True) class ImageGrpcServiceServicer(object): @@ -90,6 +109,7 @@ def add_ImageGrpcServiceServicer_to_server(servicer, server): generic_handler = grpc.method_handlers_generic_handler( 'omnivirt.ImageGrpcService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('omnivirt.ImageGrpcService', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -107,11 +127,21 @@ class ImageGrpcService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/omnivirt.ImageGrpcService/list_images', + return grpc.experimental.unary_unary( + request, + target, + '/omnivirt.ImageGrpcService/list_images', images__pb2.ListImageRequest.SerializeToString, images__pb2.ListImageResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def download_image(request, @@ -124,11 +154,21 @@ class ImageGrpcService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/omnivirt.ImageGrpcService/download_image', + return grpc.experimental.unary_unary( + request, + target, + '/omnivirt.ImageGrpcService/download_image', images__pb2.DownloadImageRequest.SerializeToString, images__pb2.GeneralImageResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def load_image(request, @@ -141,11 +181,21 @@ class ImageGrpcService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/omnivirt.ImageGrpcService/load_image', + return grpc.experimental.unary_unary( + request, + target, + '/omnivirt.ImageGrpcService/load_image', images__pb2.LoadImageRequest.SerializeToString, images__pb2.GeneralImageResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def delete_image(request, @@ -158,8 +208,18 @@ class ImageGrpcService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/omnivirt.ImageGrpcService/delete_image', + return grpc.experimental.unary_unary( + request, + target, + '/omnivirt.ImageGrpcService/delete_image', images__pb2.DeleteImageRequest.SerializeToString, images__pb2.GeneralImageResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/eulerlauncher/grpcs/eulerlauncher_grpc/instances_pb2.py b/eulerlauncher/grpcs/eulerlauncher_grpc/instances_pb2.py index 9877833f058ff6798506e937cee9f7ce7d1e555e..be819a1b61a9a7cd6efa29690682c5ea83171952 100644 --- a/eulerlauncher/grpcs/eulerlauncher_grpc/instances_pb2.py +++ b/eulerlauncher/grpcs/eulerlauncher_grpc/instances_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: instances.proto +# Protobuf Python Version: 5.28.1 """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 28, + 1, + '', + 'instances.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -15,26 +26,26 @@ _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0finstances.proto\x12\x08omnivirt\"M\n\x08Instance\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05image\x18\x02 \x01(\t\x12\x10\n\x08vm_state\x18\x03 \x01(\t\x12\x12\n\nip_address\x18\x04 \x01(\t\"\x16\n\x14ListInstancesRequest\">\n\x15ListInstancesResponse\x12%\n\tinstances\x18\x01 \x03(\x0b\x32\x12.omnivirt.Instance\"4\n\x15\x43reateInstanceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05image\x18\x02 \x01(\t\"j\n\x16\x43reateInstanceResponse\x12\x0b\n\x03ret\x18\x01 \x01(\r\x12\x0b\n\x03msg\x18\x02 \x01(\t\x12)\n\x08instance\x18\x03 \x01(\x0b\x32\x12.omnivirt.InstanceH\x00\x88\x01\x01\x42\x0b\n\t_instance\"%\n\x15\x44\x65leteInstanceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"2\n\x16\x44\x65leteInstanceResponse\x12\x0b\n\x03ret\x18\x01 \x01(\r\x12\x0b\n\x03msg\x18\x02 \x01(\t2\x9a\x02\n\x13InstanceGrpcService\x12S\n\x0elist_instances\x12\x1e.omnivirt.ListInstancesRequest\x1a\x1f.omnivirt.ListInstancesResponse\"\x00\x12V\n\x0f\x63reate_instance\x12\x1f.omnivirt.CreateInstanceRequest\x1a .omnivirt.CreateInstanceResponse\"\x00\x12V\n\x0f\x64\x65lete_instance\x12\x1f.omnivirt.DeleteInstanceRequest\x1a .omnivirt.DeleteInstanceResponse\"\x00\x42\x03\x80\x01\x01\x62\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'instances_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\200\001\001' - _INSTANCE._serialized_start=29 - _INSTANCE._serialized_end=106 - _LISTINSTANCESREQUEST._serialized_start=108 - _LISTINSTANCESREQUEST._serialized_end=130 - _LISTINSTANCESRESPONSE._serialized_start=132 - _LISTINSTANCESRESPONSE._serialized_end=194 - _CREATEINSTANCEREQUEST._serialized_start=196 - _CREATEINSTANCEREQUEST._serialized_end=248 - _CREATEINSTANCERESPONSE._serialized_start=250 - _CREATEINSTANCERESPONSE._serialized_end=356 - _DELETEINSTANCEREQUEST._serialized_start=358 - _DELETEINSTANCEREQUEST._serialized_end=395 - _DELETEINSTANCERESPONSE._serialized_start=397 - _DELETEINSTANCERESPONSE._serialized_end=447 - _INSTANCEGRPCSERVICE._serialized_start=450 - _INSTANCEGRPCSERVICE._serialized_end=732 +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'instances_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\200\001\001' + _globals['_INSTANCE']._serialized_start=29 + _globals['_INSTANCE']._serialized_end=106 + _globals['_LISTINSTANCESREQUEST']._serialized_start=108 + _globals['_LISTINSTANCESREQUEST']._serialized_end=130 + _globals['_LISTINSTANCESRESPONSE']._serialized_start=132 + _globals['_LISTINSTANCESRESPONSE']._serialized_end=194 + _globals['_CREATEINSTANCEREQUEST']._serialized_start=196 + _globals['_CREATEINSTANCEREQUEST']._serialized_end=248 + _globals['_CREATEINSTANCERESPONSE']._serialized_start=250 + _globals['_CREATEINSTANCERESPONSE']._serialized_end=356 + _globals['_DELETEINSTANCEREQUEST']._serialized_start=358 + _globals['_DELETEINSTANCEREQUEST']._serialized_end=395 + _globals['_DELETEINSTANCERESPONSE']._serialized_start=397 + _globals['_DELETEINSTANCERESPONSE']._serialized_end=447 + _globals['_INSTANCEGRPCSERVICE']._serialized_start=450 + _globals['_INSTANCEGRPCSERVICE']._serialized_end=732 # @@protoc_insertion_point(module_scope) diff --git a/eulerlauncher/grpcs/eulerlauncher_grpc/instances_pb2_grpc.py b/eulerlauncher/grpcs/eulerlauncher_grpc/instances_pb2_grpc.py index 970781c046dbdda278277ff639fd0e0addcb983a..bfef0dbbebf50665145e3ca6cd8a8da33eb45514 100644 --- a/eulerlauncher/grpcs/eulerlauncher_grpc/instances_pb2_grpc.py +++ b/eulerlauncher/grpcs/eulerlauncher_grpc/instances_pb2_grpc.py @@ -4,6 +4,25 @@ import grpc from eulerlauncher.grpcs.eulerlauncher_grpc import instances_pb2 as instances__pb2 +GRPC_GENERATED_VERSION = '1.68.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in instances_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + class InstanceGrpcServiceStub(object): """Missing associated documentation comment in .proto file.""" @@ -18,17 +37,17 @@ class InstanceGrpcServiceStub(object): '/omnivirt.InstanceGrpcService/list_instances', request_serializer=instances__pb2.ListInstancesRequest.SerializeToString, response_deserializer=instances__pb2.ListInstancesResponse.FromString, - ) + _registered_method=True) self.create_instance = channel.unary_unary( '/omnivirt.InstanceGrpcService/create_instance', request_serializer=instances__pb2.CreateInstanceRequest.SerializeToString, response_deserializer=instances__pb2.CreateInstanceResponse.FromString, - ) + _registered_method=True) self.delete_instance = channel.unary_unary( '/omnivirt.InstanceGrpcService/delete_instance', request_serializer=instances__pb2.DeleteInstanceRequest.SerializeToString, response_deserializer=instances__pb2.DeleteInstanceResponse.FromString, - ) + _registered_method=True) class InstanceGrpcServiceServicer(object): @@ -74,6 +93,7 @@ def add_InstanceGrpcServiceServicer_to_server(servicer, server): generic_handler = grpc.method_handlers_generic_handler( 'omnivirt.InstanceGrpcService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('omnivirt.InstanceGrpcService', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -91,11 +111,21 @@ class InstanceGrpcService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/omnivirt.InstanceGrpcService/list_instances', + return grpc.experimental.unary_unary( + request, + target, + '/omnivirt.InstanceGrpcService/list_instances', instances__pb2.ListInstancesRequest.SerializeToString, instances__pb2.ListInstancesResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def create_instance(request, @@ -108,11 +138,21 @@ class InstanceGrpcService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/omnivirt.InstanceGrpcService/create_instance', + return grpc.experimental.unary_unary( + request, + target, + '/omnivirt.InstanceGrpcService/create_instance', instances__pb2.CreateInstanceRequest.SerializeToString, instances__pb2.CreateInstanceResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def delete_instance(request, @@ -125,8 +165,18 @@ class InstanceGrpcService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/omnivirt.InstanceGrpcService/delete_instance', + return grpc.experimental.unary_unary( + request, + target, + '/omnivirt.InstanceGrpcService/delete_instance', instances__pb2.DeleteInstanceRequest.SerializeToString, instances__pb2.DeleteInstanceResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/eulerlauncher/macos-gui.py b/eulerlauncher/macos-gui.py index 3562e4f6db2f6a590aa21af157161104c6efda4d..31216867462957c0f6a7110bceba91020386ef04 100644 --- a/eulerlauncher/macos-gui.py +++ b/eulerlauncher/macos-gui.py @@ -41,7 +41,7 @@ if __name__ == '__main__': except Exception as e: print('Error: ' + str(e)) else: - launcherd_cmd = ['sudo', os.path.join(base_dir,'./bin/EulerLauncherd'), CONF_DIR_SHELL, base_dir] + launcherd_cmd = [os.path.join(base_dir,'./bin/eulerlauncherd'), CONF_DIR_SHELL] launcherd = subprocess.Popen(' '.join(launcherd_cmd), shell=True, preexec_fn=os.setsid) def term_handler(signum, frame): diff --git a/eulerlauncher/services/image_service.py b/eulerlauncher/services/image_service.py new file mode 100644 index 0000000000000000000000000000000000000000..a16868395f35403a00349e5ca4ecdcfeaac64be4 --- /dev/null +++ b/eulerlauncher/services/image_service.py @@ -0,0 +1,74 @@ +import os + +from eulerlauncher.grpcs.eulerlauncher_grpc import images_pb2, images_pb2_grpc +from eulerlauncher.utils import constants + + +class ImageService(images_pb2_grpc.ImageGrpcServiceServicer): + ''' + The Image GRPC Handler + ''' + + def __init__(self, arch, host_os, CONF, LOG) -> None: + self.CONF = CONF + self.LOG = LOG + self.work_dir = self.CONF.conf.get('default', 'work_dir') + self.image_dir = os.path.join(self.work_dir, 'images') + self.image_record_file = os.path.join(self.image_dir, 'images.json') + if host_os == 'Win': + from eulerlauncher.backends.win import image_handler as win_image_handler + self.backend = win_image_handler.WinImageHandler( + self.CONF, self.work_dir, self.image_dir, self.LOG) + elif host_os == 'MacOS': + from eulerlauncher.backends.mac import image_handler as mac_image_handler + self.backend = mac_image_handler.MacImageHandler( + self.CONF, self.work_dir, self.image_dir, self.LOG) + + + def list_images(self, request, context): + self.LOG.debug(f"Get request to list images ...") + all_images = self.backend.list_images() + ret = [] + for image in all_images: + ret.append({ + 'name': image['name'], + 'location': image['location'], + 'status': image['status'] + }) + return images_pb2.ListImageResponse(images=ret) + + + def download_image(self, request, context): + self.LOG.debug(f"Get request to download image: {request.name} ...") + ret = self.backend.download_image(request.name) + msg = '' + if ret == 0: + msg = f'Downloading: {request.name}, this might take a while, please check image status with "images" command.' + elif ret == 1: + msg = f'Image: {request.name} is valid for download, please check image name from REMOTE IMAGE LIST using "images" command ...' + return images_pb2.GeneralImageResponse(ret=ret, msg=msg) + + + def delete_image(self, request, context): + self.LOG.debug(f"Get request to delete image: {request.name} ...") + ret = self.backend.delete_image(request.name) + msg = '' + if ret == 0: + msg = f'Image: {request.name} has been successfully deleted.' + elif ret == 1: + msg = f'Image: {request.name} does not exist, please check again.' + return images_pb2.GeneralImageResponse(ret=ret, msg=msg) + + + def load_image(self, request, context): + self.LOG.debug(f"Get request to load image: {request.name} from path: {request.path} ...") + ret = self.backend.load_image(request.name, request.path) + msg = '' + if ret == 0: + msg = f'Loading: {request.name}, this might take a while, please check image status with "images" command.' + elif ret == 1: + msg = f'Image: {request.path} does not exist, please check again.' + elif ret == 2: + supported_fmt = ', '.join(constants.IMAGE_LOAD_SUPPORTED_TYPES) + msg = f'Unsupported image format, the current supported format are: {supported_fmt}.' + return images_pb2.GeneralImageResponse(ret=ret, msg=msg) diff --git a/eulerlauncher/services/imager_service.py b/eulerlauncher/services/imager_service.py deleted file mode 100644 index 0d1fb8eb82d23aa81d1b05117536a59856cc39e9..0000000000000000000000000000000000000000 --- a/eulerlauncher/services/imager_service.py +++ /dev/null @@ -1,106 +0,0 @@ -import logging -import os - -from eulerlauncher.backends.mac import image_handler as mac_image_handler -from eulerlauncher.backends.win import image_handler as win_image_handler -from eulerlauncher.grpcs.eulerlauncher_grpc import images_pb2, images_pb2_grpc -from eulerlauncher.utils import constants as omni_constants -from eulerlauncher.utils import utils as omni_utils - - -LOG = logging.getLogger(__name__) - - -class ImagerService(images_pb2_grpc.ImageGrpcServiceServicer): - ''' - The Imager GRPC Handler - ''' - - def __init__(self, arch, host_os, conf, svc_base_dir) -> None: - self.CONF = conf - self.svc_base_dir = svc_base_dir - self.work_dir = self.CONF.conf.get('default', 'work_dir') - self.image_dir = os.path.join(self.work_dir, 'images') - self.img_record_file = os.path.join(self.image_dir, 'images.json') - if host_os == 'Win': - self.backend = win_image_handler.WinImageHandler( - self.CONF, self.work_dir, self.image_dir, self.img_record_file, LOG) - elif host_os == 'MacOS': - self.backend = mac_image_handler.MacImageHandler( - self.CONF, self.work_dir, self.image_dir, self.img_record_file, - LOG, self.svc_base_dir) - - def list_images(self, request, context): - LOG.debug(f"Get request to list images ...") - all_images = omni_utils.load_json_data(self.img_record_file) - - ret = [] - for _, images in all_images.items(): - for _, img in images.items(): - image = images_pb2.Image() - image.name = img['name'] - image.location = img['location'] - image.status = img['status'] - ret.append(image) - LOG.debug(f"Responded: {ret}") - return images_pb2.ListImageResponse(images=ret) - - def download_image(self, request, context): - LOG.debug(f"Get request to download image: {request.name} ...") - all_images = omni_utils.load_json_data(self.img_record_file) - - if request.name not in all_images['remote'].keys(): - LOG.debug(f'Image: {request.name} not valid for download') - msg = f'Error: Image {request.name} is valid for download, please check image name from REMOTE IMAGE LIST using "images" command ...' - return images_pb2.GeneralImageResponse(ret=1, msg=msg) - - @omni_utils.asyncwrapper - def do_download(images, name): - self.backend.download_and_transform(images, name) - - do_download(all_images, request.name) - - msg = f'Downloading: {request.name}, this might take a while, please check image status with "images" command.' - return images_pb2.GeneralImageResponse(ret=0, msg=msg) - - def load_image(self, request, context): - LOG.debug(f"Get request to load image: {request.name} from path: {request.path} ...") - - supported, fmt = omni_utils.check_file_tail( - request.path, omni_constants.IMAGE_LOAD_SUPPORTED_TYPES) - - if not supported: - supported_fmt = ', '.join(omni_constants.IMAGE_LOAD_SUPPORTED_TYPES) - msg = f'Unsupported image format, the current supported format are: {supported_fmt}.' - - return images_pb2.GeneralImageResponse(ret=1, msg=msg) - - all_images = omni_utils.load_json_data(self.img_record_file) - - msg = f'Loading: {request.name}, this might take a while, please check image status with "images" command.' - update = False - - local_images = all_images['local'] - if request.name in local_images.keys(): - LOG.debug(f"Image: {request.name} already existed, replace it with: {request.path} ...") - msg = f'Replacing: {request.name}, with new image file: {request.path}, this might take a while, please check image status with "images" command.' - update = True - - @omni_utils.asyncwrapper - def do_load(images, name, path, fmt, update): - self.backend.load_and_transform(images, name, path, fmt, update) - - do_load(all_images, request.name, request.path, fmt, update) - - return images_pb2.GeneralImageResponse(ret=0, msg=msg) - - def delete_image(self, request, context): - LOG.debug(f"Get request to delete image: {request.name} ...") - images = omni_utils.load_json_data(self.img_record_file) - ret = self.backend.delete_image(images, request.name) - if ret == 0: - msg = f'Image: {request.name} has been successfully deleted.' - elif ret == 1: - msg = f'Image: {request.name} does not exist, please check again.' - - return images_pb2.GeneralImageResponse(ret=1, msg=msg) diff --git a/eulerlauncher/services/instance_service.py b/eulerlauncher/services/instance_service.py index bea002e913f7bceab9ce1b0dea845477c430938b..47267a275722dcf80eba2188053e9aa8ff399096 100644 --- a/eulerlauncher/services/instance_service.py +++ b/eulerlauncher/services/instance_service.py @@ -1,81 +1,63 @@ -import logging import os from eulerlauncher.grpcs.eulerlauncher_grpc import instances_pb2, instances_pb2_grpc -from eulerlauncher.utils import utils -LOG = logging.getLogger(__name__) - class InstanceService(instances_pb2_grpc.InstanceGrpcServiceServicer): ''' The Instance GRPC Handler ''' - def __init__(self, arch, host_os, conf, svc_base_dir) -> None: - self.CONF = conf - self.svc_base_dir = svc_base_dir + def __init__(self, host_arch, host_os, CONF, LOG) -> None: + self.CONF = CONF + self.LOG = LOG self.work_dir = self.CONF.conf.get('default', 'work_dir') self.instance_dir = os.path.join(self.work_dir, 'instances') self.instance_record_file = os.path.join(self.instance_dir, 'instances.json') self.image_dir = os.path.join(self.work_dir, 'images') - self.img_record_file = os.path.join(self.image_dir, 'images.json') + self.image_record_file = os.path.join(self.image_dir, 'images.json') if host_os == 'Win': from eulerlauncher.backends.win import instance_handler as win_instance_handler self.backend = win_instance_handler.WinInstanceHandler( - self.CONF, self.work_dir, self.instance_dir, self.image_dir, self.img_record_file, LOG) + self.CONF, self.work_dir, self.instance_dir, self.image_dir, self.LOG) elif host_os == 'MacOS': from eulerlauncher.backends.mac import instance_handler as mac_instance_handler self.backend = mac_instance_handler.MacInstanceHandler( - self.CONF, self.work_dir, self.instance_dir, self.image_dir, - self.img_record_file, LOG, self.svc_base_dir) + self.CONF, self.work_dir, self.instance_dir, self.image_dir, self.LOG) def list_instances(self, request, context): - LOG.debug(f"Get request to list instances ...") - instances_obj = self.backend.list_instances() - + self.LOG.debug(f"Get request to list instances ...") + all_instances = self.backend.list_instances() ret = [] - for vm_obj in instances_obj: - instance_dict = { - 'name': vm_obj.name, - 'image': vm_obj.image, - 'vm_state': vm_obj.vm_state, - 'ip_address': vm_obj.ip if vm_obj.ip else 'N/A' - } - ret.append(instance_dict) - + for instance in all_instances: + ret.append({ + 'name': instance['name'], + 'image': instance['image'], + 'vm_state': instance['state'], + 'ip_address': instance['ip_address'] + }) return instances_pb2.ListInstancesResponse(instances=ret) + def create_instance(self, request, context): - LOG.debug(f"Get request to create instance: {request.name} with image {request.image} ...") - - all_img = utils.load_json_data(self.img_record_file) - if request.image not in all_img['local'].keys(): + self.LOG.debug(f"Get request to create instance: {request.name} with image: {request.image} ...") + ret = self.backend.create_instance(request.name, request.image) + msg = '' + if ret == 0: + msg = f'Successfully created instance: {request.name} with image: {request.image}.' + elif ret == 1: msg = f'Error: Image "{request.image}" is not available locally, please check again or (down)load it before using ...' - return instances_pb2.CreateInstanceResponse(ret=2, msg=msg) - - all_instances = utils.load_json_data(self.instance_record_file) - if request.name in all_instances['instances'].keys(): + elif ret == 2: msg = f'Error: Instance with name {request.name} already exist, please specify another name.' - return instances_pb2.CreateInstanceResponse(ret=2, msg=msg) - - check_result = self.backend.check_names(request.name, all_instances) - if check_result == 1: - msg = f'Error: Instance with name {request.name} already exist in exixting Hyper-V or Qemu backend, please specify another name.' - return instances_pb2.CreateInstanceResponse(ret=2, msg=msg) - - vm = self.backend.create_instance( - request.name, request.image, self.instance_record_file, all_instances, all_img) - msg = f'Successfully created {request.name} with image {request.image}' - return instances_pb2.CreateInstanceResponse(ret=1, msg=msg, instance=vm) + return instances_pb2.CreateInstanceResponse(ret=ret, msg=msg) + def delete_instance(self, request, context): - LOG.debug(f"Get request to delete instance: {request.name} ...") - all_instances = utils.load_json_data(self.instance_record_file) - if request.name not in all_instances['instances'].keys(): + self.LOG.debug(f"Get request to delete instance: {request.name} ...") + ret = self.backend.delete_instance(request.name) + msg = '' + if ret == 0: + msg = f'Successfully deleted instance: {request.name}.' + elif ret == 1: msg = f'Error: Instance with name {request.name} does not exist.' - return instances_pb2.DeleteInstanceResponse(ret=2, msg=msg) - - self.backend.delete_instance(request.name, self.instance_record_file, all_instances) - msg = f'Successfully deleted instance: {request.name}.' - return instances_pb2.DeleteInstanceResponse(ret=1, msg=msg) + return instances_pb2.DeleteInstanceResponse(ret=ret, msg=msg) diff --git a/eulerlauncher/utils/constants.py b/eulerlauncher/utils/constants.py index 9383d2d5f2d0edf75c689631eb017ec0d846b989..0be17e80e361a55e323f52239a6ca1528a55954a 100644 --- a/eulerlauncher/utils/constants.py +++ b/eulerlauncher/utils/constants.py @@ -22,14 +22,17 @@ ARCH_MAP = { 'x86_64': 'x86_64' } -VM_STATE_MAP = { - 2: 'Running', - 3: 'Stopped', - 10: 'Rebooting', - 32768: 'Paused', - 32769: 'Suspended', +INSTANCE_STATE_MAP = { + 0: 'N/A', + 1: 'Running', + 2: 'Blocked', + 3: 'Paused', + 4: 'Shutdown', + 5: 'Shutoff', + 6: 'Crashed', + 7: 'Suspended', 99: 'N/A' - } +} OS_MAP = { 'Darwin': 'MacOS', diff --git a/eulerlauncher/utils/objs.py b/eulerlauncher/utils/objs.py index 714e26d3247e638c9c7748080bb0550d2f9006d5..55c90a912aa3042a1ba031c284b1175978c5a47b 100644 --- a/eulerlauncher/utils/objs.py +++ b/eulerlauncher/utils/objs.py @@ -2,47 +2,6 @@ import os import configparser from eulerlauncher.utils import exceptions -from eulerlauncher.utils import constants - -class Instance(object): - - def __init__(self, name='') -> None: - self.name = name - self.uuid = '' - self.identifier = {} - self.metadata = None - self.vm_state = None - self.vcpu = None - self.ram = None - self.disk = None - self.info = None - self.image = None - self.ip = 'N/A' - self.mac = 'N/A' - - -class Image(object): - - def __init__(self) -> None: - self.name = '' - self.location = '' - self.status = constants.IMAGE_STATUS_INIT - self.path = '' - - def to_dict(self): - image_dict = { - 'name': self.name, - 'location': self.location, - 'status': self.status, - 'path': self.path - } - return image_dict - - def from_dict(self, img_dict): - self.name = img_dict['name'] - self.location = img_dict['location'] - self.status = img_dict['status'] - self.path = img_dict['path'] class Conf(object): diff --git a/eulerlauncher/utils/utils.py b/eulerlauncher/utils/utils.py index 8381c74aaddaa9d9418fdc9e08dfa1c014ab1e7b..190baa8634a0d123164734142a9f3ef4c7b6b939 100644 --- a/eulerlauncher/utils/utils.py +++ b/eulerlauncher/utils/utils.py @@ -3,6 +3,7 @@ import json import os import random from threading import Thread +from lxml import etree import uuid @@ -52,13 +53,21 @@ def format_mac_addr(mac_str): def load_json_data(json_file): with open(json_file, 'r', encoding='utf-8') as fr: - data = json.load(fr) + data = json.load(fr) return data def save_json_data(json_file, data): with open(json_file, 'w', encoding='utf-8') as fw: - json.dump(data, fw, indent=4, ensure_ascii=False) + json.dump(data, fw, indent=4, ensure_ascii=False) + +def load_xml_data(xml_file): + data = etree.parse(xml_file) + return data + +def save_xml_data(xml_file, data): + with open(xml_file, 'wb') as fw: + fw.write(etree.tostring(data, pretty_print=True, encoding='utf-8')) def generate_mac(): local_mac = uuid.uuid1().hex[-12:] diff --git a/requirements-win.txt b/requirements-win.txt index b67b36dc4415cf837515d153a8423b9e6e45fb9a..2de7fd5ab6ee2ef0f44f32a2db1ad6182528529a 100644 --- a/requirements-win.txt +++ b/requirements-win.txt @@ -22,3 +22,4 @@ PyYAML six urllib3 wget +libvirt-python \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index db5e0f68c92003b71fc38050787376e243cb4a4a..07ff4f2eb6b253cd04fcdc434bbc5fc699f7a7f4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ oslo.i18n oslo.log oslo.serialization oslo.utils -Pillow +pillow prettytable protobuf psutil @@ -23,3 +23,4 @@ PyYAML six urllib3 wget +libvirt-python \ No newline at end of file diff --git a/resources/.DS_Store b/resources/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..431a59269701140d86619ca4654664d1ac2f222c Binary files /dev/null and b/resources/.DS_Store differ diff --git a/resources/libvirt/.DS_Store b/resources/libvirt/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ae56b38ac304ba2a6311ecd6fa1e7df569669e4c Binary files /dev/null and b/resources/libvirt/.DS_Store differ diff --git a/resources/libvirt/libvirt-aarch64.xml b/resources/libvirt/libvirt-aarch64.xml new file mode 100644 index 0000000000000000000000000000000000000000..822c77564a9d9a37b5b270aee84fb9aeabd5681a --- /dev/null +++ b/resources/libvirt/libvirt-aarch64.xml @@ -0,0 +1,31 @@ + + + + + + + hvm + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/libvirt/libvirt-x86_64.xml b/resources/libvirt/libvirt-x86_64.xml new file mode 100644 index 0000000000000000000000000000000000000000..7e711cee9f607f8d932166b95094f62a62531271 --- /dev/null +++ b/resources/libvirt/libvirt-x86_64.xml @@ -0,0 +1,30 @@ + + + + + + hvm + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/qemu/edk2-aarch64-code.fd b/resources/qemu/edk2-aarch64-code.fd deleted file mode 100644 index 832bfe21451ce2a2cc71f7ad91a92d20a372e8cf..0000000000000000000000000000000000000000 Binary files a/resources/qemu/edk2-aarch64-code.fd and /dev/null differ diff --git a/resources/qemu/edk2-x86_64-code.fd b/resources/qemu/edk2-x86_64-code.fd deleted file mode 100644 index b1cb5da36aee2f9b3c243c721cd6e44ae36c121c..0000000000000000000000000000000000000000 Binary files a/resources/qemu/edk2-x86_64-code.fd and /dev/null differ diff --git a/specs/EulerLauncher-MacOS.spec b/specs/EulerLauncher-Mac.spec similarity index 84% rename from specs/EulerLauncher-MacOS.spec rename to specs/EulerLauncher-Mac.spec index 8952fca46ba4eb075a554fdcf7e45e50327179e3..50029630c88b065f148fc0ff7463b4f9e5adfbd6 100644 --- a/specs/EulerLauncher-MacOS.spec +++ b/specs/EulerLauncher-Mac.spec @@ -7,8 +7,8 @@ block_cipher = None a = Analysis( ['../eulerlauncher/macos-gui.py'], pathex=[], - binaries=[('../dist/EulerLauncherd', './bin')], - datas=[('../etc/eulerlauncher.conf', './etc'), ('../etc/images/favicon.png', './etc'), ('../resources/qemu/edk2-aarch64-code.fd', './etc'), ('../resources/qemu/edk2-x86_64-code.fd', './etc')], + binaries=[('../dist/eulerLauncherd', './bin')], + datas=[('../etc/eulerlauncher.conf', './etc'), ('../etc/images/favicon.png', './etc'), ('../resources/libvirt/libvirt-aarch64.xml', './etc'), ('../resources/libvirt/libvirt-x86_64.xml', './etc')], hiddenimports=[], hookspath=[], hooksconfig={}, diff --git a/specs/EulerLauncherd-Mac.spec b/specs/EulerLauncherd-Mac.spec index 9dbbc2894fc5bbdf7c38e5c54af73e6436e1242f..75efb54821eccaaf1fedf80606f0f695bf03745b 100644 --- a/specs/EulerLauncherd-Mac.spec +++ b/specs/EulerLauncherd-Mac.spec @@ -28,7 +28,7 @@ exe = EXE( a.zipfiles, a.datas, [], - name='EulerLauncherd', + name='eulerlauncherd', debug=False, bootloader_ignore_signals=False, strip=False, diff --git a/specs/install.spec b/specs/install.spec index ae6258c6c45689a26a4f2a5bbb089c7963ec2e08..97e503fcf87b86d529e1fee161b72aabb3abb954 100644 --- a/specs/install.spec +++ b/specs/install.spec @@ -8,7 +8,7 @@ a = Analysis( ['../eulerlauncher/install.py'], pathex=[], binaries=[('../dist/eulerlauncher', './etc')], - datas=[('../etc/eulerlauncher.conf', './etc'), ('../resources/qemu/edk2-aarch64-code.fd', './etc'), ('../resources/qemu/edk2-x86_64-code.fd', './etc')], + datas=[('../etc/eulerlauncher.conf', './etc'), ('../resources/libvirt/libvirt-aarch64.xml', './etc'), ('../resources/libvirt/libvirt-x86_64.xml', './etc')], hiddenimports=[], hookspath=[], hooksconfig={},