diff --git a/uncloud/cli/__init__.py b/uncloud/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/uncloud/cli/commands/__init__.py b/uncloud/cli/commands/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/uncloud/cli/commands/helper.py b/uncloud/cli/commands/helper.py new file mode 100755 index 0000000..6bef690 --- /dev/null +++ b/uncloud/cli/commands/helper.py @@ -0,0 +1,62 @@ +import json +import binascii +import click +import requests + +from os.path import join as join_path + +from pyotp import TOTP +from uncloud.settings import settings + + +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) + + +def make_request(*args, data=None, request_method=requests.post): + r = request_method( + join_path(settings['client']['api_server'], *args), json=data + ) + try: + print(load_dump_pretty(r.content)) + except Exception: + print('Error occurred while getting output from api server.') + + +def get_token(_, param, value): + if value is not None: + try: + token = TOTP(value).now() + except binascii.Error: + raise click.BadParameter('') + else: + param.name = 'token' + return token + + +def add_otp_options(f): + options = [ + click.option('--name', show_default='name mentioned in config file.', prompt=True), + click.option('--realm', show_default='realm mentioned in config file.', prompt=True), + click.option('--seed', callback=get_token, show_default='seed mentioned in config file', + prompt=True) + ] + + for opt in reversed(options): + f = opt(f) + + return f + + +def add_vm_options(f): + options = [ + click.option('--vm-name', required=True), + click.option('--action', required=True, default=f.__name__) + ] + for opt in reversed(options): + f = opt(f) + + return f diff --git a/uncloud/cli/commands/host.py b/uncloud/cli/commands/host.py new file mode 100755 index 0000000..29ee417 --- /dev/null +++ b/uncloud/cli/commands/host.py @@ -0,0 +1,31 @@ +import click +import requests + +from .helper import add_otp_options, make_request + + +@click.group() +def host(): + pass + + +@host.command('create') +@add_otp_options +@click.option('--hostname', 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) +def create(**kwargs): + kwargs['specs'] = { + 'cpu': kwargs.pop('cpu'), + 'ram': kwargs.pop('ram'), + 'os-ssd': kwargs.pop('os_ssd'), + 'hdd': kwargs.pop('hdd') + } + make_request('host', 'create', data=kwargs) + + +@host.command('list') +def list_host(): + make_request('host', 'list', request_method=requests.get) diff --git a/uncloud/cli/commands/image.py b/uncloud/cli/commands/image.py new file mode 100755 index 0000000..6f9fe86 --- /dev/null +++ b/uncloud/cli/commands/image.py @@ -0,0 +1,24 @@ +import click +import requests + +from .helper import make_request + + +@click.group() +def image(): + pass + + +@image.command('list') +@click.option('--public', is_flag=True) +def _list(public): + if public: + make_request('image', 'list-public', request_method=requests.get) + + +@image.command('create-from-file') +@click.option('--name', required=True) +@click.option('--uuid', required=True) +@click.option('--image-store-name', 'image_store', required=True) +def create_from_file(**kwargs): + make_request('image', 'create', data=kwargs) diff --git a/uncloud/cli/commands/network.py b/uncloud/cli/commands/network.py new file mode 100644 index 0000000..81e22d5 --- /dev/null +++ b/uncloud/cli/commands/network.py @@ -0,0 +1,17 @@ +import click + +from .helper import add_otp_options, make_request + + +@click.group() +def network(): + pass + + +@network.command('create') +@add_otp_options +@click.option('--network-name', required=True) +@click.option('--network-type', 'type', required=True) +@click.option('--user', required=True, type=bool, default=False) +def create(**kwargs): + make_request('network', 'create', data=kwargs) diff --git a/uncloud/cli/commands/user.py b/uncloud/cli/commands/user.py new file mode 100755 index 0000000..2116520 --- /dev/null +++ b/uncloud/cli/commands/user.py @@ -0,0 +1,48 @@ +import click + +from .helper import add_otp_options, make_request + + +@click.group() +def user(): + pass + + +@user.command('files') +@add_otp_options +def list_files(**kwargs): + make_request('user', 'files', data=kwargs) + + +@user.command('vms') +@add_otp_options +def list_vms(**kwargs): + make_request('user', 'vms', data=kwargs) + + +@user.command('networks') +@add_otp_options +def list_networks(**kwargs): + make_request('user', 'network', data=kwargs) + + +@user.command('add-ssh') +@add_otp_options +@click.option('--key-name', required=True) +@click.option('--key', required=True) +def add_ssh(**kwargs): + make_request('user', 'add-ssh', data=kwargs) + + +@user.command('remove-ssh') +@add_otp_options +@click.option('--key-name', required=True) +def remove_ssh(**kwargs): + make_request('user', 'remove-ssh', data=kwargs) + + +@user.command('get-ssh') +@add_otp_options +@click.option('--key-name', default='') +def get_ssh(**kwargs): + make_request('user', 'get-ssh', data=kwargs) diff --git a/uncloud/cli/commands/vm.py b/uncloud/cli/commands/vm.py new file mode 100755 index 0000000..8527ac2 --- /dev/null +++ b/uncloud/cli/commands/vm.py @@ -0,0 +1,63 @@ +import click + +from .helper import add_otp_options, make_request, add_vm_options + + +@click.group() +def vm(): + pass + + +@vm.command('create') +@add_otp_options +@add_vm_options +@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(**kwargs): + kwargs['specs'] = { + 'cpu': kwargs.pop('cpu'), + 'ram': kwargs.pop('ram'), + 'os-ssd': kwargs.pop('os_ssd'), + 'hdd': kwargs.pop('hdd') + } + make_request('vm', kwargs.pop('action'), data=kwargs) + + +@vm.command('start') +@add_otp_options +@add_vm_options +def start(**kwargs): + make_request('vm', 'action', data=kwargs) + + +@vm.command('stop') +@add_otp_options +@add_vm_options +def stop(**kwargs): + make_request('vm', 'action', data=kwargs) + + +@vm.command('delete') +@add_otp_options +@add_vm_options +def delete(**kwargs): + make_request('vm', 'action', data=kwargs) + + +@vm.command('status') +@add_otp_options +@click.option('--vm-name', required=True) +def status(**kwargs): + make_request('vm', 'status', data=kwargs) + + +@vm.command('migrate') +@add_otp_options +@click.option('--vm-name', required=True) +@click.option('--destination', required=True) +def vm_migration(**kwargs): + make_request('vm', 'migrate', data=kwargs) diff --git a/uncloud/cli/main.py b/uncloud/cli/main.py new file mode 100644 index 0000000..7d625c4 --- /dev/null +++ b/uncloud/cli/main.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +import click + +from uncloud.cli.commands.vm import vm +from uncloud.cli.commands.user import user +from uncloud.cli.commands.host import host +from uncloud.cli.commands.image import image +from uncloud.cli.commands.network import network + + +@click.group() +def cli(): + pass + + +cli.add_command(vm) +cli.add_command(user) +cli.add_command(image) +cli.add_command(host) +cli.add_command(network) + +if __name__ == '__main__': + cli() diff --git a/uncloud/filescanner/main.py b/uncloud/filescanner/main.py index 7ce8654..bb318c3 100755 --- a/uncloud/filescanner/main.py +++ b/uncloud/filescanner/main.py @@ -67,7 +67,7 @@ def track_file(file, base_dir): os.setxattr(file, "user.utracked", b"True") -def main(): +def main(debug=False): base_dir = settings["storage"]["file_dir"] # Recursively Get All Files and Folder below BASE_DIR diff --git a/uncloud/host/main.py b/uncloud/host/main.py index 5ce9e6e..e469725 100755 --- a/uncloud/host/main.py +++ b/uncloud/host/main.py @@ -40,7 +40,7 @@ def maintenance(host): shared.vm_pool.put(vm) -def main(hostname): +def main(hostname, debug=False): host_pool = shared.host_pool host = next(filter(lambda h: h.hostname == hostname, host_pool.hosts), None) diff --git a/uncloud/imagescanner/main.py b/uncloud/imagescanner/main.py index 93e4dd5..fc77809 100755 --- a/uncloud/imagescanner/main.py +++ b/uncloud/imagescanner/main.py @@ -26,7 +26,7 @@ def qemu_img_type(path): return qemu_img_info["format"] -def main(): +def main(debug=False): # We want to get images entries that requests images to be created images = shared.etcd_client.get_prefix( settings["etcd"]["image_prefix"], value_in_json=True diff --git a/uncloud/metadata/main.py b/uncloud/metadata/main.py index 389b9a0..a59a998 100644 --- a/uncloud/metadata/main.py +++ b/uncloud/metadata/main.py @@ -111,8 +111,8 @@ class Root(Resource): api.add_resource(Root, "/") -def main(port=None): - app.run(debug=True, host="::", port=port) +def main(port=None, debug=False): + app.run(debug=debug, host="::", port=port) if __name__ == "__main__": diff --git a/uncloud/scheduler/main.py b/uncloud/scheduler/main.py index 20fa0d6..79b1edc 100755 --- a/uncloud/scheduler/main.py +++ b/uncloud/scheduler/main.py @@ -43,29 +43,19 @@ def main(debug=False): dead_host_mitigation(dead_hosts) elif request_entry.type == RequestType.ScheduleVM: - print(request_event.value) - logger.debug( - "%s, %s", request_entry.key, request_entry.value - ) + logger.debug("%s, %s", request_entry.key, request_entry.value) vm_entry = shared.vm_pool.get(request_entry.uuid) if vm_entry is None: - logger.info( - "Trying to act on {} but it is deleted".format( - request_entry.uuid - ) - ) + logger.info("Trying to act on {} but it is deleted".format(request_entry.uuid)) continue - shared.etcd_client.client.delete( - request_entry.key - ) # consume Request + + shared.etcd_client.client.delete(request_entry.key) # consume Request try: assign_host(vm_entry) except NoSuitableHostFound: - vm_entry.add_log( - "Can't schedule VM. No Resource Left." - ) + vm_entry.add_log("Can't schedule VM. No Resource Left.") shared.vm_pool.put(vm_entry) logger.info("No Resource Left. Emailing admin....")