From f222ab58861d9a0d54624fd1e1fca01fe1f6364a Mon Sep 17 00:00:00 2001 From: Ahmed Bilal <49-ahmedbilal@users.noreply.code.ungleich.ch> Date: Mon, 11 Nov 2019 19:49:35 +0100 Subject: [PATCH] Networking options added Perform action by using virtual machine name and images name instead of uuid Specs is passed individually now. For example, --cpu, --ram, --os-ssd, --hdd etc. --- Pipfile | 2 +- Pipfile.lock | 16 ++++---- commands/helper.py | 17 ++++---- commands/host.py | 42 +++++++++++--------- commands/image.py | 14 +++---- commands/network.py | 25 ++++++++++++ commands/user.py | 69 ++++++++++++++++++++++++++++---- commands/vm.py | 95 +++++++++++++++++++++++++++------------------ ucloud.py | 3 +- 9 files changed, 193 insertions(+), 90 deletions(-) create mode 100644 commands/network.py diff --git a/Pipfile b/Pipfile index b8badd7..87b7f4c 100755 --- a/Pipfile +++ b/Pipfile @@ -12,4 +12,4 @@ pyotp = "*" click = "*" [requires] -python_version = "3.7" +python_version = "3.5" diff --git a/Pipfile.lock b/Pipfile.lock index 3190413..8875bff 100755 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,11 +1,11 @@ { "_meta": { "hash": { - "sha256": "1f6e0448a1e04906885393b4ff57423363593680a7b6f73a5ed1f514496bb1f3" + "sha256": "ad97ea0bec676c536266766c718bd590b17e0e28d47728ab68cf21d09fbb07d4" }, "pipfile-spec": 6, "requires": { - "python_version": "3.7" + "python_version": "3.5" }, "sources": [ { @@ -18,10 +18,10 @@ "default": { "certifi": { "hashes": [ - "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", - "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" + "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", + "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" ], - "version": "==2019.6.16" + "version": "==2019.9.11" }, "chardet": { "hashes": [ @@ -47,11 +47,11 @@ }, "pyotp": { "hashes": [ - "sha256:1e3dc3d16919c4efac528d1dbecc17de1a97c4ecfdacb89d7726ed2c6645adff", - "sha256:be0ffeabddaa5ee53e7204e7740da842d070cf69168247a3d0c08541b84de602" + "sha256:c88f37fd47541a580b744b42136f387cdad481b560ef410c0d85c957eb2a2bc0", + "sha256:fc537e8acd985c5cbf51e11b7d53c42276fee017a73aec7c07380695671ca1a1" ], "index": "pypi", - "version": "==2.2.7" + "version": "==2.3.0" }, "python-decouple": { "hashes": [ diff --git a/commands/helper.py b/commands/helper.py index d29df90..ce3da6b 100755 --- a/commands/helper.py +++ b/commands/helper.py @@ -1,21 +1,20 @@ -import click import json -from dataclasses import dataclass from pyotp import TOTP -@dataclass class OTPCredentials: - name: str - realm: str - seed: str + def __init__(self, name, realm, seed): + self.name = name # type: str + self.realm = realm # type: str + self.seed = seed # type: str def get_json(self): - r = {"name": self.name, "realm": self.realm, "token": TOTP(self.seed).now()} - return r + return {"name": self.name, "realm": self.realm, "token": TOTP(self.seed).now()} def load_dump_pretty(content): + if isinstance(content, bytes): + content = content.decode("utf-8") parsed = json.loads(content) - return json.dumps(parsed, indent=4, sort_keys=True) \ No newline at end of file + return json.dumps(parsed, indent=4, sort_keys=True) diff --git a/commands/host.py b/commands/host.py index 24cf3da..decd773 100755 --- a/commands/host.py +++ b/commands/host.py @@ -1,9 +1,10 @@ -import click import json -import requests +from commands.helper import OTPCredentials, load_dump_pretty from decouple import config -from .helper import OTPCredentials, load_dump_pretty + +import click +import requests @click.group() @@ -11,25 +12,30 @@ def host(): pass -@host.command("add") +@host.command("create") @click.option("--name", envvar="OTP_NAME", required=True) @click.option("--realm", envvar="OTP_REALM", required=True) @click.option("--seed", envvar="OTP_SEED", required=True) -@click.option("--specs", required=True) @click.option("--hostname", required=True) -def add_host(name, realm, seed, specs, hostname): - with open(specs, "r") as specs_f: - specs = json.loads(specs_f.read()) - data = { - **OTPCredentials(name, realm, seed).get_json(), - "specs": specs, - "hostname": hostname, - } - r = requests.post(f"{config('UCLOUD_API_SERVER')}/host/create", json=data) - print(load_dump_pretty(r.content)) - +@click.option("--cpu", required=True, type=int) +@click.option("--ram", required=True) +@click.option("--os-ssd", required=True) +@click.option("--hdd", default=list(), multiple=True) +def create(name, realm, seed, hostname, cpu, ram, os_ssd, hdd): + data = { + **OTPCredentials(name, realm, seed).get_json(), + "hostname": hostname, + "specs": { + 'cpu': cpu, + 'ram': ram, + 'os-ssd': os_ssd, + 'hdd': hdd + }, + } + r = requests.post("{}/host/create".format(config('UCLOUD_API_SERVER')), json=data) + print(load_dump_pretty(r.content)) @host.command("list") def list_host(): - r = requests.get(f"{config('UCLOUD_API_SERVER')}/host/list") - print(load_dump_pretty(r.content)) \ No newline at end of file + r = requests.get("{}/host/list".format(config('UCLOUD_API_SERVER'))) + print(load_dump_pretty(r.content)) diff --git a/commands/image.py b/commands/image.py index eb5b429..42be2c4 100755 --- a/commands/image.py +++ b/commands/image.py @@ -1,10 +1,8 @@ -import click -import json -import requests - +from commands.helper import load_dump_pretty from decouple import config -from .helper import OTPCredentials, load_dump_pretty +import click +import requests @click.group() def image(): @@ -16,15 +14,15 @@ def image(): @click.option("--private", is_flag=True) def _list(public, private): if public: - r = requests.get(f"{config('UCLOUD_API_SERVER')}/image/list-public") + r = requests.get("{}/image/list-public".format(config('UCLOUD_API_SERVER'))) print(load_dump_pretty(r.content)) @image.command("create-from-file") @click.option("--name", required=True) @click.option("--uuid", required=True) -@click.option("--image_store_name", required=True) +@click.option("--image-store-name", required=True) def create_from_file(name, uuid, image_store_name): data = {"name": name, "uuid": uuid, "image_store": image_store_name} - r = requests.post(f"{config('UCLOUD_API_SERVER')}/image/create", json=data) + r = requests.post("{}/image/create".format(config('UCLOUD_API_SERVER')), json=data) print(load_dump_pretty(r.content)) diff --git a/commands/network.py b/commands/network.py new file mode 100644 index 0000000..a41e1a8 --- /dev/null +++ b/commands/network.py @@ -0,0 +1,25 @@ +from commands.helper import load_dump_pretty, OTPCredentials +from decouple import config + +import click +import requests + +@click.group() +def network(): + pass + + +@network.command("create") +@click.option("--name", envvar="OTP_NAME", required=True) +@click.option("--realm", envvar="OTP_REALM", required=True) +@click.option("--seed", envvar="OTP_SEED", required=True) +@click.option("--network-name", required=True) +@click.option("--network-type", required=True) +def create(name, realm, seed, network_name, network_type): + data = { + **OTPCredentials(name, realm, seed).get_json(), + "network_name": network_name, + "type": network_type + } + r = requests.post("{}/network/create".format(config('UCLOUD_API_SERVER')), json=data) + print(load_dump_pretty(r.content)) diff --git a/commands/user.py b/commands/user.py index cd23d31..3c1fd88 100755 --- a/commands/user.py +++ b/commands/user.py @@ -1,9 +1,8 @@ -import click -import json -import requests - +from commands.helper import OTPCredentials, load_dump_pretty from decouple import config -from .helper import OTPCredentials, load_dump_pretty + +import click +import requests @click.group() @@ -17,7 +16,7 @@ def user(): @click.option("--seed", envvar="OTP_SEED", required=True) def list_files(name, realm, seed): data = OTPCredentials(name, realm, seed).get_json() - r = requests.get(f"{config('UCLOUD_API_SERVER')}/user/files", json=data) + r = requests.get("{}/user/files".format(config('UCLOUD_API_SERVER')), json=data) print(load_dump_pretty(r.content)) @@ -27,5 +26,61 @@ def list_files(name, realm, seed): @click.option("--seed", envvar="OTP_SEED", required=True) def list_vms(name, realm, seed): data = OTPCredentials(name, realm, seed).get_json() - r = requests.get(f"{config('UCLOUD_API_SERVER')}/user/vms", json=data) + r = requests.get("{}/user/vms".format(config('UCLOUD_API_SERVER')), json=data) print(load_dump_pretty(r.content)) + + +@user.command("network") +@click.option("--name", envvar="OTP_NAME", required=True) +@click.option("--realm", envvar="OTP_REALM", required=True) +@click.option("--seed", envvar="OTP_SEED", required=True) +def list_files(name, realm, seed): + data = OTPCredentials(name, realm, seed).get_json() + r = requests.get("{}/user/networks".format(config('UCLOUD_API_SERVER')), json=data) + print(load_dump_pretty(r.content)) + + +@user.command("add-ssh") +@click.option("--name", envvar="OTP_NAME", required=True) +@click.option("--realm", envvar="OTP_REALM", required=True) +@click.option("--seed", envvar="OTP_SEED", required=True) +@click.option("--key-name", required=True) +@click.option("--key", required=True) +def add_ssh(name, realm, seed, key_name, key): + otp = OTPCredentials(name, realm, seed) + data = { + **otp.get_json(), + "key_name": key_name, + "key": key + } + r = requests.post("{}/user/add-ssh".format(config('UCLOUD_API_SERVER')), json=data) + print(load_dump_pretty(r.content)) + + +@user.command("remove-ssh") +@click.option("--name", envvar="OTP_NAME", required=True) +@click.option("--realm", envvar="OTP_REALM", required=True) +@click.option("--seed", envvar="OTP_SEED", required=True) +@click.option("--key-name", required=True) +def remove_ssh(name, realm, seed, key_name): + otp = OTPCredentials(name, realm, seed) + data = { + **otp.get_json(), + "key_name": key_name, + } + r = requests.get("{}/user/remove-ssh".format(config('UCLOUD_API_SERVER')), json=data) + print(load_dump_pretty(r.content)) + +@user.command("get-ssh") +@click.option("--name", envvar="OTP_NAME", required=True) +@click.option("--realm", envvar="OTP_REALM", required=True) +@click.option("--seed", envvar="OTP_SEED", required=True) +@click.option("--key-name", default="") +def add_ssh(name, realm, seed, key_name): + otp = OTPCredentials(name, realm, seed) + data = { + **otp.get_json(), + "key_name": key_name, + } + r = requests.get("{}/user/get-ssh".format(config('UCLOUD_API_SERVER')), json=data) + print(load_dump_pretty(r.content)) \ No newline at end of file diff --git a/commands/vm.py b/commands/vm.py index 3c2f46b..21813ff 100755 --- a/commands/vm.py +++ b/commands/vm.py @@ -1,14 +1,15 @@ -import click import json + +from commands.helper import OTPCredentials, load_dump_pretty +from decouple import config + +import click import requests -from decouple import config -from .helper import OTPCredentials, load_dump_pretty - -def vm_command(command, otp, uuid): - data = {**otp.get_json(), "uuid": uuid, "action": command} - r = requests.post(f"{config('UCLOUD_API_SERVER')}/vm/action", json=data) +def vm_command(command, otp, vm_name, **kwargs): + data = {**otp.get_json(), "vm_name": vm_name, "action": command, **kwargs} + r = requests.post("{}/vm/action".format(config('UCLOUD_API_SERVER')), json=data) return r @@ -21,27 +22,38 @@ def vm(): @click.option("--name", envvar="OTP_NAME", required=True) @click.option("--realm", envvar="OTP_REALM", required=True) @click.option("--seed", envvar="OTP_SEED", required=True) -@click.option("--specs", required=True) -@click.option("--image_uuid", required=True) -def create(name, realm, seed, specs, image_uuid): - with open(specs, "r") as specs_f: - specs = json.loads(specs_f.read()) - data = { - **OTPCredentials(name, realm, seed).get_json(), - "specs": specs, - "image_uuid": image_uuid, - } - r = requests.post(f"{config('UCLOUD_API_SERVER')}/vm/create", json=data) - print(load_dump_pretty(r.content)) +@click.option("--vm-name", required=True) +@click.option("--cpu", required=True, type=int) +@click.option("--ram", required=True) +@click.option("--os-ssd", required=True) +@click.option("--hdd", default=list(), multiple=True) +@click.option("--image", required=True) +@click.option("--network", default=list(), multiple=True) +def create(name, realm, seed, vm_name, cpu, ram, os_ssd, hdd, image, network): + data = { + **OTPCredentials(name, realm, seed).get_json(), + "vm_name": vm_name, + "specs": { + 'cpu': cpu, + 'ram': ram, + 'os-ssd': os_ssd, + 'hdd': hdd + }, + "network": network, + "image": image, + } + r = requests.post("{}/vm/create".format(config('UCLOUD_API_SERVER')), json=data) + print(load_dump_pretty(r.content)) @vm.command("start") @click.option("--name", envvar="OTP_NAME", required=True) @click.option("--realm", envvar="OTP_REALM", required=True) @click.option("--seed", envvar="OTP_SEED", required=True) -@click.option("--uuid", required=True) -def start(name, realm, seed, uuid): - r = vm_command("start", OTPCredentials(name, realm, seed), uuid) +@click.option("--vm-name", required=True) +@click.option("--in_support_of") +def start(name, realm, seed, vm_name, in_support_of): + r = vm_command("start", OTPCredentials(name, realm, seed), vm_name, in_support_of=in_support_of) print(load_dump_pretty(r.content)) @@ -49,9 +61,10 @@ def start(name, realm, seed, uuid): @click.option("--name", envvar="OTP_NAME", required=True) @click.option("--realm", envvar="OTP_REALM", required=True) @click.option("--seed", envvar="OTP_SEED", required=True) -@click.option("--uuid", required=True) -def stop(name, realm, seed, uuid): - r = vm_command("stop", OTPCredentials(name, realm, seed), uuid) +@click.option("--vm-name", required=True) +@click.option("--in_support_of") +def stop(name, realm, seed, vm_name, in_support_of): + r = vm_command("stop", OTPCredentials(name, realm, seed), vm_name, in_support_of=in_support_of) print(load_dump_pretty(r.content)) @@ -59,9 +72,10 @@ def stop(name, realm, seed, uuid): @click.option("--name", envvar="OTP_NAME", required=True) @click.option("--realm", envvar="OTP_REALM", required=True) @click.option("--seed", envvar="OTP_SEED", required=True) -@click.option("--uuid", required=True) -def delete(name, realm, seed, uuid): - r = vm_command("delete", OTPCredentials(name, realm, seed), uuid) +@click.option("--vm-name", required=True) +@click.option("--in_support_of") +def delete(name, realm, seed, vm_name, in_support_of): + r = vm_command("delete", OTPCredentials(name, realm, seed), vm_name, in_support_of=in_support_of) print(load_dump_pretty(r.content)) @@ -69,24 +83,29 @@ def delete(name, realm, seed, uuid): @click.option("--name", envvar="OTP_NAME", required=True) @click.option("--realm", envvar="OTP_REALM", required=True) @click.option("--seed", envvar="OTP_SEED", required=True) -@click.option("--uuid", required=True) -def status(name, realm, seed, uuid): - data = {"name": name, "realm": realm, "seed": seed, "uuid": uuid} - r = requests.get(f"{config('UCLOUD_API_SERVER')}/vm/status", json=data) +@click.option("--vm-name", required=True) +@click.option("--in_support_of") +def status(name, realm, seed, vm_name, in_support_of): + otp = OTPCredentials(name, realm, seed) + data = {**otp.get_json(), "vm_name": vm_name, "in_support_of": in_support_of} + r = requests.get("{}/vm/status".format(config('UCLOUD_API_SERVER')), json=data) print(load_dump_pretty(r.content)) + @vm.command("migrate") @click.option("--name", envvar="OTP_NAME", required=True) @click.option("--realm", envvar="OTP_REALM", required=True) @click.option("--seed", envvar="OTP_SEED", required=True) -@click.option("--uuid", required=True) +@click.option("--vm-name", required=True) @click.option("--destination", required=True) -def vm_migration(name, realm, seed, uuid, destination): +@click.option("--in_support_of") +def vm_migration(name, realm, seed, vm_name, destination, in_support_of): otp = OTPCredentials(name, realm, seed) data = { **otp.get_json(), - "uuid": uuid, - "destination": destination + "vm_name": vm_name, + "destination": destination, + "in_support_of": in_support_of, } - r = requests.post(f"{config('UCLOUD_API_SERVER')}/vm/migrate", json=data) - print(load_dump_pretty(r.content)) \ No newline at end of file + r = requests.post("{}/vm/migrate".format(config('UCLOUD_API_SERVER')), json=data) + print(load_dump_pretty(r.content)) diff --git a/ucloud.py b/ucloud.py index ceb26fc..3713d58 100755 --- a/ucloud.py +++ b/ucloud.py @@ -4,7 +4,7 @@ from commands.vm import vm from commands.user import user from commands.host import host from commands.image import image - +from commands.network import network @click.group() def entry_point(): @@ -15,6 +15,7 @@ entry_point.add_command(vm) entry_point.add_command(user) entry_point.add_command(image) entry_point.add_command(host) +entry_point.add_command(network) if __name__ == "__main__": entry_point()