From 3364de050373aab4cff07e394287cbae2f937eb0 Mon Sep 17 00:00:00 2001 From: wangshj40 <1026749812@qq.com> Date: Thu, 18 May 2023 16:32:03 +0800 Subject: [PATCH] 1 --- ...irt-MacOS.spec => EulerLauncher-MacOS.spec | 10 +- OmniVirtd-Mac.spec => EulerLauncherd-Mac.spec | 4 +- cli.spec | 4 +- docs/developer-manual.md | 22 +- docs/mac-user-manual.md | 66 +-- docs/win-user-manual.md | 54 +-- ...nivirt-win.conf => eulerlauncher-win.conf} | 10 +- etc/{omnivirt.conf => eulerlauncher.conf} | 18 +- {omnivirt => eulerlauncher}/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin .../backends/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin .../backends/mac/image_handler.py | 236 +++++------ .../backends/mac/instance_handler.py | 354 ++++++++-------- .../backends/mac/qemu.py | 60 +-- .../backends/win/__init__.py | 0 .../win/__pycache__/__init__.cpython-310.pyc | Bin .../backends/win/image_handler.py | 246 +++++------ .../backends/win/instance_handler.py | 400 +++++++++--------- .../backends/win/powershell.py | 98 ++--- .../backends/win/vmops.py | 362 ++++++++-------- {omnivirt => eulerlauncher}/cli.py | 286 ++++++------- .../eulerlauncherd.py | 296 ++++++------- {omnivirt => eulerlauncher}/grpcs/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin {omnivirt => eulerlauncher}/grpcs/client.py | 190 ++++----- {omnivirt => eulerlauncher}/grpcs/images.py | 58 +-- .../grpcs/instances.py | 46 +- .../grpcs/omnivirt_grpc/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin .../grpcs/omnivirt_grpc/images.proto | 92 ++-- .../grpcs/omnivirt_grpc/images_pb2.py | 0 .../grpcs/omnivirt_grpc/images_pb2_grpc.py | 0 .../grpcs/omnivirt_grpc/instances.proto | 96 ++--- .../grpcs/omnivirt_grpc/instances_pb2.py | 0 .../grpcs/omnivirt_grpc/instances_pb2_grpc.py | 0 {omnivirt => eulerlauncher}/install.py | 0 {omnivirt => eulerlauncher}/macos-gui.py | 0 .../services/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin .../services/imager_service.py | 212 +++++----- .../services/instance_service.py | 162 +++---- {omnivirt => eulerlauncher}/utils/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin .../utils/constants.py | 72 ++-- .../utils/exceptions.py | 120 +++--- {omnivirt => eulerlauncher}/utils/objs.py | 108 ++--- {omnivirt => eulerlauncher}/utils/utils.py | 188 ++++---- install.spec | 6 +- setup.cfg | 8 +- 50 files changed, 1942 insertions(+), 1942 deletions(-) rename OmniVirt-MacOS.spec => EulerLauncher-MacOS.spec (73%) rename OmniVirtd-Mac.spec => EulerLauncherd-Mac.spec (91%) rename etc/{omnivirt-win.conf => eulerlauncher-win.conf} (95%) rename etc/{omnivirt.conf => eulerlauncher.conf} (91%) rename {omnivirt => eulerlauncher}/__init__.py (100%) rename {omnivirt => eulerlauncher}/__pycache__/__init__.cpython-310.pyc (100%) rename {omnivirt => eulerlauncher}/backends/__init__.py (100%) rename {omnivirt => eulerlauncher}/backends/__pycache__/__init__.cpython-310.pyc (100%) rename {omnivirt => eulerlauncher}/backends/mac/image_handler.py (97%) rename {omnivirt => eulerlauncher}/backends/mac/instance_handler.py (97%) rename {omnivirt => eulerlauncher}/backends/mac/qemu.py (97%) rename {omnivirt => eulerlauncher}/backends/win/__init__.py (100%) rename {omnivirt => eulerlauncher}/backends/win/__pycache__/__init__.cpython-310.pyc (100%) rename {omnivirt => eulerlauncher}/backends/win/image_handler.py (97%) rename {omnivirt => eulerlauncher}/backends/win/instance_handler.py (97%) rename {omnivirt => eulerlauncher}/backends/win/powershell.py (97%) rename {omnivirt => eulerlauncher}/backends/win/vmops.py (97%) rename {omnivirt => eulerlauncher}/cli.py (64%) rename omnivirt/omnivirtd.py => eulerlauncher/eulerlauncherd.py (85%) rename {omnivirt => eulerlauncher}/grpcs/__init__.py (100%) rename {omnivirt => eulerlauncher}/grpcs/__pycache__/__init__.cpython-310.pyc (100%) rename {omnivirt => eulerlauncher}/grpcs/client.py (96%) rename {omnivirt => eulerlauncher}/grpcs/images.py (97%) rename {omnivirt => eulerlauncher}/grpcs/instances.py (97%) rename {omnivirt => eulerlauncher}/grpcs/omnivirt_grpc/__init__.py (100%) rename {omnivirt => eulerlauncher}/grpcs/omnivirt_grpc/__pycache__/__init__.cpython-310.pyc (100%) rename {omnivirt => eulerlauncher}/grpcs/omnivirt_grpc/images.proto (94%) rename {omnivirt => eulerlauncher}/grpcs/omnivirt_grpc/images_pb2.py (100%) rename {omnivirt => eulerlauncher}/grpcs/omnivirt_grpc/images_pb2_grpc.py (100%) rename {omnivirt => eulerlauncher}/grpcs/omnivirt_grpc/instances.proto (94%) rename {omnivirt => eulerlauncher}/grpcs/omnivirt_grpc/instances_pb2.py (100%) rename {omnivirt => eulerlauncher}/grpcs/omnivirt_grpc/instances_pb2_grpc.py (100%) rename {omnivirt => eulerlauncher}/install.py (100%) rename {omnivirt => eulerlauncher}/macos-gui.py (100%) rename {omnivirt => eulerlauncher}/services/__init__.py (100%) rename {omnivirt => eulerlauncher}/services/__pycache__/__init__.cpython-310.pyc (100%) rename {omnivirt => eulerlauncher}/services/imager_service.py (97%) rename {omnivirt => eulerlauncher}/services/instance_service.py (98%) rename {omnivirt => eulerlauncher}/utils/__init__.py (100%) rename {omnivirt => eulerlauncher}/utils/__pycache__/__init__.cpython-310.pyc (100%) rename {omnivirt => eulerlauncher}/utils/constants.py (95%) rename {omnivirt => eulerlauncher}/utils/exceptions.py (96%) rename {omnivirt => eulerlauncher}/utils/objs.py (96%) rename {omnivirt => eulerlauncher}/utils/utils.py (95%) diff --git a/OmniVirt-MacOS.spec b/EulerLauncher-MacOS.spec similarity index 73% rename from OmniVirt-MacOS.spec rename to EulerLauncher-MacOS.spec index 12a5e61..758124f 100644 --- a/OmniVirt-MacOS.spec +++ b/EulerLauncher-MacOS.spec @@ -5,10 +5,10 @@ block_cipher = None a = Analysis( - ['omnivirt/macos-gui.py'], + ['eulerlauncher/macos-gui.py'], pathex=[], - binaries=[('dist/OmniVirtd', './bin')], - datas=[('etc/omnivirt.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/qemu/edk2-aarch64-code.fd', './etc'), ('resources/qemu/edk2-x86_64-code.fd', './etc')], hiddenimports=[], hookspath=[], hooksconfig={}, @@ -28,7 +28,7 @@ exe = EXE( a.zipfiles, a.datas, [], - name='OmniVirt', + name='EulerLauncher', debug=False, bootloader_ignore_signals=False, strip=False, @@ -46,7 +46,7 @@ exe = EXE( ) app = BUNDLE( exe, - name='OmniVirt.app', + name='EulerLauncher.app', icon='etc/images/favicon.ico', bundle_identifier=None, ) diff --git a/OmniVirtd-Mac.spec b/EulerLauncherd-Mac.spec similarity index 91% rename from OmniVirtd-Mac.spec rename to EulerLauncherd-Mac.spec index 3a8748b..f6cc372 100644 --- a/OmniVirtd-Mac.spec +++ b/EulerLauncherd-Mac.spec @@ -5,7 +5,7 @@ block_cipher = None a = Analysis( - ['omnivirt/omnivirtd.py'], + ['eulerlauncher/eulerlauncherd.py'], pathex=[], binaries=[], datas=[], @@ -28,7 +28,7 @@ exe = EXE( a.zipfiles, a.datas, [], - name='OmniVirtd', + name='EulerLauncherd', debug=False, bootloader_ignore_signals=False, strip=False, diff --git a/cli.spec b/cli.spec index bfaf66d..574d7c7 100644 --- a/cli.spec +++ b/cli.spec @@ -5,7 +5,7 @@ block_cipher = None a = Analysis( - ['omnivirt/cli.py'], + ['eulerlauncher/cli.py'], pathex=[], binaries=[], datas=[], @@ -28,7 +28,7 @@ exe = EXE( a.zipfiles, a.datas, [], - name='omnivirt', + name='eulerlauncher', debug=False, bootloader_ignore_signals=False, strip=False, diff --git a/docs/developer-manual.md b/docs/developer-manual.md index 86c398e..3e7177f 100644 --- a/docs/developer-manual.md +++ b/docs/developer-manual.md @@ -46,28 +46,28 @@ pip3 install -r requirements.txt EulerLauncher可执行文件包括以下几个部分: -1. EulerLauncherd: omnivirt守护进程,以root权限运行在后台,与调用虚拟化组件(Qemu、HyperV、KVM等)及镜像组件进行相关操作; -2. EulerLauncher.app: OmniVirt服务端主程序,将omnivirtd及其他相关程序、数据、文件等打包为MacOS APP软件包,便于分发和使用。 -3. EulerLauncher: MacOS可执行文件,OmniVirt客户端CLI工具,用于与服务端交互。 -4. install: MacOS可执行文件,将OmniVirt运行所需配置文件及相关数据文件安装至`Application Support`文件夹。 +1. EulerLauncherd: eulerlauncher守护进程,以root权限运行在后台,与调用虚拟化组件(Qemu、HyperV、KVM等)及镜像组件进行相关操作; +2. EulerLauncher.app: EulerLauncher服务端主程序,将eulerlauncherd及其他相关程序、数据、文件等打包为MacOS APP软件包,便于分发和使用。 +3. EulerLauncher: MacOS可执行文件,EulerLauncher客户端CLI工具,用于与服务端交互。 +4. install: MacOS可执行文件,将EulerLauncher运行所需配置文件及相关数据文件安装至`Application Support`文件夹。 由于`EulerLauncher.app`对`EulerLauncherd`有依赖关系,请严格按照以下顺序构建`EulerLauncherd`及`EulerLauncher.app`: 1. EulerLauncherd: - 项目源码中已包含用于构建OmniVirtd的Spec脚本`OmniVirtd-Mac.spec`, 若非必要,请勿修改该文件,使用一下命令开始构建: + 项目源码中已包含用于构建EulerLauncherd的Spec脚本`EulerLauncherd-Mac.spec`, 若非必要,请勿修改该文件,使用一下命令开始构建: ``` Shell - pyinstaller --clean --noconfirm OmniVirtd-Mac.spec + pyinstaller --clean --noconfirm EulerLauncherd-Mac.spec ``` 2. EulerLauncher.app: - 项目源码中已包含用于构建OmniVirt的Spec脚本`OmniVirt-MacOS.spec`, 若非必要,请勿修改该文件,使用一下命令开始构建: + 项目源码中已包含用于构建EulerLauncher的Spec脚本`EulerLauncher-MacOS.spec`, 若非必要,请勿修改该文件,使用一下命令开始构建: ``` Shell - pyinstaller --clean --noconfirm OmniVirt-MacOS.spec + pyinstaller --clean --noconfirm EulerLauncher-MacOS.spec ``` -构建`omnivirt` CLI 及 `install` 脚本, cli与install之间有依赖关系,请严格按照下面的顺序进行构建: +构建`eulerlauncher` CLI 及 `install` 脚本, cli与install之间有依赖关系,请严格按照下面的顺序进行构建: ``` Shell pyinstaller --clean --noconfirm cli.spec @@ -79,12 +79,12 @@ pyinstaller --clean --noconfirm install.spec 首先,我们创建一个新目录并将文件移动到其中。 ``` Shell mkdir -p dist/dmg -cp -R dist/OmniVirt.app dist/dmg +cp -R dist/EulerLauncher.app dist/dmg ``` 然后,我们可以使用下面的命令来制作磁盘镜像文件: ``` Shell -create-dmg --volname "OmniVirt" --volicon "etc/images/favicon.png" --window-pos 200 120 --window-size 600 300 --icon-size 100 --icon "OmniVirt.app" 175 120 --hide-extension "OmniVirt.app" --app-drop-link 425 120 "dist/OmniVirt.dmg" "dist/dmg/" +create-dmg --volname "EulerLauncher" --volicon "etc/images/favicon.png" --window-pos 200 120 --window-size 600 300 --icon-size 100 --icon "EulerLauncher.app" 175 120 --hide-extension "EulerLauncher.app" --app-drop-link 425 120 "dist/EulerLauncher.dmg" "dist/dmg/" ``` `EulerLauncher.dmg`中将只包含`EulerLauncher.app`主程序,需要将`install`脚本及`EulerLauncher` CLI工具一并压缩后再进行分发。 diff --git a/docs/mac-user-manual.md b/docs/mac-user-manual.md index a73ed41..7356a70 100644 --- a/docs/mac-user-manual.md +++ b/docs/mac-user-manual.md @@ -1,4 +1,4 @@ -# 在MacOS下安装与运行OmniVirt +# 在MacOS下安装与运行EulerLauncher ## 准备工作 @@ -23,7 +23,7 @@ Homebrew是一款Mac OS平台下的软件包管理工具,拥有安装、卸载 ### 安装Qemu及wget -**OmniVirt**在MacOS上运行依赖于`QEMU`,镜像下载依赖于`wget`,使用`Homebrew`可以非常方便的下载和管理此类软件,使用以下命令进行安装: +**EulerLauncher**在MacOS上运行依赖于`QEMU`,镜像下载依赖于`wget`,使用`Homebrew`可以非常方便的下载和管理此类软件,使用以下命令进行安装: ``` Shell brew install qemu @@ -32,7 +32,7 @@ brew install wget ### 配置sudo免密码权限 -**OmniVirt**在MacOS上运行依赖于`QEMU`,为了使用户的网络体验更加优秀,因此采用了MacOS的[vmnet framework][1]来提供虚拟机的网络能力,当前`vmnet`使用时需要使用管理员权限,因此在使用`QEMU`后端创建带有`vmnet`类型网络设备的虚拟机时,需要启用管理员权限,OmniVirt在启动时会自动使用`sudo`命令来实现这一过程,因此需要为当前用户配置`sudo`免密码使用权限,如您介意此配置,请停止使用OmniVirt。 +**EulerLauncher**在MacOS上运行依赖于`QEMU`,为了使用户的网络体验更加优秀,因此采用了MacOS的[vmnet framework][1]来提供虚拟机的网络能力,当前`vmnet`使用时需要使用管理员权限,因此在使用`QEMU`后端创建带有`vmnet`类型网络设备的虚拟机时,需要启用管理员权限,OmniVirt在启动时会自动使用`sudo`命令来实现这一过程,因此需要为当前用户配置`sudo`免密码使用权限,如您介意此配置,请停止使用EulerLauncher。 1. 在MacOS桌面下敲击 `command` + `shift` + `u` 组合键,打开`访达`中的`实用工具`,并找到`终端.app` @@ -44,19 +44,19 @@ brew install wget 4. 敲击`ESC`,再输入`:WQ`进行保存 -## 安装OmniVirt +## 安装EulerLauncher -**OmniVirt**当前支持MacOS Ventura, 支持Apple Silicon芯片版及x86芯片版,前往[OmniVirt最新版下载][1]下载MacOS版软件包并解压到期望的位置。 +**EulerLauncher**当前支持MacOS Ventura, 支持Apple Silicon芯片版及x86芯片版,前往[EulerLauncher最新版下载][1]下载MacOS版软件包并解压到期望的位置。 解压后的目录包含以下文件: -其中`install`可执行文件为安装文件,用于将**OmniVirt**所需支持文件安装到指定位置,`OmniVirt.dmg`为主程序的磁盘映象。 +其中`install`可执行文件为安装文件,用于将**EulerLauncher**所需支持文件安装到指定位置,`EulerLauncher.dmg`为主程序的磁盘映象。 1. 安装支持文件(本操作需要sudo权限,请先完成前面的步骤):双击`install`可执行文件,等待程序完成执行。 -2. 配置**OmniVirt**: +2. 配置**EulerLauncher**: - 查看`qemu`及`wget`所处位置,`qemu`二进制文件在不同架构下名称不同,请根据自身情况选择正确的名称(Apple Silicon: qemu-system-aarch64; Intel: qemu-system-x86_64): ``` Shell @@ -70,16 +70,16 @@ brew install wget ``` 查看完成后,记录路径结果,在接下来的步骤中将会使用到。 - - 打开`omnivirt.conf`并进行配置: + - 打开`eulerlauncher.conf`并进行配置: ``` Shell - sudo vi /Library/Application\ Support/org.openeuler.omnivirt/omnivirt.conf + sudo vi /Library/Application\ Support/org.openeuler.eulerlauncher/eulerlauncher.conf ``` - omnivirt的配置如下 + eulerlauncher的配置如下 ``` [default] log_dir = # 日志文件位置(xxx.log) - work_dir = # omnivirt工作目录,用于存储虚拟机镜像、虚拟机文件等 + work_dir = # eulerlauncher工作目录,用于存储虚拟机镜像、虚拟机文件等 wget_dir = # wget的可执行文件路径,请参考上一步的内容进行配置 qemu_dir = # qemu的可执行文件路径,请参考上一步的内容进行配置 debug = True @@ -89,27 +89,27 @@ brew install wget memory = 1024 # 配置虚拟机的内存大小,单位为M,M1用户请勿配置超过2048 ``` -3. 安装**OmniVirt.app**: +3. 安装**EulerLauncher.app**: - - 双击`OmniVirt.dmg`,在弹出的窗口中用鼠标将`OmniVirt.app`拖动到`Applications`中,即可完成安装,并可在应用程序中找到`OmniVirt.app` + - 双击`EulerLauncher.dmg`,在弹出的窗口中用鼠标将`EulerLauncher.app`拖动到`Applications`中,即可完成安装,并可在应用程序中找到`EulerLauncher.app` -## 使用OmniVirt +## 使用EulerLauncher -1. 在应用程序中找到`OmniVirt.app`,单击启动程序。 +1. 在应用程序中找到`EulerLauncher.app`,单击启动程序。 -2. OmniVirt需要访问网络,在弹出如下窗口时点击`允许`: +2. EulerLauncher需要访问网络,在弹出如下窗口时点击`允许`: -3. OmniVirt当前仅支持命令行方式进行访问,请打开`终端.app`,使用命令行进行操作。 +3. EulerLauncher当前仅支持命令行方式进行访问,请打开`终端.app`,使用命令行进行操作。 ### 镜像操作 1. 获取可用镜像列表: ```Shell -omnivirt images +eulerlauncher images +-----------+----------+--------------+ | Images | Location | Status | @@ -120,12 +120,12 @@ omnivirt images +-----------+----------+--------------+ ``` -**OmniVirt**镜像有两种位置属性:1)远端镜像 2)本地镜像,只有处于本地且状态为 `Ready` 的镜像可以直接用来创建虚拟机,位于远端的镜像需要下载后才能够使用;你也可以加载已经预先下载好的本地镜像到**OmniVirt**中,具体操作方法可以参考接下来的操作指导。 +**EulerLauncher**镜像有两种位置属性:1)远端镜像 2)本地镜像,只有处于本地且状态为 `Ready` 的镜像可以直接用来创建虚拟机,位于远端的镜像需要下载后才能够使用;你也可以加载已经预先下载好的本地镜像到**EulerLauncher**中,具体操作方法可以参考接下来的操作指导。 2. 下载远端镜像 ```Shell -omnivirt download-image 22.03-LTS +eulerlauncher download-image 22.03-LTS Downloading: 22.03-LTS, this might take a while, please check image status with "images" command. ``` @@ -133,7 +133,7 @@ Downloading: 22.03-LTS, this might take a while, please check image status with 镜像下载请求是一个异步请求,具体的下载动作将在后台完成,具体耗时与你的网络情况相关,整体的镜像下载流程包括下载、解压缩、格式转换等相关子流程,在下载过程中可以通过 `image` 命令随时查看下载进展与镜像状态: ```Shell -omnivirt images +eulerlauncher images +-----------+----------+--------------+ | Images | Location | Status | @@ -148,7 +148,7 @@ omnivirt images 当镜像状态转变为 `Ready` 时,表示镜像下载完成,处于 `Ready` 状态的镜像可被用来创建虚拟机: ```Shell -omnivirt images +eulerlauncher images +-----------+----------+--------------+ | Images | Location | Status | @@ -161,10 +161,10 @@ omnivirt images 3. 加载本地镜像 -用户也可以加载自定义镜像或预先下载到本地的镜像到OmniVirt中用于创建自定义虚拟机: +用户也可以加载自定义镜像或预先下载到本地的镜像到EulerLauncher中用于创建自定义虚拟机: ```Shell -omnivirt load-image --path {image_file_path} IMAGE_NAME +eulerlauncher load-image --path {image_file_path} IMAGE_NAME ``` 当前支持加载的镜像格式有 `xxx.qcow2.xz`,`xxx.qcow2` @@ -172,7 +172,7 @@ omnivirt load-image --path {image_file_path} IMAGE_NAME 例如: ```Shell -omnivirt load-image --path /opt/openEuler-22.03-LTS-x86_64.qcow2.xz 2203-load +eulerlauncher load-image --path /opt/openEuler-22.03-LTS-x86_64.qcow2.xz 2203-load Loading: 2203-load, this might take a while, please check image status with "images" command. ``` @@ -180,7 +180,7 @@ Loading: 2203-load, this might take a while, please check image status with "ima 将位于 `/opt` 目录下的 `openEuler-22.03-LTS-x86_64.qcow2.xz` 加载到OmniVirt系统中,并命名为 `2203-load`,与下载命令一样,加载命令也是一个异步命令,用户需要用镜像列表命令查询镜像状态直到显示为 `Ready`, 但相对于直接下载镜像,加载镜像的速度会快很多: ```Shell -omnivirt images +eulerlauncher images +-----------+----------+--------------+ | Images | Location | Status | @@ -190,7 +190,7 @@ omnivirt images | 2203-load | Local | Loading | +-----------+----------+--------------+ -omnivirt images +eulerlauncher images +-----------+----------+--------------+ | Images | Location | Status | @@ -203,10 +203,10 @@ omnivirt images 4. 删除镜像: -通过下面的命令将镜像从OmniVirt系统中删除: +通过下面的命令将镜像从EulerLauncher系统中删除: ```Shell -omnivirt delete-image 2203-load +eulerlauncher delete-image 2203-load Image: 2203-load has been successfully deleted. ``` @@ -216,7 +216,7 @@ Image: 2203-load has been successfully deleted. 1. 获取虚拟机列表: ```shell -omnivirt list +eulerlauncher list +----------+-----------+---------+---------------+ | Name | Image | State | IP | @@ -241,17 +241,17 @@ ssh root@{instance_ip} 3. 创建虚拟机 ```Shell -omnivirt launch --image {image_name} {instance_name} +eulerlauncher launch --image {image_name} {instance_name} ``` 通过 `--image` 指定镜像,同时指定虚拟机名称。 4. 删除虚拟机 ```Shell -omnivirt delete-instance {instance_name} +eulerlauncher delete-instance {instance_name} ``` 根据虚拟机名称删除指定的虚拟机。 [1]: https://developer.apple.com/documentation/vmnet -[2]: https://gitee.com/openeuler/omnivirt/releases \ No newline at end of file +[2]: https://gitee.com/openeuler/eulerlauncher/releases \ No newline at end of file diff --git a/docs/win-user-manual.md b/docs/win-user-manual.md index 6329c7a..55631c1 100644 --- a/docs/win-user-manual.md +++ b/docs/win-user-manual.md @@ -6,40 +6,40 @@ **EulerLauncher**解压后包含以下几个部分: -- omnivirtd.exe:OmniVirt的主进程,是运行在后台的守护进程,负责与各类虚拟化后端交互,管理虚拟机、容器以及镜像的生命周期,omnivirtd.exe是运行在后台的守护进程。 -- onivirt.exe:OmniVirt的CLI客户端,用户通过该客户端与omnivirtd守护进程交互,对虚拟机、镜像等进行相关操作。 -- omnivirt-win.conf:OmniVirt配置文件,需与omnivirtd.exe放置于同一目录下,参考下面配置进行相应配置: +- eulerlauncherd.exe:EulerLauncher的主进程,是运行在后台的守护进程,负责与各类虚拟化后端交互,管理虚拟机、容器以及镜像的生命周期,eulerlauncherd.exe是运行在后台的守护进程。 +- eulerlauncher.exe:EulerLauncher的CLI客户端,用户通过该客户端与eulerlauncherd守护进程交互,对虚拟机、镜像等进行相关操作。 +- eulerlauncher-win.conf:EulerLauncher配置文件,需与eulerlauncherd.exe放置于同一目录下,参考下面配置进行相应配置: ```Conf [default] # 配置日志文件的存储目录 -log_dir = D:\omnivirt-workdir\logs +log_dir = D:\eulerlauncher-workdir\logs # 配置日志等级是否开启Debug debug = True -# 配置OmniVirt的工作目录 -work_dir = D:\omnivirt-workdir -# 配置OmniVirt的镜像目录,镜像目录为对工作目录的相对目录 +# 配置EulerLauncher的工作目录 +work_dir = D:\eulerlauncher-workdir +# 配置EulerLauncher的镜像目录,镜像目录为对工作目录的相对目录 image_dir = images -# 配置OmniVirt的虚拟机文件目录,虚拟机文件目录为对工作目录的相对目录 +# 配置EulerLauncher的虚拟机文件目录,虚拟机文件目录为对工作目录的相对目录 instance_dir = instances ``` -配置完成后请右键点击omnivirtd.exe,选择以管理员身份运行,点击后omnivird.exe将以守护进程的形式在后台运行。 +配置完成后请右键点击eulerlauncherd.exe,选择以管理员身份运行,点击后eulerlauncherd.exe将以守护进程的形式在后台运行。 打开 `PowerShell` 或 `Terminal` ,准备进行对应的操作。 ### Windows下退出EulerLauncherd后台进程 -当omnivirtd.exe运行后,会在操作系统右下角托盘区域生成omnivirtd托盘图标: +当eulerlauncherd.exe运行后,会在操作系统右下角托盘区域生成eulerlauncherd托盘图标: -鼠标右键点击托盘图标,并选择 `Exit OmniVirt` 即可退出omnivirtd后台进程。 +鼠标右键点击托盘图标,并选择 `Exit EulerLauncher` 即可退出eulerlauncherd后台进程。 ### 镜像操作 1. 获取可用镜像列表: ```PowerShell -omnivirt.exe images +eulerlauncher.exe images +-----------+----------+--------------+ | Images | Location | Status | @@ -50,12 +50,12 @@ omnivirt.exe images +-----------+----------+--------------+ ``` -**EulerLauncher**镜像有两种位置属性:1)远端镜像 2)本地镜像,只有处于本地且状态为 `Ready` 的镜像可以直接用来创建虚拟机,位于远端的镜像需要下载后才能够使用;你也可以加载已经预先下载好的本地镜像到**OmniVirt**中,具体操作方法可以参考接下来的操作指导。 +**EulerLauncher**镜像有两种位置属性:1)远端镜像 2)本地镜像,只有处于本地且状态为 `Ready` 的镜像可以直接用来创建虚拟机,位于远端的镜像需要下载后才能够使用;你也可以加载已经预先下载好的本地镜像到**EulerLauncher**中,具体操作方法可以参考接下来的操作指导。 2. 下载远端镜像 ```PowerShell -omnivirt.exe download-image 22.03-LTS +eulerlauncher.exe download-image 22.03-LTS Downloading: 22.03-LTS, this might take a while, please check image status with "images" command. ``` @@ -63,7 +63,7 @@ Downloading: 22.03-LTS, this might take a while, please check image status with 镜像下载请求是一个异步请求,具体的下载动作将在后台完成,具体耗时与你的网络情况相关,整体的镜像下载流程包括下载、解压缩、格式转换等相关子流程,在下载过程中可以通过 `image` 命令随时查看下载进展与镜像状态: ```PowerShell -omnivirt.exe images +eulerlauncher.exe images +-----------+----------+--------------+ | Images | Location | Status | @@ -78,7 +78,7 @@ omnivirt.exe images 当镜像状态转变为 `Ready` 时,表示镜像下载完成,处于 `Ready` 状态的镜像可被用来创建虚拟机: ```PowerShell -omnivirt.exe images +eulerlauncher.exe images +-----------+----------+--------------+ | Images | Location | Status | @@ -91,10 +91,10 @@ omnivirt.exe images 3. 加载本地镜像 -用户也可以加载自定义镜像或预先下载到本地的镜像到OmniVirt中用于创建自定义虚拟机: +用户也可以加载自定义镜像或预先下载到本地的镜像到EulerLauncher中用于创建自定义虚拟机: ```PowerShell -omnivirt.exe load-image --path {image_file_path} IMAGE_NAME +eulerlauncher.exe load-image --path {image_file_path} IMAGE_NAME ``` 当前支持加载的镜像格式有 `xxx.qcow2.xz`,`xxx.qcow2` @@ -102,15 +102,15 @@ omnivirt.exe load-image --path {image_file_path} IMAGE_NAME 例如: ```PowerShell -omnivirt.exe load-image --path D:\openEuler-22.03-LTS-x86_64.qcow2.xz 2203-load +eulerlauncher.exe load-image --path D:\openEuler-22.03-LTS-x86_64.qcow2.xz 2203-load Loading: 2203-load, this might take a while, please check image status with "images" command. ``` -将位于 `D:\` 目录下的 `openEuler-22.03-LTS-x86_64.qcow2.xz` 加载到OmniVirt系统中,并命名为 `2203-load`,与下载命令一样,加载命令也是一个异步命令,用户需要用镜像列表命令查询镜像状态直到显示为 `Ready`, 但相对于直接下载镜像,加载镜像的速度会快很多: +将位于 `D:\` 目录下的 `openEuler-22.03-LTS-x86_64.qcow2.xz` 加载到EulerLauncher系统中,并命名为 `2203-load`,与下载命令一样,加载命令也是一个异步命令,用户需要用镜像列表命令查询镜像状态直到显示为 `Ready`, 但相对于直接下载镜像,加载镜像的速度会快很多: ```PowerShell -omnivirt.exe images +eulerlauncher.exe images +-----------+----------+--------------+ | Images | Location | Status | @@ -120,7 +120,7 @@ omnivirt.exe images | 2203-load | Local | Loading | +-----------+----------+--------------+ -omnivirt images +eulerlauncher images +-----------+----------+--------------+ | Images | Location | Status | @@ -136,7 +136,7 @@ omnivirt images 通过下面的命令将镜像从OmniVirt系统中删除: ```PowerShell -omnivirt.exe delete-image 2203-load +eulerlauncher.exe delete-image 2203-load Image: 2203-load has been successfully deleted. ``` @@ -146,7 +146,7 @@ Image: 2203-load has been successfully deleted. 1. 获取虚拟机列表: ```Powershell -omnivirt.exe list +eulerlauncher.exe list +----------+-----------+---------+---------------+ | Name | Image | State | IP | @@ -171,16 +171,16 @@ ssh root@{instance_ip} 3. 创建虚拟机 ```PowerShell -omnivirt.exe launch --image {image_name} {instance_name} +eulerlauncher.exe launch --image {image_name} {instance_name} ``` 通过 `--image` 指定镜像,同时指定虚拟机名称。 4. 删除虚拟机 ```PowerShell -omnivirt.exe delete-instance {instance_name} +eulerlauncher.exe delete-instance {instance_name} ``` 根据虚拟机名称删除指定的虚拟机。 -[1]: https://gitee.com/openeuler/omnivirt/releases +[1]: https://gitee.com/openeuler/eulerlauncher/releases [2]: https://learn.microsoft.com/zh-cn/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v \ No newline at end of file diff --git a/etc/omnivirt-win.conf b/etc/eulerlauncher-win.conf similarity index 95% rename from etc/omnivirt-win.conf rename to etc/eulerlauncher-win.conf index 423681a..817262a 100644 --- a/etc/omnivirt-win.conf +++ b/etc/eulerlauncher-win.conf @@ -1,6 +1,6 @@ -[default] -log_dir = D:\workdir\logs -debug = True -work_dir = D:\workdir -image_dir = images +[default] +log_dir = D:\workdir\logs +debug = True +work_dir = D:\workdir +image_dir = images instance_dir = instances \ No newline at end of file diff --git a/etc/omnivirt.conf b/etc/eulerlauncher.conf similarity index 91% rename from etc/omnivirt.conf rename to etc/eulerlauncher.conf index a101a51..07ac1c6 100644 --- a/etc/omnivirt.conf +++ b/etc/eulerlauncher.conf @@ -1,10 +1,10 @@ -[default] -log_dir = -work_dir = -wget_dir = -qemu_dir = -debug = True - -[vm] -cpu_num = 1 +[default] +log_dir = +work_dir = +wget_dir = +qemu_dir = +debug = True + +[vm] +cpu_num = 1 memory = 1024 \ No newline at end of file diff --git a/omnivirt/__init__.py b/eulerlauncher/__init__.py similarity index 100% rename from omnivirt/__init__.py rename to eulerlauncher/__init__.py diff --git a/omnivirt/__pycache__/__init__.cpython-310.pyc b/eulerlauncher/__pycache__/__init__.cpython-310.pyc similarity index 100% rename from omnivirt/__pycache__/__init__.cpython-310.pyc rename to eulerlauncher/__pycache__/__init__.cpython-310.pyc diff --git a/omnivirt/backends/__init__.py b/eulerlauncher/backends/__init__.py similarity index 100% rename from omnivirt/backends/__init__.py rename to eulerlauncher/backends/__init__.py diff --git a/omnivirt/backends/__pycache__/__init__.cpython-310.pyc b/eulerlauncher/backends/__pycache__/__init__.cpython-310.pyc similarity index 100% rename from omnivirt/backends/__pycache__/__init__.cpython-310.pyc rename to eulerlauncher/backends/__pycache__/__init__.cpython-310.pyc diff --git a/omnivirt/backends/mac/image_handler.py b/eulerlauncher/backends/mac/image_handler.py similarity index 97% rename from omnivirt/backends/mac/image_handler.py rename to eulerlauncher/backends/mac/image_handler.py index c11f03d..9329456 100644 --- a/omnivirt/backends/mac/image_handler.py +++ b/eulerlauncher/backends/mac/image_handler.py @@ -1,118 +1,118 @@ -import copy -import lzma -import wget -import os -import subprocess -import shutil -import ssl - -from omnivirt.utils import constants -from omnivirt.utils import utils as omni_utils -from omnivirt.utils import objs - - -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 - 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 - - - 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) - - download_cmd = [self.wget_bin, images['remote'][img_to_download]['path'], - '-O', os.path.join(self.image_dir, img_name), '--no-check-certificate'] - 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)) - - # 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 ...') - - - def delete_image(self, images, img_to_delete): - if img_to_delete not in images['local'].keys(): - 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) - - return 0 - - def load_and_transform(self, images, img_to_load, path, fmt, update=False): - - if update: - self._delete_image(images, img_to_load) - - 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 ...') +import copy +import lzma +import wget +import os +import subprocess +import shutil +import ssl + +from omnivirt.utils import constants +from omnivirt.utils import utils as omni_utils +from omnivirt.utils import objs + + +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 + 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 + + + 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) + + download_cmd = [self.wget_bin, images['remote'][img_to_download]['path'], + '-O', os.path.join(self.image_dir, img_name), '--no-check-certificate'] + 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)) + + # 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 ...') + + + def delete_image(self, images, img_to_delete): + if img_to_delete not in images['local'].keys(): + 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) + + return 0 + + def load_and_transform(self, images, img_to_load, path, fmt, update=False): + + if update: + self._delete_image(images, img_to_load) + + 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 ...') diff --git a/omnivirt/backends/mac/instance_handler.py b/eulerlauncher/backends/mac/instance_handler.py similarity index 97% rename from omnivirt/backends/mac/instance_handler.py rename to eulerlauncher/backends/mac/instance_handler.py index a7aa10d..208c580 100644 --- a/omnivirt/backends/mac/instance_handler.py +++ b/eulerlauncher/backends/mac/instance_handler.py @@ -1,177 +1,177 @@ -import os -import psutil -import shutil -import signal -import subprocess -import sys -import time - -from oslo_utils import uuidutils - -from omnivirt.utils import constants -from omnivirt.utils import utils as omni_utils -from omnivirt.utils import objs -from omnivirt.backends.mac import qemu - - -class MacInstanceHandler(object): - - def __init__(self, conf, work_dir, instance_dir, image_dir, - image_record_file, logger, base_dir) -> 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 - - 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] - 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_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) - - 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 = { - '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'] - } - - all_instances['instances'][name] = instance_record_dict - omni_utils.save_json_data(instance_record, all_instances) - - return { - 'name': name, - 'vm_state': self._check_vm_state(vm_dict), - 'image': image_id, - 'ip_address': vm_dict['ip_address'] - } - - 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 ...') - else: - self.LOG.debug(f'Instance: {name} unable to handled, 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) - - return 0 - +import os +import psutil +import shutil +import signal +import subprocess +import sys +import time + +from oslo_utils import uuidutils + +from omnivirt.utils import constants +from omnivirt.utils import utils as omni_utils +from omnivirt.utils import objs +from omnivirt.backends.mac import qemu + + +class MacInstanceHandler(object): + + def __init__(self, conf, work_dir, instance_dir, image_dir, + image_record_file, logger, base_dir) -> 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 + + 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] + 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_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) + + 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 = { + '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'] + } + + all_instances['instances'][name] = instance_record_dict + omni_utils.save_json_data(instance_record, all_instances) + + return { + 'name': name, + 'vm_state': self._check_vm_state(vm_dict), + 'image': image_id, + 'ip_address': vm_dict['ip_address'] + } + + 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 ...') + else: + self.LOG.debug(f'Instance: {name} unable to handled, 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) + + return 0 + diff --git a/omnivirt/backends/mac/qemu.py b/eulerlauncher/backends/mac/qemu.py similarity index 97% rename from omnivirt/backends/mac/qemu.py rename to eulerlauncher/backends/mac/qemu.py index 732bd3f..2f3ce0a 100644 --- a/omnivirt/backends/mac/qemu.py +++ b/eulerlauncher/backends/mac/qemu.py @@ -1,30 +1,30 @@ -import platform -import subprocess -import os - -from omnivirt.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.omnivirt/','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 +import platform +import subprocess +import os + +from omnivirt.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.omnivirt/','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/omnivirt/backends/win/__init__.py b/eulerlauncher/backends/win/__init__.py similarity index 100% rename from omnivirt/backends/win/__init__.py rename to eulerlauncher/backends/win/__init__.py diff --git a/omnivirt/backends/win/__pycache__/__init__.cpython-310.pyc b/eulerlauncher/backends/win/__pycache__/__init__.cpython-310.pyc similarity index 100% rename from omnivirt/backends/win/__pycache__/__init__.cpython-310.pyc rename to eulerlauncher/backends/win/__pycache__/__init__.cpython-310.pyc diff --git a/omnivirt/backends/win/image_handler.py b/eulerlauncher/backends/win/image_handler.py similarity index 97% rename from omnivirt/backends/win/image_handler.py rename to eulerlauncher/backends/win/image_handler.py index be040fe..b04ecdd 100644 --- a/omnivirt/backends/win/image_handler.py +++ b/eulerlauncher/backends/win/image_handler.py @@ -1,123 +1,123 @@ -import copy -import lzma -import wget -import os -import shutil -import ssl - -from omnivirt.backends.win import powershell -from omnivirt.utils import constants -from omnivirt.utils import utils as omni_utils -from omnivirt.utils import objs - - -ssl._create_default_https_context = ssl._create_unverified_context - - -class WinImageHandler(object): - - def __init__(self, conf, work_dir, image_dir, image_record_file, logger) -> None: - self.conf = conf - self.work_dir = work_dir - self.image_dir = image_dir - self.image_record_file = image_record_file - self.LOG = logger - - 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) - 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) - - # Convert the qcow2 img to vhdx - vhdx_name = img_to_download + '.vhdx' - self.LOG.debug(f'Converting image file: {img_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))) - - self.LOG.debug(f'Cleanup temp files ...') - os.remove(os.path.join(self.image_dir, qcow2_name)) - - # 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 ...') - - def delete_image(self, images, img_to_delete): - if img_to_delete not in images['local'].keys(): - 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'] - 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) - - return 0 - - def load_and_transform(self, images, img_to_load, path, fmt, update=False): - - if update: - self._delete_image(images, img_to_load) - - 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) - - # Convert the qcow2 img to vhdx - vhdx_name = img_to_load + '.vhdx' - self.LOG.debug(f'Converting image file: {qcow2_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))) - self.LOG.debug(f'Cleanup temp files ...') - os.remove(os.path.join(self.image_dir, qcow2_name)) - - # Record local image - 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) - self.LOG.debug(f'Image: {vhdx_name} is ready ...') +import copy +import lzma +import wget +import os +import shutil +import ssl + +from omnivirt.backends.win import powershell +from omnivirt.utils import constants +from omnivirt.utils import utils as omni_utils +from omnivirt.utils import objs + + +ssl._create_default_https_context = ssl._create_unverified_context + + +class WinImageHandler(object): + + def __init__(self, conf, work_dir, image_dir, image_record_file, logger) -> None: + self.conf = conf + self.work_dir = work_dir + self.image_dir = image_dir + self.image_record_file = image_record_file + self.LOG = logger + + 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) + 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) + + # Convert the qcow2 img to vhdx + vhdx_name = img_to_download + '.vhdx' + self.LOG.debug(f'Converting image file: {img_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))) + + self.LOG.debug(f'Cleanup temp files ...') + os.remove(os.path.join(self.image_dir, qcow2_name)) + + # 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 ...') + + def delete_image(self, images, img_to_delete): + if img_to_delete not in images['local'].keys(): + 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'] + 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) + + return 0 + + def load_and_transform(self, images, img_to_load, path, fmt, update=False): + + if update: + self._delete_image(images, img_to_load) + + 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) + + # Convert the qcow2 img to vhdx + vhdx_name = img_to_load + '.vhdx' + self.LOG.debug(f'Converting image file: {qcow2_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))) + self.LOG.debug(f'Cleanup temp files ...') + os.remove(os.path.join(self.image_dir, qcow2_name)) + + # Record local image + 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) + self.LOG.debug(f'Image: {vhdx_name} is ready ...') diff --git a/omnivirt/backends/win/instance_handler.py b/eulerlauncher/backends/win/instance_handler.py similarity index 97% rename from omnivirt/backends/win/instance_handler.py rename to eulerlauncher/backends/win/instance_handler.py index bbcc0e3..dcdd739 100644 --- a/omnivirt/backends/win/instance_handler.py +++ b/eulerlauncher/backends/win/instance_handler.py @@ -1,200 +1,200 @@ -import os -import shutil -import time - -from oslo_utils import uuidutils -from os_win import constants as os_win_const -from os_win import exceptions as os_win_exc - -from omnivirt.backends.win import powershell -from omnivirt.backends.win import vmops -from omnivirt.utils import constants -from omnivirt.utils import utils as omni_utils -from omnivirt.utils import objs - - -_vmops = vmops.VMOps() - -class WinInstanceHandler(object): - - def __init__(self, conf, work_dir, instance_dir, image_dir, image_record_file, logger) -> 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.LOG = logger - - def list_instances(self): - vms = _vmops.list_instances() - return vms - - def check_names(self, name, all_instances): - ret = _vmops.check_all_instance_names(name) - return ret - - def create_instance(self, name, image_id, instance_record, all_instances, all_images): - # Create dir for the instance - vm_dict = { - 'name': name, - 'uuid': uuidutils.generate_uuid(), - 'image': image_id, - 'vm_state': constants.VM_STATE_MAP[99], - 'ip_address': 'N/A', - 'mac_address': 'N/A', - 'identification': { - 'type': 'name', - 'id': name - } - } - - 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 + '.vhdx')) - _vmops.build_and_run_vm(name, vm_dict['uuid'], image_id, False, 2, instance_path, root_disk_path) - - info = _vmops.get_info(name) - vm_dict['vm_state'] = constants.VM_STATE_MAP[info['EnabledState']] - ip = _vmops.get_instance_ip_addr(name) - if ip: - vm_dict['ip_address'] = ip - - instance_record_dict = { - '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'] - } - - all_instances['instances'][name] = instance_record_dict - omni_utils.save_json_data(instance_record, all_instances) - - return { - 'name': name, - 'vm_state': vm_dict['vm_state'], - 'image': image_id, - 'ip_address': vm_dict['ip_address'] - } - - def delete_instance(self, name, instance_record, all_instances): - # Delete instance - _vmops.delete_instance(name) - - # Cleanup files and records - instance_dir = all_instances['instances'][name]['path'] - shutil.rmtree(instance_dir) - del all_instances['instances'][name] - - omni_utils.save_json_data(instance_record, all_instances) - - return 0 - - def reboot(self, instance, reboot_type='soft'): - """Reboot the specified instance.""" - self.LOG.debug("Rebooting instance", instance=instance) - - if reboot_type == 'soft': - if self._soft_shutdown(instance): - self.power_on(instance) - return - - self._set_vm_state(instance, - os_win_const.HYPERV_VM_STATE_REBOOT) - - def _soft_shutdown(self, instance, - timeout=5, - retry_interval=1): - """Perform a soft shutdown on the VM. - - :return: True if the instance was shutdown within time limit, - False otherwise. - """ - self.LOG.debug("Performing Soft shutdown on instance", instance=instance) - - while timeout > 0: - # Perform a soft shutdown on the instance. - # Wait maximum timeout for the instance to be shutdown. - # If it was not shutdown, retry until it succeeds or a maximum of - # time waited is equal to timeout. - wait_time = min(retry_interval, timeout) - try: - self.LOG.debug("Soft shutdown instance, timeout remaining: %d", - timeout, instance=instance) - self._vmutils.soft_shutdown_vm(instance.name) - if self._wait_for_power_off(instance.name, wait_time): - self.LOG.info("Soft shutdown succeeded.", - instance=instance) - return True - except os_win_exc.HyperVException as e: - # Exception is raised when trying to shutdown the instance - # while it is still booting. - self.LOG.debug("Soft shutdown failed: %s", e, instance=instance) - time.sleep(wait_time) - - timeout -= retry_interval - - self.LOG.warning("Timed out while waiting for soft shutdown.", - instance=instance) - return False - - def pause(self, instance): - """Pause VM instance.""" - self.LOG.debug("Pause instance", instance=instance) - self._set_vm_state(instance, - os_win_const.HYPERV_VM_STATE_PAUSED) - - def unpause(self, instance): - """Unpause paused VM instance.""" - self.LOG.debug("Unpause instance", instance=instance) - self._set_vm_state(instance, - os_win_const.HYPERV_VM_STATE_ENABLED) - - def suspend(self, instance): - """Suspend the specified instance.""" - self.LOG.debug("Suspend instance", instance=instance) - self._set_vm_state(instance, - os_win_const.HYPERV_VM_STATE_SUSPENDED) - - def resume(self, instance): - """Resume the suspended VM instance.""" - self.LOG.debug("Resume instance", instance=instance) - self._set_vm_state(instance, - os_win_const.HYPERV_VM_STATE_ENABLED) - - def power_off(self, instance, timeout=0, retry_interval=0): - """Power off the specified instance.""" - self.LOG.debug("Power off instance", instance=instance) - - # We must make sure that the console log workers are stopped, - # otherwise we won't be able to delete or move the VM log files. - self._serial_console_ops.stop_console_handler(instance.name) - - if retry_interval <= 0: - retry_interval = SHUTDOWN_TIME_INCREMENT - - try: - if timeout and self._soft_shutdown(instance, - timeout, - retry_interval): - return - - self._set_vm_state(instance, - os_win_const.HYPERV_VM_STATE_DISABLED) - except os_win_exc.HyperVVMNotFoundException: - # The manager can call the stop API after receiving instance - # power off events. If this is triggered when the instance - # is being deleted, it might attempt to power off an unexisting - # instance. We'll just pass in this case. - self.LOG.debug("Instance not found. Skipping power off", - instance=instance) - - def power_on(self, instance): - """Power on the specified instance.""" - self.LOG.debug("Power on instance", instance=instance) - self._set_vm_state(instance, os_win_const.HYPERV_VM_STATE_ENABLED) +import os +import shutil +import time + +from oslo_utils import uuidutils +from os_win import constants as os_win_const +from os_win import exceptions as os_win_exc + +from omnivirt.backends.win import powershell +from omnivirt.backends.win import vmops +from omnivirt.utils import constants +from omnivirt.utils import utils as omni_utils +from omnivirt.utils import objs + + +_vmops = vmops.VMOps() + +class WinInstanceHandler(object): + + def __init__(self, conf, work_dir, instance_dir, image_dir, image_record_file, logger) -> 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.LOG = logger + + def list_instances(self): + vms = _vmops.list_instances() + return vms + + def check_names(self, name, all_instances): + ret = _vmops.check_all_instance_names(name) + return ret + + def create_instance(self, name, image_id, instance_record, all_instances, all_images): + # Create dir for the instance + vm_dict = { + 'name': name, + 'uuid': uuidutils.generate_uuid(), + 'image': image_id, + 'vm_state': constants.VM_STATE_MAP[99], + 'ip_address': 'N/A', + 'mac_address': 'N/A', + 'identification': { + 'type': 'name', + 'id': name + } + } + + 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 + '.vhdx')) + _vmops.build_and_run_vm(name, vm_dict['uuid'], image_id, False, 2, instance_path, root_disk_path) + + info = _vmops.get_info(name) + vm_dict['vm_state'] = constants.VM_STATE_MAP[info['EnabledState']] + ip = _vmops.get_instance_ip_addr(name) + if ip: + vm_dict['ip_address'] = ip + + instance_record_dict = { + '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'] + } + + all_instances['instances'][name] = instance_record_dict + omni_utils.save_json_data(instance_record, all_instances) + + return { + 'name': name, + 'vm_state': vm_dict['vm_state'], + 'image': image_id, + 'ip_address': vm_dict['ip_address'] + } + + def delete_instance(self, name, instance_record, all_instances): + # Delete instance + _vmops.delete_instance(name) + + # Cleanup files and records + instance_dir = all_instances['instances'][name]['path'] + shutil.rmtree(instance_dir) + del all_instances['instances'][name] + + omni_utils.save_json_data(instance_record, all_instances) + + return 0 + + def reboot(self, instance, reboot_type='soft'): + """Reboot the specified instance.""" + self.LOG.debug("Rebooting instance", instance=instance) + + if reboot_type == 'soft': + if self._soft_shutdown(instance): + self.power_on(instance) + return + + self._set_vm_state(instance, + os_win_const.HYPERV_VM_STATE_REBOOT) + + def _soft_shutdown(self, instance, + timeout=5, + retry_interval=1): + """Perform a soft shutdown on the VM. + + :return: True if the instance was shutdown within time limit, + False otherwise. + """ + self.LOG.debug("Performing Soft shutdown on instance", instance=instance) + + while timeout > 0: + # Perform a soft shutdown on the instance. + # Wait maximum timeout for the instance to be shutdown. + # If it was not shutdown, retry until it succeeds or a maximum of + # time waited is equal to timeout. + wait_time = min(retry_interval, timeout) + try: + self.LOG.debug("Soft shutdown instance, timeout remaining: %d", + timeout, instance=instance) + self._vmutils.soft_shutdown_vm(instance.name) + if self._wait_for_power_off(instance.name, wait_time): + self.LOG.info("Soft shutdown succeeded.", + instance=instance) + return True + except os_win_exc.HyperVException as e: + # Exception is raised when trying to shutdown the instance + # while it is still booting. + self.LOG.debug("Soft shutdown failed: %s", e, instance=instance) + time.sleep(wait_time) + + timeout -= retry_interval + + self.LOG.warning("Timed out while waiting for soft shutdown.", + instance=instance) + return False + + def pause(self, instance): + """Pause VM instance.""" + self.LOG.debug("Pause instance", instance=instance) + self._set_vm_state(instance, + os_win_const.HYPERV_VM_STATE_PAUSED) + + def unpause(self, instance): + """Unpause paused VM instance.""" + self.LOG.debug("Unpause instance", instance=instance) + self._set_vm_state(instance, + os_win_const.HYPERV_VM_STATE_ENABLED) + + def suspend(self, instance): + """Suspend the specified instance.""" + self.LOG.debug("Suspend instance", instance=instance) + self._set_vm_state(instance, + os_win_const.HYPERV_VM_STATE_SUSPENDED) + + def resume(self, instance): + """Resume the suspended VM instance.""" + self.LOG.debug("Resume instance", instance=instance) + self._set_vm_state(instance, + os_win_const.HYPERV_VM_STATE_ENABLED) + + def power_off(self, instance, timeout=0, retry_interval=0): + """Power off the specified instance.""" + self.LOG.debug("Power off instance", instance=instance) + + # We must make sure that the console log workers are stopped, + # otherwise we won't be able to delete or move the VM log files. + self._serial_console_ops.stop_console_handler(instance.name) + + if retry_interval <= 0: + retry_interval = SHUTDOWN_TIME_INCREMENT + + try: + if timeout and self._soft_shutdown(instance, + timeout, + retry_interval): + return + + self._set_vm_state(instance, + os_win_const.HYPERV_VM_STATE_DISABLED) + except os_win_exc.HyperVVMNotFoundException: + # The manager can call the stop API after receiving instance + # power off events. If this is triggered when the instance + # is being deleted, it might attempt to power off an unexisting + # instance. We'll just pass in this case. + self.LOG.debug("Instance not found. Skipping power off", + instance=instance) + + def power_on(self, instance): + """Power on the specified instance.""" + self.LOG.debug("Power on instance", instance=instance) + self._set_vm_state(instance, os_win_const.HYPERV_VM_STATE_ENABLED) diff --git a/omnivirt/backends/win/powershell.py b/eulerlauncher/backends/win/powershell.py similarity index 97% rename from omnivirt/backends/win/powershell.py rename to eulerlauncher/backends/win/powershell.py index ac6a60d..289572a 100644 --- a/omnivirt/backends/win/powershell.py +++ b/eulerlauncher/backends/win/powershell.py @@ -1,49 +1,49 @@ -import os -from glob import glob -import subprocess as sp - - -class PowerShell: - # from scapy - def __init__(self, coding, ): - cmd = [self._where('PowerShell.exe'), - "-NoLogo", "-NonInteractive", # Do not print headers - "-Command", "-"] # Listen commands from stdin - startupinfo = sp.STARTUPINFO() - startupinfo.dwFlags |= sp.STARTF_USESHOWWINDOW - self.popen = sp.Popen(cmd, stdout=sp.PIPE, stdin=sp.PIPE, stderr=sp.STDOUT, startupinfo=startupinfo) - self.coding = coding - - def __enter__(self): - return self - - def __exit__(self, a, b, c): - self.popen.kill() - - def run(self, cmd, timeout=15): - b_cmd = cmd.encode(encoding=self.coding) - try: - b_outs, errs = self.popen.communicate(b_cmd, timeout=timeout) - except sp.TimeoutExpired: - self.popen.kill() - b_outs, errs = self.popen.communicate() - outs = b_outs.decode(encoding=self.coding) - return outs, errs - - @staticmethod - def _where(filename, dirs=None, env="PATH"): - """Find file in current dir, in deep_lookup cache or in system path""" - if dirs is None: - dirs = [] - if not isinstance(dirs, list): - dirs = [dirs] - if glob(filename): - return filename - paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs - try: - return next(os.path.normpath(match) - for path in paths - for match in glob(os.path.join(path, filename)) - if match) - except (StopIteration, RuntimeError): - raise IOError("File not found: %s" % filename) +import os +from glob import glob +import subprocess as sp + + +class PowerShell: + # from scapy + def __init__(self, coding, ): + cmd = [self._where('PowerShell.exe'), + "-NoLogo", "-NonInteractive", # Do not print headers + "-Command", "-"] # Listen commands from stdin + startupinfo = sp.STARTUPINFO() + startupinfo.dwFlags |= sp.STARTF_USESHOWWINDOW + self.popen = sp.Popen(cmd, stdout=sp.PIPE, stdin=sp.PIPE, stderr=sp.STDOUT, startupinfo=startupinfo) + self.coding = coding + + def __enter__(self): + return self + + def __exit__(self, a, b, c): + self.popen.kill() + + def run(self, cmd, timeout=15): + b_cmd = cmd.encode(encoding=self.coding) + try: + b_outs, errs = self.popen.communicate(b_cmd, timeout=timeout) + except sp.TimeoutExpired: + self.popen.kill() + b_outs, errs = self.popen.communicate() + outs = b_outs.decode(encoding=self.coding) + return outs, errs + + @staticmethod + def _where(filename, dirs=None, env="PATH"): + """Find file in current dir, in deep_lookup cache or in system path""" + if dirs is None: + dirs = [] + if not isinstance(dirs, list): + dirs = [dirs] + if glob(filename): + return filename + paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs + try: + return next(os.path.normpath(match) + for path in paths + for match in glob(os.path.join(path, filename)) + if match) + except (StopIteration, RuntimeError): + raise IOError("File not found: %s" % filename) diff --git a/omnivirt/backends/win/vmops.py b/eulerlauncher/backends/win/vmops.py similarity index 97% rename from omnivirt/backends/win/vmops.py rename to eulerlauncher/backends/win/vmops.py index 6882803..f376943 100644 --- a/omnivirt/backends/win/vmops.py +++ b/eulerlauncher/backends/win/vmops.py @@ -1,181 +1,181 @@ -import json - -from os_win import constants as os_win_const -from os_win import exceptions as os_win_exc -from os_win.utils.compute import vmutils10 -from os_win import utilsfactory -from oslo_utils import uuidutils - -from omnivirt.utils import objs -from omnivirt.utils import constants -from omnivirt.utils import utils as omni_utils -from omnivirt.backends.win import powershell - -SWITCH_NAME = 'Default Switch' - - -class VMUtils_omni(vmutils10.VMUtils10): - - def __init__(self) -> None: - super().__init__() - - def get_instance_notes(self, instance_name): - instance_notes = self._get_instance_notes(instance_name) - return instance_notes - - def get_vm_nic_uids(self, vm_name): - nics = self._get_vm_nics(vm_name) - return nics - -class VMOps(object): - _ROOT_DISK_CTRL_ADDR = 0 - - def __init__(self, virtapi=None): - self._virtapi = virtapi - self._vmutils = VMUtils_omni() - self._netutils = utilsfactory.get_networkutils() - self._hostutils = utilsfactory.get_hostutils() - - def _set_vm_state(self, instance, req_state): - instance_name = instance.name - self._vmutils.set_vm_state(instance_name, req_state) - - def list_instance_uuids(self): - instance_uuids = [] - for (instance_name, notes) in self._vmutils.list_instance_notes(): - if notes and uuidutils.is_uuid_like(notes[0]): - instance_uuids.append(str(notes[0])) - else: - pass - #LOG.debug("Notes not found or not resembling a GUID for " - # "instance: %s", instance_name) - return instance_uuids - - def check_all_instance_names(self, name): - instance_names = self._vmutils.list_instances() - if name in instance_names: - return 1 - else: - return 0 - - def list_instances(self): - instance_names = self._vmutils.list_instances() - vm_list = [] - for instance_name in instance_names: - vm = objs.Instance(name=instance_name) - meta = self.get_meta(instance_name) - if not meta or not meta['creator'] == 'omnivirt': - continue - else: - vm.metadata = meta - vm.uuid = meta['uuid'] - info = self.get_info(instance_name) - vm.info = info - vm.vm_state = constants.VM_STATE_MAP[info['EnabledState']] - ip_address = self.get_instance_ip_addr(instance_name) - vm.ip = ip_address - vm.image = meta['image'] - vm_list.append(vm) - - return vm_list - - - 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) - with powershell.PowerShell('GBK') as ps: - outs, errs = ps.run('arp -a | findstr /i {}'.format(mac_address)) - ip_address = outs.strip(' ').split(' ')[0] - - return ip_address - - - def get_info(self, instance): - """Get information about the VM.""" - # LOG.debug("get_info called for instance", instance=instance) - - instance_name = instance - if not self._vmutils.vm_exists(instance_name): - raise # exception.InstanceNotFound(instance_id=instance.uuid) - - info = self._vmutils.get_vm_summary_info(instance_name) - - return info - - def create_vm(self, vm_name, vnuma_enabled, vm_gen, instance_path, - meta_data): - self._vmutils.create_vm(vm_name, - vnuma_enabled, - vm_gen, - instance_path, - [json.dumps(meta_data)]) - - def build_and_run_vm(self, vm_name, uuid, image_name, vnuma_enabled, vm_gen, instance_path, root_disk_path): - meta_data = { - 'uuid': uuid, - 'image': image_name, - 'creator': 'omnivirt' - } - - # Create an instance - self.create_vm(vm_name, vnuma_enabled, vm_gen, instance_path, meta_data) - # Create a scsi controller for this instance - self._vmutils.create_scsi_controller(vm_name) - # Attach the root disk to the driver - self.attach_disk(vm_name, root_disk_path, constants.DISK) - # Start the instance - self.power_up(vm_name) - nic_name = vm_name + '_eth0' - self.add_nic(vm_name, nic_name) - self.connect_vnic_to_switch(SWITCH_NAME, nic_name) - return 0 - - def get_meta(self, instance_name, expect_existing=False): - try: - instance_notes = self._vmutils.get_instance_notes(instance_name) - if instance_notes: - return json.loads(instance_notes[0]) - else: - return instance_notes - except os_win_exc.HyperVVMNotFoundException: - raise - - def delete_instance(self, vm_name): - # Stop the VM first. - self._vmutils.stop_vm_jobs(vm_name) - self._vmutils.set_vm_state(vm_name, os_win_const.HYPERV_VM_STATE_DISABLED) - self._vmutils.destroy_vm(vm_name) - - while(1): - if not self._vmutils.vm_exists(vm_name): - break - return 0 - - def get_vm_disks(self, vm_name): - return self._vmutils.get_vm_disks(vm_name) - - def attach_disk(self, instance_name, path, drive_type): - self._vmutils.attach_scsi_drive(instance_name, path, drive_type) - - def power_up(self, instance_name): - req_state = os_win_const.HYPERV_VM_STATE_ENABLED - self._vmutils.set_vm_state(instance_name, req_state) - - def add_nic(self, instance_name, nic_name): - self._vmutils.create_nic(instance_name, nic_name) - - def get_vm_nics(self, instance_name, nic_name): - return self._vmutils._get_nic_data_by_name(nic_name) - - def list_switch_ports(self, switch_name): - return self._netutils.get_switch_ports(switch_name) - - def connect_vnic_to_switch(self, switch_name, vnic_name): - self._netutils.connect_vnic_to_vswitch(switch_name, vnic_name) - - def get_switch_port(self, switch_name, port_id): - return self._netutils.get_port_by_id(port_id, switch_name) - - def get_host_ips(self): - return self._hostutils.get_local_ips() +import json + +from os_win import constants as os_win_const +from os_win import exceptions as os_win_exc +from os_win.utils.compute import vmutils10 +from os_win import utilsfactory +from oslo_utils import uuidutils + +from omnivirt.utils import objs +from omnivirt.utils import constants +from omnivirt.utils import utils as omni_utils +from omnivirt.backends.win import powershell + +SWITCH_NAME = 'Default Switch' + + +class VMUtils_omni(vmutils10.VMUtils10): + + def __init__(self) -> None: + super().__init__() + + def get_instance_notes(self, instance_name): + instance_notes = self._get_instance_notes(instance_name) + return instance_notes + + def get_vm_nic_uids(self, vm_name): + nics = self._get_vm_nics(vm_name) + return nics + +class VMOps(object): + _ROOT_DISK_CTRL_ADDR = 0 + + def __init__(self, virtapi=None): + self._virtapi = virtapi + self._vmutils = VMUtils_omni() + self._netutils = utilsfactory.get_networkutils() + self._hostutils = utilsfactory.get_hostutils() + + def _set_vm_state(self, instance, req_state): + instance_name = instance.name + self._vmutils.set_vm_state(instance_name, req_state) + + def list_instance_uuids(self): + instance_uuids = [] + for (instance_name, notes) in self._vmutils.list_instance_notes(): + if notes and uuidutils.is_uuid_like(notes[0]): + instance_uuids.append(str(notes[0])) + else: + pass + #LOG.debug("Notes not found or not resembling a GUID for " + # "instance: %s", instance_name) + return instance_uuids + + def check_all_instance_names(self, name): + instance_names = self._vmutils.list_instances() + if name in instance_names: + return 1 + else: + return 0 + + def list_instances(self): + instance_names = self._vmutils.list_instances() + vm_list = [] + for instance_name in instance_names: + vm = objs.Instance(name=instance_name) + meta = self.get_meta(instance_name) + if not meta or not meta['creator'] == 'omnivirt': + continue + else: + vm.metadata = meta + vm.uuid = meta['uuid'] + info = self.get_info(instance_name) + vm.info = info + vm.vm_state = constants.VM_STATE_MAP[info['EnabledState']] + ip_address = self.get_instance_ip_addr(instance_name) + vm.ip = ip_address + vm.image = meta['image'] + vm_list.append(vm) + + return vm_list + + + 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) + with powershell.PowerShell('GBK') as ps: + outs, errs = ps.run('arp -a | findstr /i {}'.format(mac_address)) + ip_address = outs.strip(' ').split(' ')[0] + + return ip_address + + + def get_info(self, instance): + """Get information about the VM.""" + # LOG.debug("get_info called for instance", instance=instance) + + instance_name = instance + if not self._vmutils.vm_exists(instance_name): + raise # exception.InstanceNotFound(instance_id=instance.uuid) + + info = self._vmutils.get_vm_summary_info(instance_name) + + return info + + def create_vm(self, vm_name, vnuma_enabled, vm_gen, instance_path, + meta_data): + self._vmutils.create_vm(vm_name, + vnuma_enabled, + vm_gen, + instance_path, + [json.dumps(meta_data)]) + + def build_and_run_vm(self, vm_name, uuid, image_name, vnuma_enabled, vm_gen, instance_path, root_disk_path): + meta_data = { + 'uuid': uuid, + 'image': image_name, + 'creator': 'omnivirt' + } + + # Create an instance + self.create_vm(vm_name, vnuma_enabled, vm_gen, instance_path, meta_data) + # Create a scsi controller for this instance + self._vmutils.create_scsi_controller(vm_name) + # Attach the root disk to the driver + self.attach_disk(vm_name, root_disk_path, constants.DISK) + # Start the instance + self.power_up(vm_name) + nic_name = vm_name + '_eth0' + self.add_nic(vm_name, nic_name) + self.connect_vnic_to_switch(SWITCH_NAME, nic_name) + return 0 + + def get_meta(self, instance_name, expect_existing=False): + try: + instance_notes = self._vmutils.get_instance_notes(instance_name) + if instance_notes: + return json.loads(instance_notes[0]) + else: + return instance_notes + except os_win_exc.HyperVVMNotFoundException: + raise + + def delete_instance(self, vm_name): + # Stop the VM first. + self._vmutils.stop_vm_jobs(vm_name) + self._vmutils.set_vm_state(vm_name, os_win_const.HYPERV_VM_STATE_DISABLED) + self._vmutils.destroy_vm(vm_name) + + while(1): + if not self._vmutils.vm_exists(vm_name): + break + return 0 + + def get_vm_disks(self, vm_name): + return self._vmutils.get_vm_disks(vm_name) + + def attach_disk(self, instance_name, path, drive_type): + self._vmutils.attach_scsi_drive(instance_name, path, drive_type) + + def power_up(self, instance_name): + req_state = os_win_const.HYPERV_VM_STATE_ENABLED + self._vmutils.set_vm_state(instance_name, req_state) + + def add_nic(self, instance_name, nic_name): + self._vmutils.create_nic(instance_name, nic_name) + + def get_vm_nics(self, instance_name, nic_name): + return self._vmutils._get_nic_data_by_name(nic_name) + + def list_switch_ports(self, switch_name): + return self._netutils.get_switch_ports(switch_name) + + def connect_vnic_to_switch(self, switch_name, vnic_name): + self._netutils.connect_vnic_to_vswitch(switch_name, vnic_name) + + def get_switch_port(self, switch_name, port_id): + return self._netutils.get_port_by_id(port_id, switch_name) + + def get_host_ips(self): + return self._hostutils.get_local_ips() diff --git a/omnivirt/cli.py b/eulerlauncher/cli.py similarity index 64% rename from omnivirt/cli.py rename to eulerlauncher/cli.py index 5d6217b..11ceae0 100644 --- a/omnivirt/cli.py +++ b/eulerlauncher/cli.py @@ -1,144 +1,144 @@ -import click -import prettytable as pt - -from omnivirt.grpcs import client -from omnivirt.utils import utils as omni_utils -from omnivirt.utils import exceptions - - -omnivirt_client = client.Client() - -# List all instances on the host -@click.command() -def list(): - - try: - ret = omnivirt_client.list_instances() - except Exception: - print('Calling to OmniVirtd daemon failed, please check OmniVirtd daemon status ...') - else: - tb = pt.PrettyTable() - - tb.field_names = ["Name", "Image", "State", "IP"] - - try: - for instance in ret['instances']: - tb.add_row( - [instance['name'], - instance['image'], - instance['vmState'], - instance['ipAddress']]) - except KeyError: - pass - - print(tb) - - -# List all usable images -@click.command() -def images(): - - try: - ret = omnivirt_client.list_images() - except Exception: - print('Calling to OmniVirtd daemon failed, please check OmniVirtd daemon status ...') - else: - tb = pt.PrettyTable() - - tb.field_names = ["Images", "Location", "Status"] - - for image in ret['images']: - tb.add_row( - [image['name'], image['location'], image['status']]) - - print(tb) - - -@click.command() -@click.argument('name') -def download_image(name): - - try: - ret = omnivirt_client.download_image(name) - except Exception: - print('Calling to OmniVirtd daemon failed, please check OmniVirtd daemon status ...') - else: - print(ret['msg']) - - -@click.command() -@click.argument('name') -@click.option('--path', help='Image file to load') -def load_image(name, path): - - try: - ret = omnivirt_client.load_image(name, path) - except Exception: - print('Calling to OmniVirtd daemon failed, please check OmniVirtd daemon status ...') - else: - print(ret['msg']) - - -@click.command() -@click.argument('name') -def delete_image(name): - - try: - ret = omnivirt_client.delete_image(name) - except Exception: - print('Calling to OmniVirtd daemon failed, please check OmniVirtd daemon status ...') - else: - print(ret['msg']) - - -@click.command() -@click.argument('name') -def delete_instance(name): - - try: - ret = omnivirt_client.delete_instance(name) - except Exception: - print('Calling to OmniVirtd daemon failed, please check OmniVirtd daemon status ...') - else: - print(ret['msg']) - -@click.command() -@click.argument('vm_name') -@click.option('--image', help='Image to build vm') -def launch(vm_name, image): - - try: - ret = omnivirt_client.create_instance(vm_name, image) - except Exception: - print('Calling to OmniVirtd daemon failed, please check OmniVirtd 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']) - - -@click.group() -def cli(): - pass - - -if __name__ == '__main__': - cli.add_command(list) - cli.add_command(images) - cli.add_command(download_image) - cli.add_command(load_image) - cli.add_command(launch) - cli.add_command(delete_image) - cli.add_command(delete_instance) +import click +import prettytable as pt + +from eulerlauncher.grpcs import client +from eulerlauncher.utils import utils as omni_utils +from eulerlauncher.utils import exceptions + + +eulerlauncher_client = client.Client() + +# List all instances on the host +@click.command() +def list(): + + try: + ret = eulerlauncher_client.list_instances() + except Exception: + print('Calling to EulerLauncherd daemon failed, please check EulerLauncherd daemon status ...') + else: + tb = pt.PrettyTable() + + tb.field_names = ["Name", "Image", "State", "IP"] + + try: + for instance in ret['instances']: + tb.add_row( + [instance['name'], + instance['image'], + instance['vmState'], + instance['ipAddress']]) + except KeyError: + pass + + print(tb) + + +# List all usable images +@click.command() +def images(): + + try: + ret = eulerlauncher_client.list_images() + except Exception: + print('Calling to EulerLauncherd daemon failed, please check EulerLauncherd daemon status ...') + else: + tb = pt.PrettyTable() + + tb.field_names = ["Images", "Location", "Status"] + + for image in ret['images']: + tb.add_row( + [image['name'], image['location'], image['status']]) + + print(tb) + + +@click.command() +@click.argument('name') +def download_image(name): + + try: + ret = eulerlauncher_client.download_image(name) + except Exception: + print('Calling to EulerLauncherd daemon failed, please check EulerLauncherd daemon status ...') + else: + print(ret['msg']) + + +@click.command() +@click.argument('name') +@click.option('--path', help='Image file to load') +def load_image(name, path): + + try: + ret = eulerlauncher_client.load_image(name, path) + except Exception: + print('Calling to EulerLauncherd daemon failed, please check EulerLauncherd daemon status ...') + else: + print(ret['msg']) + + +@click.command() +@click.argument('name') +def delete_image(name): + + try: + ret = eulerlauncher_client.delete_image(name) + except Exception: + print('Calling to EulerLauncherd daemon failed, please check EulerLauncherd daemon status ...') + else: + print(ret['msg']) + + +@click.command() +@click.argument('name') +def delete_instance(name): + + try: + ret = eulerlauncher_client.delete_instance(name) + except Exception: + print('Calling to EulerLauncherd daemon failed, please check EulerLauncherd daemon status ...') + else: + print(ret['msg']) + +@click.command() +@click.argument('vm_name') +@click.option('--image', help='Image to build vm') +def launch(vm_name, image): + + try: + ret = eulerlauncher_client.create_instance(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']) + + +@click.group() +def cli(): + pass + + +if __name__ == '__main__': + cli.add_command(list) + cli.add_command(images) + cli.add_command(download_image) + cli.add_command(load_image) + cli.add_command(launch) + cli.add_command(delete_image) + cli.add_command(delete_instance) cli() \ No newline at end of file diff --git a/omnivirt/omnivirtd.py b/eulerlauncher/eulerlauncherd.py similarity index 85% rename from omnivirt/omnivirtd.py rename to eulerlauncher/eulerlauncherd.py index 08b7a60..4744b25 100644 --- a/omnivirt/omnivirtd.py +++ b/eulerlauncher/eulerlauncherd.py @@ -1,148 +1,148 @@ -import argparse -from concurrent import futures -import grpc -import logging -import os -import PIL.Image -import platform -import pystray -import requests -import signal -import subprocess -import sys -import time - -from omnivirt.grpcs.omnivirt_grpc import images_pb2, images_pb2_grpc -from omnivirt.grpcs.omnivirt_grpc import instances_pb2, instances_pb2_grpc -from omnivirt.services import imager_service, instance_service -from omnivirt.utils import constants -from omnivirt.utils import objs -from omnivirt.utils import utils - - -IMG_URL = 'https://gitee.com/openeuler/omnivirt/raw/master/etc/supported_images.json' - -# Avoid create zombie children in MacOS and Linux -signal.signal(signal.SIGCHLD, signal.SIG_IGN) - -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): - log_dir = config.conf.get('default', 'log_dir') - debug = config.conf.get('default', 'debug') - - if not os.path.exists(log_dir): - os.makedirs(log_dir) - - log_file = os.path.join(log_dir, 'omnivirt.log') - - if debug == 'True': - log_level = logging.DEBUG - else: - log_level = logging.INFO - logging.basicConfig( - format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s', - filename=log_file, level=log_level, filemode='a+') - - -def init(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') - - LOG.debug('Initializing OmniVirtd ...') - 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 ...') - if not os.path.exists(instance_dir): - LOG.debug('Create %s as working directory ...' % work_dir) - os.makedirs(instance_dir) - LOG.debug('Checking for instance database ...') - if not os.path.exists(instance_record_file): - instances = { - 'instances': {} - } - utils.save_json_data(instance_record_file, instances) - - LOG.debug('Checking for image directory ...') - 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, - 'local': {} - } - utils.save_json_data(img_record_file, image_body) - -def serve(arch, host_os, CONF, LOG, base_dir): - ''' - Run the Omnivirtd 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') - server.start() - LOG.debug('OmniVirtd Service Started ...') - - def term_handler(signum, frame): - pid = os.getpid() - os.killpg(os.getpgid(pid), signal.SIGKILL) - - # Avoid create orphan children in MacOS and Linux - signal.signal(signal.SIGTERM, term_handler) - - while True: - time.sleep(1) - -def init_omnivirtd(conf, base_dir): - CONF = objs.Conf(conf) - - config_logging(CONF) - LOG = logging.getLogger(__name__) - - host_arch_raw = platform.uname().machine - host_os_raw = platform.uname().system - - 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: - serve(host_arch, host_os, CONF, LOG, base_dir) - - -if __name__ == '__main__': - args = parser.parse_args() - conf_file = args.conf_file - try: - pass - except Exception as e: - print('Error: ' + str(e)) - else: - init_omnivirtd(conf_file, args.base_dir) +import argparse +from concurrent import futures +import grpc +import logging +import os +import PIL.Image +import platform +import pystray +import requests +import signal +import subprocess +import sys +import time + +from eulerlauncher.grpcs.omnivirt_grpc import images_pb2, images_pb2_grpc +from eulerlauncher.grpcs.omnivirt_grpc import instances_pb2, instances_pb2_grpc +from eulerlauncher.services import imager_service, instance_service +from eulerlauncher.utils import constants +from eulerlauncher.utils import objs +from eulerlauncher.utils import utils + + +IMG_URL = 'https://gitee.com/openeuler/omnivirt/raw/master/etc/supported_images.json' + +# Avoid create zombie children in MacOS and Linux +signal.signal(signal.SIGCHLD, signal.SIG_IGN) + +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): + log_dir = config.conf.get('default', 'log_dir') + debug = config.conf.get('default', 'debug') + + if not os.path.exists(log_dir): + os.makedirs(log_dir) + + log_file = os.path.join(log_dir, 'eulerlauncher.log') + + if debug == 'True': + log_level = logging.DEBUG + else: + log_level = logging.INFO + logging.basicConfig( + format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s', + filename=log_file, level=log_level, filemode='a+') + + +def init(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') + + 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 ...') + if not os.path.exists(instance_dir): + LOG.debug('Create %s as working directory ...' % work_dir) + os.makedirs(instance_dir) + LOG.debug('Checking for instance database ...') + if not os.path.exists(instance_record_file): + instances = { + 'instances': {} + } + utils.save_json_data(instance_record_file, instances) + + LOG.debug('Checking for image directory ...') + 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, + 'local': {} + } + utils.save_json_data(img_record_file, image_body) + +def serve(arch, host_os, CONF, LOG, base_dir): + ''' + 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') + server.start() + LOG.debug('EulerLauncherd Service Started ...') + + def term_handler(signum, frame): + pid = os.getpid() + os.killpg(os.getpgid(pid), signal.SIGKILL) + + # Avoid create orphan children in MacOS and Linux + signal.signal(signal.SIGTERM, term_handler) + + while True: + time.sleep(1) + +def init_eulerlauncherd(conf, base_dir): + CONF = objs.Conf(conf) + + config_logging(CONF) + LOG = logging.getLogger(__name__) + + host_arch_raw = platform.uname().machine + host_os_raw = platform.uname().system + + 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: + serve(host_arch, host_os, CONF, LOG, base_dir) + + +if __name__ == '__main__': + args = parser.parse_args() + conf_file = args.conf_file + try: + pass + except Exception as e: + print('Error: ' + str(e)) + else: + init_eulerlauncherd(conf_file, args.base_dir) diff --git a/omnivirt/grpcs/__init__.py b/eulerlauncher/grpcs/__init__.py similarity index 100% rename from omnivirt/grpcs/__init__.py rename to eulerlauncher/grpcs/__init__.py diff --git a/omnivirt/grpcs/__pycache__/__init__.cpython-310.pyc b/eulerlauncher/grpcs/__pycache__/__init__.cpython-310.pyc similarity index 100% rename from omnivirt/grpcs/__pycache__/__init__.cpython-310.pyc rename to eulerlauncher/grpcs/__pycache__/__init__.cpython-310.pyc diff --git a/omnivirt/grpcs/client.py b/eulerlauncher/grpcs/client.py similarity index 96% rename from omnivirt/grpcs/client.py rename to eulerlauncher/grpcs/client.py index fe4c065..f57cc67 100644 --- a/omnivirt/grpcs/client.py +++ b/eulerlauncher/grpcs/client.py @@ -1,95 +1,95 @@ -import grpc -import os - -from omnivirt.grpcs.omnivirt_grpc import images_pb2, images_pb2_grpc -from omnivirt.grpcs.omnivirt_grpc import instances_pb2, instances_pb2_grpc -from omnivirt.grpcs import images, instances -from omnivirt.utils import constants -from omnivirt.utils import utils as omnivirt_utils - - -class Client(object): - def __init__(self, channel_target=None): - if not channel_target: - channel_target = 'localhost:50052' - channel = grpc.insecure_channel(channel_target) - - images_client = images_pb2_grpc.ImageGrpcServiceStub(channel) - instances_client = instances_pb2_grpc.InstanceGrpcServiceStub(channel) - - self._images = images.Image(images_client) - self._instances = instances.Instance(instances_client) - - @omnivirt_utils.response2dict - def list_images(self, filters=None): - """ [IMAGE] List images - - :param filters(list): None - :return: dict -- list of images' info - """ - - return self._images.list() - - @omnivirt_utils.response2dict - def download_image(self, name): - """ Download image - """ - - return self._images.download(name) - - @omnivirt_utils.response2dict - def load_image(self, name, path): - """ Load local image file - """ - - if not os.path.exists(path): - err_msg = { - 'ret': 1, - 'msg': f'No such file or directory: {path}, please check again.' - } - return err_msg - - supported = False - for tp in constants.IMAGE_LOAD_SUPPORTED_TYPES: - if path.endswith(tp): - supported = True - break - - if not supported: - err_msg = { - 'ret': 1, - 'msg': f'Image file format does not supported: {path}, please check again.' - } - return err_msg - - return self._images.load(name, path) - - @omnivirt_utils.response2dict - def delete_image(self, name): - """ Delete the requested image - """ - - return self._images.delete(name) - - @omnivirt_utils.response2dict - def list_instances(self): - """ List instances - :return: dict -- list of instances' info - """ - - return self._instances.list() - - @omnivirt_utils.response2dict - def create_instance(self, name, image): - """ Create instance - :return: dict -- dict of instance's info - """ - - return self._instances.create(name, image) - - @omnivirt_utils.response2dict - def delete_instance(self, name): - """ Delete the requested instance - """ - - return self._instances.delete(name) +import grpc +import os + +from omnivirt.grpcs.omnivirt_grpc import images_pb2, images_pb2_grpc +from omnivirt.grpcs.omnivirt_grpc import instances_pb2, instances_pb2_grpc +from omnivirt.grpcs import images, instances +from omnivirt.utils import constants +from omnivirt.utils import utils as omnivirt_utils + + +class Client(object): + def __init__(self, channel_target=None): + if not channel_target: + channel_target = 'localhost:50052' + channel = grpc.insecure_channel(channel_target) + + images_client = images_pb2_grpc.ImageGrpcServiceStub(channel) + instances_client = instances_pb2_grpc.InstanceGrpcServiceStub(channel) + + self._images = images.Image(images_client) + self._instances = instances.Instance(instances_client) + + @omnivirt_utils.response2dict + def list_images(self, filters=None): + """ [IMAGE] List images + + :param filters(list): None + :return: dict -- list of images' info + """ + + return self._images.list() + + @omnivirt_utils.response2dict + def download_image(self, name): + """ Download image + """ + + return self._images.download(name) + + @omnivirt_utils.response2dict + def load_image(self, name, path): + """ Load local image file + """ + + if not os.path.exists(path): + err_msg = { + 'ret': 1, + 'msg': f'No such file or directory: {path}, please check again.' + } + return err_msg + + supported = False + for tp in constants.IMAGE_LOAD_SUPPORTED_TYPES: + if path.endswith(tp): + supported = True + break + + if not supported: + err_msg = { + 'ret': 1, + 'msg': f'Image file format does not supported: {path}, please check again.' + } + return err_msg + + return self._images.load(name, path) + + @omnivirt_utils.response2dict + def delete_image(self, name): + """ Delete the requested image + """ + + return self._images.delete(name) + + @omnivirt_utils.response2dict + def list_instances(self): + """ List instances + :return: dict -- list of instances' info + """ + + return self._instances.list() + + @omnivirt_utils.response2dict + def create_instance(self, name, image): + """ Create instance + :return: dict -- dict of instance's info + """ + + return self._instances.create(name, image) + + @omnivirt_utils.response2dict + def delete_instance(self, name): + """ Delete the requested instance + """ + + return self._instances.delete(name) diff --git a/omnivirt/grpcs/images.py b/eulerlauncher/grpcs/images.py similarity index 97% rename from omnivirt/grpcs/images.py rename to eulerlauncher/grpcs/images.py index ba452d3..44deff7 100644 --- a/omnivirt/grpcs/images.py +++ b/eulerlauncher/grpcs/images.py @@ -1,30 +1,30 @@ -from omnivirt.grpcs.omnivirt_grpc import images_pb2 - - -class Image(object): - def __init__(self, client): - self.client = client - - def list(self): - """Get list of images""" - request = images_pb2.ListImageRequest() - response = self.client.list_images(request) - return response - - def download(self, name): - """Download the requested image""" - request = images_pb2.DownloadImageRequest(name=name) - response = self.client.download_image(request) - return response - - def load(self, name, path): - """Load local image file""" - request = images_pb2.LoadImageRequest(name=name, path=path) - response = self.client.load_image(request) - return response - - def delete(self, name): - """Delete the requested image""" - request = images_pb2.DeleteImageRequest(name=name) - response = self.client.delete_image(request) +from omnivirt.grpcs.omnivirt_grpc import images_pb2 + + +class Image(object): + def __init__(self, client): + self.client = client + + def list(self): + """Get list of images""" + request = images_pb2.ListImageRequest() + response = self.client.list_images(request) + return response + + def download(self, name): + """Download the requested image""" + request = images_pb2.DownloadImageRequest(name=name) + response = self.client.download_image(request) + return response + + def load(self, name, path): + """Load local image file""" + request = images_pb2.LoadImageRequest(name=name, path=path) + response = self.client.load_image(request) + return response + + def delete(self, name): + """Delete the requested image""" + request = images_pb2.DeleteImageRequest(name=name) + response = self.client.delete_image(request) return response \ No newline at end of file diff --git a/omnivirt/grpcs/instances.py b/eulerlauncher/grpcs/instances.py similarity index 97% rename from omnivirt/grpcs/instances.py rename to eulerlauncher/grpcs/instances.py index b630a7f..0bc2cac 100644 --- a/omnivirt/grpcs/instances.py +++ b/eulerlauncher/grpcs/instances.py @@ -1,23 +1,23 @@ -from omnivirt.grpcs.omnivirt_grpc import instances_pb2 - -class Instance(object): - def __init__(self, client): - self.client = client - - def list(self): - """Get list of instance""" - request = instances_pb2.ListInstancesRequest() - response = self.client.list_instances(request) - return response - - def create(self, name, image): - """Create instance""" - request = instances_pb2.CreateInstanceRequest(name=name, image=image) - response = self.client.create_instance(request) - return response - - def delete(self, name): - """Delete instance""" - request = instances_pb2.DeleteInstanceRequest(name=name) - response = self.client.delete_instance(request) - return response +from omnivirt.grpcs.omnivirt_grpc import instances_pb2 + +class Instance(object): + def __init__(self, client): + self.client = client + + def list(self): + """Get list of instance""" + request = instances_pb2.ListInstancesRequest() + response = self.client.list_instances(request) + return response + + def create(self, name, image): + """Create instance""" + request = instances_pb2.CreateInstanceRequest(name=name, image=image) + response = self.client.create_instance(request) + return response + + def delete(self, name): + """Delete instance""" + request = instances_pb2.DeleteInstanceRequest(name=name) + response = self.client.delete_instance(request) + return response diff --git a/omnivirt/grpcs/omnivirt_grpc/__init__.py b/eulerlauncher/grpcs/omnivirt_grpc/__init__.py similarity index 100% rename from omnivirt/grpcs/omnivirt_grpc/__init__.py rename to eulerlauncher/grpcs/omnivirt_grpc/__init__.py diff --git a/omnivirt/grpcs/omnivirt_grpc/__pycache__/__init__.cpython-310.pyc b/eulerlauncher/grpcs/omnivirt_grpc/__pycache__/__init__.cpython-310.pyc similarity index 100% rename from omnivirt/grpcs/omnivirt_grpc/__pycache__/__init__.cpython-310.pyc rename to eulerlauncher/grpcs/omnivirt_grpc/__pycache__/__init__.cpython-310.pyc diff --git a/omnivirt/grpcs/omnivirt_grpc/images.proto b/eulerlauncher/grpcs/omnivirt_grpc/images.proto similarity index 94% rename from omnivirt/grpcs/omnivirt_grpc/images.proto rename to eulerlauncher/grpcs/omnivirt_grpc/images.proto index 26996d9..b86f978 100644 --- a/omnivirt/grpcs/omnivirt_grpc/images.proto +++ b/eulerlauncher/grpcs/omnivirt_grpc/images.proto @@ -1,46 +1,46 @@ -syntax = "proto3"; -package omnivirt; - -option cc_generic_services = true; - - -service ImageGrpcService { - rpc list_images (ListImageRequest) returns (ListImageResponse) {} - rpc download_image (DownloadImageRequest) returns (GeneralImageResponse) {} - rpc load_image (LoadImageRequest) returns (GeneralImageResponse) {} - rpc delete_image (DeleteImageRequest) returns (GeneralImageResponse) {} -} - - -message Image { - string name = 1; - string location = 2; - string status = 3; -} - - -message ListImageRequest { -} - - -message ListImageResponse { - repeated Image images = 1; -} - -message DownloadImageRequest { - string name = 1; -} - -message LoadImageRequest { - string name = 1; - string path = 2; -} - -message DeleteImageRequest { - string name = 1; -} - -message GeneralImageResponse { - uint32 ret = 1; - string msg = 2; -} +syntax = "proto3"; +package omnivirt; + +option cc_generic_services = true; + + +service ImageGrpcService { + rpc list_images (ListImageRequest) returns (ListImageResponse) {} + rpc download_image (DownloadImageRequest) returns (GeneralImageResponse) {} + rpc load_image (LoadImageRequest) returns (GeneralImageResponse) {} + rpc delete_image (DeleteImageRequest) returns (GeneralImageResponse) {} +} + + +message Image { + string name = 1; + string location = 2; + string status = 3; +} + + +message ListImageRequest { +} + + +message ListImageResponse { + repeated Image images = 1; +} + +message DownloadImageRequest { + string name = 1; +} + +message LoadImageRequest { + string name = 1; + string path = 2; +} + +message DeleteImageRequest { + string name = 1; +} + +message GeneralImageResponse { + uint32 ret = 1; + string msg = 2; +} diff --git a/omnivirt/grpcs/omnivirt_grpc/images_pb2.py b/eulerlauncher/grpcs/omnivirt_grpc/images_pb2.py similarity index 100% rename from omnivirt/grpcs/omnivirt_grpc/images_pb2.py rename to eulerlauncher/grpcs/omnivirt_grpc/images_pb2.py diff --git a/omnivirt/grpcs/omnivirt_grpc/images_pb2_grpc.py b/eulerlauncher/grpcs/omnivirt_grpc/images_pb2_grpc.py similarity index 100% rename from omnivirt/grpcs/omnivirt_grpc/images_pb2_grpc.py rename to eulerlauncher/grpcs/omnivirt_grpc/images_pb2_grpc.py diff --git a/omnivirt/grpcs/omnivirt_grpc/instances.proto b/eulerlauncher/grpcs/omnivirt_grpc/instances.proto similarity index 94% rename from omnivirt/grpcs/omnivirt_grpc/instances.proto rename to eulerlauncher/grpcs/omnivirt_grpc/instances.proto index c2e751c..e2e592c 100644 --- a/omnivirt/grpcs/omnivirt_grpc/instances.proto +++ b/eulerlauncher/grpcs/omnivirt_grpc/instances.proto @@ -1,48 +1,48 @@ -syntax = "proto3"; -package omnivirt; - -option cc_generic_services = true; - - -service InstanceGrpcService { - rpc list_instances (ListInstancesRequest) returns (ListInstancesResponse) {} - rpc create_instance (CreateInstanceRequest) returns (CreateInstanceResponse) {} - rpc delete_instance (DeleteInstanceRequest) returns (DeleteInstanceResponse) {} -} - - -message Instance { - string name = 1; - string image = 2; - string vm_state = 3; - string ip_address = 4; -} - - -message ListInstancesRequest { -} - - -message ListInstancesResponse { - repeated Instance instances = 1; -} - -message CreateInstanceRequest { - string name = 1; - string image = 2; -} - -message CreateInstanceResponse { - uint32 ret = 1; - string msg = 2; - optional Instance instance = 3; -} - -message DeleteInstanceRequest { - string name = 1; -} - -message DeleteInstanceResponse { - uint32 ret = 1; - string msg = 2; -} +syntax = "proto3"; +package omnivirt; + +option cc_generic_services = true; + + +service InstanceGrpcService { + rpc list_instances (ListInstancesRequest) returns (ListInstancesResponse) {} + rpc create_instance (CreateInstanceRequest) returns (CreateInstanceResponse) {} + rpc delete_instance (DeleteInstanceRequest) returns (DeleteInstanceResponse) {} +} + + +message Instance { + string name = 1; + string image = 2; + string vm_state = 3; + string ip_address = 4; +} + + +message ListInstancesRequest { +} + + +message ListInstancesResponse { + repeated Instance instances = 1; +} + +message CreateInstanceRequest { + string name = 1; + string image = 2; +} + +message CreateInstanceResponse { + uint32 ret = 1; + string msg = 2; + optional Instance instance = 3; +} + +message DeleteInstanceRequest { + string name = 1; +} + +message DeleteInstanceResponse { + uint32 ret = 1; + string msg = 2; +} diff --git a/omnivirt/grpcs/omnivirt_grpc/instances_pb2.py b/eulerlauncher/grpcs/omnivirt_grpc/instances_pb2.py similarity index 100% rename from omnivirt/grpcs/omnivirt_grpc/instances_pb2.py rename to eulerlauncher/grpcs/omnivirt_grpc/instances_pb2.py diff --git a/omnivirt/grpcs/omnivirt_grpc/instances_pb2_grpc.py b/eulerlauncher/grpcs/omnivirt_grpc/instances_pb2_grpc.py similarity index 100% rename from omnivirt/grpcs/omnivirt_grpc/instances_pb2_grpc.py rename to eulerlauncher/grpcs/omnivirt_grpc/instances_pb2_grpc.py diff --git a/omnivirt/install.py b/eulerlauncher/install.py similarity index 100% rename from omnivirt/install.py rename to eulerlauncher/install.py diff --git a/omnivirt/macos-gui.py b/eulerlauncher/macos-gui.py similarity index 100% rename from omnivirt/macos-gui.py rename to eulerlauncher/macos-gui.py diff --git a/omnivirt/services/__init__.py b/eulerlauncher/services/__init__.py similarity index 100% rename from omnivirt/services/__init__.py rename to eulerlauncher/services/__init__.py diff --git a/omnivirt/services/__pycache__/__init__.cpython-310.pyc b/eulerlauncher/services/__pycache__/__init__.cpython-310.pyc similarity index 100% rename from omnivirt/services/__pycache__/__init__.cpython-310.pyc rename to eulerlauncher/services/__pycache__/__init__.cpython-310.pyc diff --git a/omnivirt/services/imager_service.py b/eulerlauncher/services/imager_service.py similarity index 97% rename from omnivirt/services/imager_service.py rename to eulerlauncher/services/imager_service.py index c2d6f28..994236f 100644 --- a/omnivirt/services/imager_service.py +++ b/eulerlauncher/services/imager_service.py @@ -1,106 +1,106 @@ -import logging -import os - -from omnivirt.backends.mac import image_handler as mac_image_handler -from omnivirt.backends.win import image_handler as win_image_handler -from omnivirt.grpcs.omnivirt_grpc import images_pb2, images_pb2_grpc -from omnivirt.utils import constants as omni_constants -from omnivirt.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) +import logging +import os + +from omnivirt.backends.mac import image_handler as mac_image_handler +from omnivirt.backends.win import image_handler as win_image_handler +from omnivirt.grpcs.omnivirt_grpc import images_pb2, images_pb2_grpc +from omnivirt.utils import constants as omni_constants +from omnivirt.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/omnivirt/services/instance_service.py b/eulerlauncher/services/instance_service.py similarity index 98% rename from omnivirt/services/instance_service.py rename to eulerlauncher/services/instance_service.py index e4125e9..be912df 100644 --- a/omnivirt/services/instance_service.py +++ b/eulerlauncher/services/instance_service.py @@ -1,81 +1,81 @@ -import logging -import os - -from omnivirt.grpcs.omnivirt_grpc import instances_pb2, instances_pb2_grpc -from omnivirt.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 - 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') - if host_os == 'Win': - from omnivirt.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) - elif host_os == 'MacOS': - from omnivirt.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) - - def list_instances(self, request, context): - LOG.debug(f"Get request to list instances ...") - instances_obj = 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) - - 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(): - 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(): - 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) - - 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(): - 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) +import logging +import os + +from omnivirt.grpcs.omnivirt_grpc import instances_pb2, instances_pb2_grpc +from omnivirt.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 + 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') + if host_os == 'Win': + from omnivirt.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) + elif host_os == 'MacOS': + from omnivirt.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) + + def list_instances(self, request, context): + LOG.debug(f"Get request to list instances ...") + instances_obj = 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) + + 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(): + 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(): + 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) + + 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(): + 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) diff --git a/omnivirt/utils/__init__.py b/eulerlauncher/utils/__init__.py similarity index 100% rename from omnivirt/utils/__init__.py rename to eulerlauncher/utils/__init__.py diff --git a/omnivirt/utils/__pycache__/__init__.cpython-310.pyc b/eulerlauncher/utils/__pycache__/__init__.cpython-310.pyc similarity index 100% rename from omnivirt/utils/__pycache__/__init__.cpython-310.pyc rename to eulerlauncher/utils/__pycache__/__init__.cpython-310.pyc diff --git a/omnivirt/utils/constants.py b/eulerlauncher/utils/constants.py similarity index 95% rename from omnivirt/utils/constants.py rename to eulerlauncher/utils/constants.py index 9383d2d..dc0ed20 100644 --- a/omnivirt/utils/constants.py +++ b/eulerlauncher/utils/constants.py @@ -1,37 +1,37 @@ -STORAGE_PROTOCOL_ISCSI = 'iscsi' -STORAGE_PROTOCOL_FC = 'fibre_channel' -STORAGE_PROTOCOL_SMBFS = 'smbfs' -STORAGE_PROTOCOL_RBD = 'rbd' - -DISK = "VHD" - -IMAGE_LOCATION_REMOTE = 'Remote' -IMAGE_LOCATION_LOCAL = 'Local' - -IMAGE_STATUS_INIT = 'N/A' -IMAGE_STATUS_DOWLOADABLE = 'Downloadable' -IMAGE_STATUS_DOWNLOADING = 'Downloading' -IMAGE_STATUS_LOADING = 'Loading' -IMAGE_STATUS_READY = 'Ready' - -IMAGE_LOAD_SUPPORTED_TYPES = ['qcow2.xz', 'qcow2'] - -ARCH_MAP = { - 'AMD64': 'x86_64', - 'arm64': 'aarch64', - 'x86_64': 'x86_64' -} - -VM_STATE_MAP = { - 2: 'Running', - 3: 'Stopped', - 10: 'Rebooting', - 32768: 'Paused', - 32769: 'Suspended', - 99: 'N/A' - } - -OS_MAP = { - 'Darwin': 'MacOS', - 'Windows': 'Win' +STORAGE_PROTOCOL_ISCSI = 'iscsi' +STORAGE_PROTOCOL_FC = 'fibre_channel' +STORAGE_PROTOCOL_SMBFS = 'smbfs' +STORAGE_PROTOCOL_RBD = 'rbd' + +DISK = "VHD" + +IMAGE_LOCATION_REMOTE = 'Remote' +IMAGE_LOCATION_LOCAL = 'Local' + +IMAGE_STATUS_INIT = 'N/A' +IMAGE_STATUS_DOWLOADABLE = 'Downloadable' +IMAGE_STATUS_DOWNLOADING = 'Downloading' +IMAGE_STATUS_LOADING = 'Loading' +IMAGE_STATUS_READY = 'Ready' + +IMAGE_LOAD_SUPPORTED_TYPES = ['qcow2.xz', 'qcow2'] + +ARCH_MAP = { + 'AMD64': 'x86_64', + 'arm64': 'aarch64', + 'x86_64': 'x86_64' +} + +VM_STATE_MAP = { + 2: 'Running', + 3: 'Stopped', + 10: 'Rebooting', + 32768: 'Paused', + 32769: 'Suspended', + 99: 'N/A' + } + +OS_MAP = { + 'Darwin': 'MacOS', + 'Windows': 'Win' } \ No newline at end of file diff --git a/omnivirt/utils/exceptions.py b/eulerlauncher/utils/exceptions.py similarity index 96% rename from omnivirt/utils/exceptions.py rename to eulerlauncher/utils/exceptions.py index 3f671e1..47a1caa 100644 --- a/omnivirt/utils/exceptions.py +++ b/eulerlauncher/utils/exceptions.py @@ -1,61 +1,61 @@ -"""OmniVirt Base Exceptions. -""" - -import logging - -LOG = logging.getLogger(__name__) - -class OmniVirtException(Exception): - """Base OmniVirt Exception - To correctly use this class, inherit from it and define - a 'msg_fmt' property. That msg_fmt will get printf'd - with the keyword arguments provided to the constructor. - """ - msg_fmt = "An unknown exception occurred." - code = 500 - headers = {} - safe = False - - def __init__(self, message=None, **kwargs): - self.kwargs = kwargs - - if 'code' not in self.kwargs: - try: - self.kwargs['code'] = self.code - except AttributeError: - pass - - try: - if not message: - message = self.msg_fmt % kwargs - else: - message = str(message) - except Exception: - self._log_exception() - message = self.msg_fmt - - self.message = message - super(OmniVirtException, self).__init__(message) - - def _log_exception(self): - LOG.exception('Exception in string format operation') - for name, value in self.kwargs.items(): - LOG.error("%s: %s" % (name, value)) # noqa - - def format_message(self): - return self.args[0] - - def __repr__(self): - dict_repr = self.__dict__ - dict_repr['class'] = self.__class__.__name__ - return str(dict_repr) - - -class NoSuchFile(OmniVirtException): - msg_fmt = "No Such File or Directory: %(file)s" - -class NoConfigFileProvided(OmniVirtException): - msg_fmt = "Config File Should Be Provided" - -class OmniVirtdNotAvailable(OmniVirtException): +"""OmniVirt Base Exceptions. +""" + +import logging + +LOG = logging.getLogger(__name__) + +class OmniVirtException(Exception): + """Base OmniVirt Exception + To correctly use this class, inherit from it and define + a 'msg_fmt' property. That msg_fmt will get printf'd + with the keyword arguments provided to the constructor. + """ + msg_fmt = "An unknown exception occurred." + code = 500 + headers = {} + safe = False + + def __init__(self, message=None, **kwargs): + self.kwargs = kwargs + + if 'code' not in self.kwargs: + try: + self.kwargs['code'] = self.code + except AttributeError: + pass + + try: + if not message: + message = self.msg_fmt % kwargs + else: + message = str(message) + except Exception: + self._log_exception() + message = self.msg_fmt + + self.message = message + super(OmniVirtException, self).__init__(message) + + def _log_exception(self): + LOG.exception('Exception in string format operation') + for name, value in self.kwargs.items(): + LOG.error("%s: %s" % (name, value)) # noqa + + def format_message(self): + return self.args[0] + + def __repr__(self): + dict_repr = self.__dict__ + dict_repr['class'] = self.__class__.__name__ + return str(dict_repr) + + +class NoSuchFile(OmniVirtException): + msg_fmt = "No Such File or Directory: %(file)s" + +class NoConfigFileProvided(OmniVirtException): + msg_fmt = "Config File Should Be Provided" + +class OmniVirtdNotAvailable(OmniVirtException): msg_fmt = "OmniVirtd Daemon is not available" \ No newline at end of file diff --git a/omnivirt/utils/objs.py b/eulerlauncher/utils/objs.py similarity index 96% rename from omnivirt/utils/objs.py rename to eulerlauncher/utils/objs.py index 9cb2004..e26f37e 100644 --- a/omnivirt/utils/objs.py +++ b/eulerlauncher/utils/objs.py @@ -1,54 +1,54 @@ -import os -import configparser - -from omnivirt.utils import exceptions -from omnivirt.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): - - def __init__(self, config_file) -> None: - self.conf = configparser.ConfigParser() - if not os.path.exists(config_file): - raise exceptions.NoSuchFile(file=config_file) - self.conf.read(config_file) +import os +import configparser + +from omnivirt.utils import exceptions +from omnivirt.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): + + def __init__(self, config_file) -> None: + self.conf = configparser.ConfigParser() + if not os.path.exists(config_file): + raise exceptions.NoSuchFile(file=config_file) + self.conf.read(config_file) diff --git a/omnivirt/utils/utils.py b/eulerlauncher/utils/utils.py similarity index 95% rename from omnivirt/utils/utils.py rename to eulerlauncher/utils/utils.py index b6f10c9..9840688 100644 --- a/omnivirt/utils/utils.py +++ b/eulerlauncher/utils/utils.py @@ -1,94 +1,94 @@ -import functools -import json -import os -import random -from threading import Thread -import uuid - - -from google.protobuf.json_format import MessageToDict - -from omnivirt.utils import exceptions -from omnivirt.utils import objs - - -def asyncwrapper(fn): - def wrapper(*args, **kwargs): - thr = Thread(target=fn, args=args, kwargs=kwargs) - thr.start() - - return wrapper - - -def response2dict(fn): - @functools.wraps(fn) - def wrap(*args, **kwargs): - response = fn(*args, **kwargs) - response = MessageToDict(response) - return response - - return wrap - - -def parse_config(args): - if len(args) != 2 or args[0] != '--config-file': - raise exceptions.NoConfigFileProvided - if not os.path.exists(args[1]): - raise exceptions.NoSuchFile(file=args[1]) - - return objs.Conf(args[1]) - - -def format_mac_addr(mac_str): - ret = '' - if len(mac_str) != 12: - return ret - mac_low = mac_str.lower() - for i in range(0, 5): - ret = ret + mac_low[2 * i] + mac_low[2 * i + 1] + '-' - ret = ret + mac_low[-2] + mac_low[-1] - - return ret - -def load_json_data(json_file): - with open(json_file, 'r', encoding='utf-8') as 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) - -def generate_mac(): - local_mac = uuid.uuid1().hex[-12:] - - mac = [random.randint(0x00, 0xff), random.randint(0x00, 0xff)] - s = [local_mac[0:2], local_mac[2:4], local_mac[4:6], local_mac[6:8]] - for item in mac: - s.append(str("%02x" % item)) - - return (':'.join(s)) - -def catch_exception(func): - - def wrap(*args, **kwargs): - try: - return func(*args, **kwargs) - except Exception: - raise exceptions.OmniVirtdNotAvailable - - return wrap - -def check_file_tail(file_name, to_check): - - ret = False - ret_fmt = None - - for fmt in to_check: - if file_name.endswith(fmt): - ret = True - ret_fmt = fmt - break - - return ret, ret_fmt +import functools +import json +import os +import random +from threading import Thread +import uuid + + +from google.protobuf.json_format import MessageToDict + +from omnivirt.utils import exceptions +from omnivirt.utils import objs + + +def asyncwrapper(fn): + def wrapper(*args, **kwargs): + thr = Thread(target=fn, args=args, kwargs=kwargs) + thr.start() + + return wrapper + + +def response2dict(fn): + @functools.wraps(fn) + def wrap(*args, **kwargs): + response = fn(*args, **kwargs) + response = MessageToDict(response) + return response + + return wrap + + +def parse_config(args): + if len(args) != 2 or args[0] != '--config-file': + raise exceptions.NoConfigFileProvided + if not os.path.exists(args[1]): + raise exceptions.NoSuchFile(file=args[1]) + + return objs.Conf(args[1]) + + +def format_mac_addr(mac_str): + ret = '' + if len(mac_str) != 12: + return ret + mac_low = mac_str.lower() + for i in range(0, 5): + ret = ret + mac_low[2 * i] + mac_low[2 * i + 1] + '-' + ret = ret + mac_low[-2] + mac_low[-1] + + return ret + +def load_json_data(json_file): + with open(json_file, 'r', encoding='utf-8') as 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) + +def generate_mac(): + local_mac = uuid.uuid1().hex[-12:] + + mac = [random.randint(0x00, 0xff), random.randint(0x00, 0xff)] + s = [local_mac[0:2], local_mac[2:4], local_mac[4:6], local_mac[6:8]] + for item in mac: + s.append(str("%02x" % item)) + + return (':'.join(s)) + +def catch_exception(func): + + def wrap(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception: + raise exceptions.OmniVirtdNotAvailable + + return wrap + +def check_file_tail(file_name, to_check): + + ret = False + ret_fmt = None + + for fmt in to_check: + if file_name.endswith(fmt): + ret = True + ret_fmt = fmt + break + + return ret, ret_fmt diff --git a/install.spec b/install.spec index cac4aec..8655401 100644 --- a/install.spec +++ b/install.spec @@ -5,10 +5,10 @@ block_cipher = None a = Analysis( - ['omnivirt/install.py'], + ['eulerlauncher/install.py'], pathex=[], - binaries=[('dist/omnivirt', './etc')], - datas=[('etc/omnivirt.conf', './etc'), ('resources/qemu/edk2-aarch64-code.fd', './etc'), ('resources/qemu/edk2-x86_64-code.fd', './etc')], + binaries=[('dist/eulerlauncher', './etc')], + datas=[('etc/eulerlauncher.conf', './etc'), ('resources/qemu/edk2-aarch64-code.fd', './etc'), ('resources/qemu/edk2-x86_64-code.fd', './etc')], hiddenimports=[], hookspath=[], hooksconfig={}, diff --git a/setup.cfg b/setup.cfg index 6a95db9..bd3854e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -name = omnivirt +name = eulerlauncher version = 0.1 summary = A tool to run openEuler instances on all platforms description_file = @@ -10,7 +10,7 @@ python_requires = >=3.8 url = https://github.com/ZhengZhenyu/omnivirt.git classifiers = Development Status :: 3 - Alpha - Environment :: OmniVirt + Environment :: EulerLauncher Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Mulan PSL v2 @@ -25,8 +25,8 @@ classifiers = [files] packages = - omnivirt + eulerlauncher [entry_points] console_scripts = - omnivirt = omnivirt.cli:main \ No newline at end of file + eulerlauncher = eulerlauncher.cli:main \ No newline at end of file -- Gitee