diff --git a/planutils/__init__.py b/planutils/__init__.py index a4b9f25..c7b6ed3 100644 --- a/planutils/__init__.py +++ b/planutils/__init__.py @@ -85,6 +85,13 @@ def main(): parser_run.add_argument('package', help='package name') parser_run.add_argument('options', help='commandline options for the package', nargs="*") + parser_remote = subparsers.add_parser('remote', help='run package remotely') + parser_remote.add_argument('package', help='package name') + parser_remote.add_argument('options', help='commandline options for the package', nargs="*") + + parser_remote_list = subparsers.add_parser('remote-list', help='list the available remote packages') + + parser_checkinstalled = subparsers.add_parser('check-installed', help='check if a package is installed') parser_checkinstalled.add_argument('package', help='package name') @@ -116,6 +123,14 @@ def main(): from planutils.package_installation import run run(args.package, args.options) + elif 'remote' == args.command: + from planutils.package_installation import remote + remote(args.package, args.options) + + elif 'remote-list' == args.command: + from planutils.package_installation import package_remote_list + package_remote_list() + elif 'list' == args.command: from planutils.package_installation import package_list package_list() diff --git a/planutils/package_installation.py b/planutils/package_installation.py index 97b2a28..f1d76b4 100644 --- a/planutils/package_installation.py +++ b/planutils/package_installation.py @@ -1,10 +1,9 @@ -import json, os, glob, subprocess, sys +import json, os, glob, subprocess, sys, requests, time from collections import defaultdict from pathlib import Path from planutils import settings -from planutils import manifest_converter PACKAGES = {} @@ -193,3 +192,96 @@ def run(target, options): if not PACKAGES[target]["runnable"]: sys.exit(f"Package {target} is not executable") subprocess.run([Path(settings.PLANUTILS_PREFIX) / "packages" / target / "run"] + options) + +def package_remote_list(): + + package_url = settings.PAAS_SERVER + "/package" + r = requests.get(package_url) + remote_packages = r.json() + + print("\nDeployed:") + for p in remote_packages: + arguments = " ".join(f'{{{a["name"]}}} ' for a in p['endpoint']['services']['solve']['args']) + print(" %s: %s\n\tArguments: %s" % (p['package_name'], p['name'], arguments )) + +def remote(target, options): + + # 1. check if the target is deployed + + package_url = settings.PAAS_SERVER + "/package" + r = requests.get(package_url) + remote_packages = r.json() + + remote_package = None + for p in remote_packages: + if p['package_name'] == target: + remote_package = p + break + + if (remote_package is None) or ('solve' not in remote_package['endpoint']['services']): + sys.exit(f"Package {target} is not remotely deployed") + + + # 2. unpack the options and target to the right json + + json_options = {} + remote_call = remote_package['endpoint']['services']['solve']['call'] + call_parts = remote_call.split(' ') + + args = {arg['name']: arg for arg in remote_package['endpoint']['services']['solve']['args']} + + if len(options) != len(args): + sys.exit(f"Call string does not match the remote call: {remote_package['endpoint']['services']['solve']['call']}") + + call_map = {} + for i, step in enumerate(call_parts[1:]): + if '{' == step[0] and '}' == step[-1]: + option = step[1:-1] + call_map[option] = options[i] + if option not in args: + sys.exit(f"Option {option} from call string is not defined in the remote call: {remote_call}") + if args[option]['type'] == 'file': + with open(options[i], 'r') as f: + json_options[option] = f.read() + else: + json_options[option] = options[i] + + rcall = remote_call + for k, v in call_map.items(): + rcall = rcall.replace('{' + k + '}', v) + print("\nMaking remote call: %s" % rcall) + + + # 3. run the remote command + + solve_url = '/'.join([settings.PAAS_SERVER, 'package', target, 'solve']) + r = requests.post(solve_url, json=json_options) + if r.status_code != 200: + sys.exit(f"Error running remote call: {r.text}") + + result_url = f"{settings.PAAS_SERVER}/{r.json()['result']}" + + # call every 0.5s until the result is ready + result = None + for _ in range(settings.PAAS_SERVER_LIMIT): + r = requests.get(result_url) + if (r.status_code == 200) and ('status' in r.json()) and (r.json()['status'] == 'ok'): + result = r.json()['result'] + break + time.sleep(0.5) + + if result is None: + sys.exit(f"Error running remote call: {r.text}") + + + # 4. unpack the results back to the client, including any new files + + result = r.json()['result'] + for fn in result['output']: + with open(fn, 'w') as f: + f.write(result['output'][fn]) + + print("\n\t{stdout}\n") + print(result['stdout']) + print("\n\t{stderr}\n") + print(result['stderr']) diff --git a/planutils/settings.py b/planutils/settings.py index 9f4ad37..f726861 100644 --- a/planutils/settings.py +++ b/planutils/settings.py @@ -8,6 +8,9 @@ SETTINGS_FILE = os.path.join(PLANUTILS_PREFIX, 'settings.json') +PAAS_SERVER = 'http://45.113.232.43:5001' +PAAS_SERVER_LIMIT = 100 + def load(): with open(SETTINGS_FILE, 'r') as f: settings = json.loads(f.read())