mirror of https://git.48k.eu/ogcli/
Compare commits
18 Commits
Author | SHA1 | Date |
---|---|---|
|
4fe97139c5 | |
|
7d2678422e | |
|
2f870d7b6a | |
|
80a68ceb5a | |
|
12d965ce1c | |
|
4b77e1bca8 | |
|
0225502832 | |
|
85d910d020 | |
|
c4eb5d165a | |
|
10a3897f92 | |
|
ea8210a805 | |
|
59f2f501aa | |
|
8f5b709212 | |
|
175a38606a | |
|
7f09d148b4 | |
|
7478b9ce15 | |
|
6a96a130b5 | |
|
268b76b3ea |
222
cli/cli.py
222
cli/cli.py
|
@ -20,109 +20,129 @@ from cli.objects.center import OgCenter
|
|||
from cli.objects.room import OgRoom
|
||||
from cli.objects.folder import OgFolder
|
||||
from cli.objects.session import OgSession
|
||||
from cli.config import cfg
|
||||
import argparse
|
||||
import requests
|
||||
import sys
|
||||
|
||||
|
||||
def _log_http_status_code(res):
|
||||
if res.status_code == 400:
|
||||
print('Error 400: invalid payload')
|
||||
elif res.status_code == 404:
|
||||
print('Error 404: object not found')
|
||||
elif res.status_code == 405:
|
||||
print('Error 405: method not allowed')
|
||||
elif res.status_code == 409:
|
||||
print('Error 409: object already exists')
|
||||
elif res.status_code == 423:
|
||||
print('Error 423: object in use')
|
||||
elif res.status_code == 501:
|
||||
print('Error 501: cannot connect to database')
|
||||
elif res.status_code == 507:
|
||||
print('Error 500: disk full')
|
||||
else:
|
||||
print(f'Received status code {res.status_code}')
|
||||
|
||||
class OgREST():
|
||||
def __init__(self, ip, port, api_token):
|
||||
if not ip or not port or not api_token:
|
||||
raise ValueError("IP, port, and API token must be provided.")
|
||||
self.URL = f'http://{ip}:{port}'
|
||||
self.HEADERS = {'Authorization': api_token}
|
||||
|
||||
def get(self, path, payload=None):
|
||||
def _request(self, method, path, payload, expected_status):
|
||||
try:
|
||||
r = requests.get(f'{self.URL}{path}',
|
||||
headers=self.HEADERS,
|
||||
json=payload)
|
||||
if r.status_code != 200:
|
||||
sys.exit(f"Unsuccessful request to ogServer: "
|
||||
f"Response with {r.status_code} HTTP status code")
|
||||
except IOError as e:
|
||||
sys.exit(f"Cannot connect to ogServer: {e}")
|
||||
return r
|
||||
res = requests.request(
|
||||
method,
|
||||
f'{self.URL}{path}',
|
||||
headers=self.HEADERS,
|
||||
json=payload,
|
||||
)
|
||||
if res.status_code not in expected_status:
|
||||
_log_http_status_code(res)
|
||||
return None
|
||||
return res
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print("Cannot connect to ogserver")
|
||||
except requests.exceptions.Timeout:
|
||||
print("Request to ogserver timed out")
|
||||
except requests.exceptions.TooManyRedirects:
|
||||
print("Too many redirects occurred while contacting ogserver")
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"An error occurred while contacting ogserver: {e}")
|
||||
return None
|
||||
|
||||
def get(self, path, payload=None):
|
||||
return self._request('GET', path, payload, expected_status={200})
|
||||
|
||||
def post(self, path, payload):
|
||||
try:
|
||||
r = requests.post(f'{self.URL}{path}',
|
||||
headers=self.HEADERS,
|
||||
json=payload)
|
||||
if r.text:
|
||||
print(r.text)
|
||||
|
||||
if r.status_code not in {200, 202}:
|
||||
sys.exit(f"Unsuccessful request to ogServer: "
|
||||
f"Response with {r.status_code} HTTP status code")
|
||||
except IOError as e:
|
||||
sys.exit(f"Cannot connect to ogServer: {e}")
|
||||
return r
|
||||
return self._request('POST', path, payload, expected_status={200, 202})
|
||||
|
||||
def delete(self, path, payload):
|
||||
try:
|
||||
r = requests.delete(f'{self.URL}{path}',
|
||||
headers=self.HEADERS,
|
||||
json=payload)
|
||||
print(r.text)
|
||||
if r.status_code != 200:
|
||||
sys.exit(f"Unsuccessful request to ogServer: "
|
||||
f"Response with {r.status_code} HTTP status code")
|
||||
except IOError as e:
|
||||
sys.exit(f"Cannot connect to ogServer: {e}")
|
||||
return r
|
||||
return self._request('DELETE', path, payload, expected_status={200})
|
||||
|
||||
|
||||
class OgCLI():
|
||||
def __init__(self, cfg):
|
||||
def __init__(self):
|
||||
self.rest = OgREST(cfg['ip'], cfg['port'], cfg['api_token'])
|
||||
|
||||
def list(self, args):
|
||||
choices = ['clients', 'scopes', 'modes', 'hardware',
|
||||
'client', 'images', 'disks', 'servers', 'repos', 'live']
|
||||
'client', 'images', 'disks', 'servers', 'repos',
|
||||
'live', 'lives']
|
||||
parser = argparse.ArgumentParser(prog='ogcli list')
|
||||
parser.add_argument('item', choices=choices)
|
||||
|
||||
if not args:
|
||||
print('Missing list subcommand', file=sys.stderr)
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
parsed_args = parser.parse_args([args[0]])
|
||||
ret = 0
|
||||
if parsed_args.item == 'clients':
|
||||
OgClient.list_clients(self.rest)
|
||||
ret = OgClient.list_clients(self.rest)
|
||||
elif parsed_args.item == 'client':
|
||||
OgClient.get_client_properties(self.rest, args[1:])
|
||||
ret = OgClient.get_client_properties(self.rest, args[1:])
|
||||
elif parsed_args.item == 'hardware':
|
||||
OgClient.list_client_hardware(self.rest, args[1:])
|
||||
ret = OgClient.list_client_hardware(self.rest, args[1:])
|
||||
elif parsed_args.item == 'modes':
|
||||
OgModes.list_available_modes(self.rest)
|
||||
ret = OgModes.list_available_modes(self.rest)
|
||||
elif parsed_args.item == 'scopes':
|
||||
OgScope.list_scopes(self.rest, args[1:])
|
||||
ret = OgScope.list_scopes(self.rest, args[1:])
|
||||
elif parsed_args.item == 'images':
|
||||
OgImage.list_images(self.rest)
|
||||
ret = OgImage.list_images(self.rest)
|
||||
elif parsed_args.item == 'disks':
|
||||
OgDisk.list_disks(self.rest, args[1:])
|
||||
ret = OgDisk.list_disks(self.rest, args[1:])
|
||||
elif parsed_args.item == 'repos':
|
||||
OgRepo.list_repos(self.rest)
|
||||
ret = OgRepo.list_repos(self.rest)
|
||||
elif parsed_args.item == 'servers':
|
||||
OgServer.list_servers(self.rest)
|
||||
elif parsed_args.item == 'live':
|
||||
OgLive.list_live(self.rest)
|
||||
ret = OgServer.list_servers(self.rest)
|
||||
elif parsed_args.item in ['live', 'lives']:
|
||||
ret = OgLive.list_live(self.rest, args[1:])
|
||||
return ret
|
||||
|
||||
def set(self, args):
|
||||
choices = ['modes', 'mode', 'repo']
|
||||
choices = ['modes', 'mode', 'repo', 'live', 'lives']
|
||||
parser = argparse.ArgumentParser(prog='ogcli set')
|
||||
parser.add_argument('item', choices=choices)
|
||||
|
||||
if not args:
|
||||
print('Missing set subcommand', file=sys.stderr)
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
parsed_args = parser.parse_args([args[0]])
|
||||
ret = 0
|
||||
if parsed_args.item in ['modes', 'mode']:
|
||||
OgModes.set_modes(self.rest, args[1:])
|
||||
ret = OgModes.set_modes(self.rest, args[1:])
|
||||
elif parsed_args.item == 'repo':
|
||||
OgRepo.set_repo(self.rest, args[1:])
|
||||
ret = OgRepo.set_repo(self.rest, args[1:])
|
||||
elif parsed_args.item in ['live', 'lives']:
|
||||
ret = OgLive.set_live(self.rest, args[1:])
|
||||
return ret
|
||||
|
||||
def request(self, args):
|
||||
choices = ['reboot', 'refresh', 'poweroff', 'wol', 'session']
|
||||
|
@ -132,19 +152,21 @@ class OgCLI():
|
|||
if not args:
|
||||
print('Missing request subcommand', file=sys.stderr)
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
parsed_args = parser.parse_args([args[0]])
|
||||
ret = 0
|
||||
if parsed_args.request_obj == 'wol':
|
||||
OgWol.request_wol(self.rest, args[1:])
|
||||
ret = OgWol.request_wol(self.rest, args[1:])
|
||||
elif parsed_args.request_obj == 'poweroff':
|
||||
OgPoweroff.request_poweroff(self.rest, args[1:])
|
||||
ret = OgPoweroff.request_poweroff(self.rest, args[1:])
|
||||
elif parsed_args.request_obj == 'refresh':
|
||||
OgClient.request_refresh(self.rest, args[1:])
|
||||
ret = OgClient.request_refresh(self.rest, args[1:])
|
||||
elif parsed_args.request_obj == 'reboot':
|
||||
OgReboot.request_reboot(self.rest, args[1:])
|
||||
ret = OgReboot.request_reboot(self.rest, args[1:])
|
||||
elif parsed_args.request_obj == 'session':
|
||||
OgSession.request_session(self.rest, args[1:])
|
||||
ret = OgSession.request_session(self.rest, args[1:])
|
||||
return ret
|
||||
|
||||
def restore(self, args):
|
||||
choices = ['image']
|
||||
|
@ -154,11 +176,13 @@ class OgCLI():
|
|||
if not args:
|
||||
print('Missing restore subcommand', file=sys.stderr)
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
parsed_args = parser.parse_args([args[0]])
|
||||
ret = 0
|
||||
if parsed_args.restore_obj == 'image':
|
||||
OgImage.restore_image(self.rest, args[1:])
|
||||
ret = OgImage.restore_image(self.rest, args[1:])
|
||||
return ret
|
||||
|
||||
def create(self, args):
|
||||
choices = ['image']
|
||||
|
@ -168,11 +192,13 @@ class OgCLI():
|
|||
if not args:
|
||||
print('Missing create subcommand', file=sys.stderr)
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
parsed_args = parser.parse_args([args[0]])
|
||||
ret = 0
|
||||
if parsed_args.create_obj == 'image':
|
||||
OgImage.create_image(self.rest, args[1:])
|
||||
ret = OgImage.create_image(self.rest, args[1:])
|
||||
return ret
|
||||
|
||||
def setup(self, args):
|
||||
choices = ['disk']
|
||||
|
@ -182,11 +208,13 @@ class OgCLI():
|
|||
if not args:
|
||||
print('Missing setup subcommand', file=sys.stderr)
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
parsed_args = parser.parse_args([args[0]])
|
||||
ret = 0
|
||||
if parsed_args.setup_obj == 'disk':
|
||||
OgDisk.setup_disk(self.rest, args[1:])
|
||||
ret = OgDisk.setup_disk(self.rest, args[1:])
|
||||
return ret
|
||||
|
||||
def update(self, args):
|
||||
choices = ['image', 'repo', 'center', 'room', 'folder']
|
||||
|
@ -196,43 +224,49 @@ class OgCLI():
|
|||
if not args:
|
||||
print('Missing update subcommand', file=sys.stderr)
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
parsed_args = parser.parse_args([args[0]])
|
||||
ret = 0
|
||||
if parsed_args.update_obj == 'image':
|
||||
OgImage.update_image(self.rest, args[1:])
|
||||
ret = OgImage.update_image(self.rest, args[1:])
|
||||
elif parsed_args.update_obj == 'center':
|
||||
OgCenter.update_center(self.rest, args[1:])
|
||||
ret = OgCenter.update_center(self.rest, args[1:])
|
||||
elif parsed_args.update_obj == 'room':
|
||||
OgRoom.update_room(self.rest, args[1:])
|
||||
ret = OgRoom.update_room(self.rest, args[1:])
|
||||
elif parsed_args.update_obj == 'folder':
|
||||
OgFolder.update_folder(self.rest, args[1:])
|
||||
ret = OgFolder.update_folder(self.rest, args[1:])
|
||||
elif parsed_args.update_obj == 'repo':
|
||||
OgRepo.update_repo(self.rest, args[1:])
|
||||
ret = OgRepo.update_repo(self.rest, args[1:])
|
||||
return ret
|
||||
|
||||
def delete(self, args):
|
||||
choices = ['server', 'repo', 'center', 'room', 'client', 'folder']
|
||||
choices = ['server', 'repo', 'center', 'room', 'client', 'folder',
|
||||
'live', 'lives']
|
||||
parser = argparse.ArgumentParser(prog='ogcli delete')
|
||||
parser.add_argument('delete_obj', choices=choices)
|
||||
|
||||
if not args:
|
||||
print('Missing delete subcommand', file=sys.stderr)
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
parsed_args = parser.parse_args([args[0]])
|
||||
if parsed_args.delete_obj == 'server':
|
||||
OgServer.delete_server(self.rest, args[1:])
|
||||
ret = OgServer.delete_server(self.rest, args[1:])
|
||||
elif parsed_args.delete_obj == 'repo':
|
||||
OgRepo.delete_repo(self.rest, args[1:])
|
||||
ret = OgRepo.delete_repo(self.rest, args[1:])
|
||||
elif parsed_args.delete_obj == 'center':
|
||||
OgCenter.delete_center(self.rest, args[1:])
|
||||
ret = OgCenter.delete_center(self.rest, args[1:])
|
||||
elif parsed_args.delete_obj == 'room':
|
||||
OgRoom.delete_room(self.rest, args[1:])
|
||||
ret = OgRoom.delete_room(self.rest, args[1:])
|
||||
elif parsed_args.delete_obj == 'client':
|
||||
OgClient.delete_client(self.rest, args[1:])
|
||||
ret = OgClient.delete_client(self.rest, args[1:])
|
||||
elif parsed_args.delete_obj == 'folder':
|
||||
OgFolder.delete_folder(self.rest, args[1:])
|
||||
ret = OgFolder.delete_folder(self.rest, args[1:])
|
||||
elif parsed_args.delete_obj in ['live', 'lives']:
|
||||
ret = OgLive.delete_live(self.rest, args[1:])
|
||||
return ret
|
||||
|
||||
def add(self, args):
|
||||
choices = ['server', 'repo', 'center', 'room', 'client', 'folder']
|
||||
|
@ -242,18 +276,36 @@ class OgCLI():
|
|||
if not args:
|
||||
print('Missing add subcommand', file=sys.stderr)
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
parsed_args = parser.parse_args([args[0]])
|
||||
ret = 0
|
||||
if parsed_args.add_obj == 'server':
|
||||
OgServer.add_server(self.rest, args[1:])
|
||||
ret = OgServer.add_server(self.rest, args[1:])
|
||||
elif parsed_args.add_obj == 'repo':
|
||||
OgRepo.add_repo(self.rest, args[1:])
|
||||
ret = OgRepo.add_repo(self.rest, args[1:])
|
||||
elif parsed_args.add_obj == 'center':
|
||||
OgCenter.add_center(self.rest, args[1:])
|
||||
ret = OgCenter.add_center(self.rest, args[1:])
|
||||
elif parsed_args.add_obj == 'room':
|
||||
OgRoom.add_room(self.rest, args[1:])
|
||||
ret = OgRoom.add_room(self.rest, args[1:])
|
||||
elif parsed_args.add_obj == 'client':
|
||||
OgClient.add_client(self.rest, args[1:])
|
||||
ret = OgClient.add_client(self.rest, args[1:])
|
||||
elif parsed_args.add_obj == 'folder':
|
||||
OgFolder.add_folder(self.rest, args[1:])
|
||||
ret = OgFolder.add_folder(self.rest, args[1:])
|
||||
return ret
|
||||
|
||||
def install(self, args):
|
||||
choices = ['live', 'lives']
|
||||
parser = argparse.ArgumentParser(prog='ogcli install')
|
||||
parser.add_argument('install_obj', choices=choices)
|
||||
|
||||
if not args:
|
||||
print('Missing install subcommand', file=sys.stderr)
|
||||
parser.print_help(file=sys.stderr)
|
||||
return 1
|
||||
|
||||
parsed_args = parser.parse_args([args[0]])
|
||||
ret = 0
|
||||
if parsed_args.install_obj in ['live', 'lives']:
|
||||
ret = OgLive.install_live(self.rest, args[1:])
|
||||
return ret
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2020-2024 Soleta Networks <opengnsys@soleta.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the
|
||||
# Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
import json
|
||||
|
||||
OG_CLI_CFG_PATH = "/opt/opengnsys/etc/ogcli.json"
|
||||
|
||||
def load_config():
|
||||
data = None
|
||||
try:
|
||||
with open(OG_CLI_CFG_PATH, 'r') as json_file:
|
||||
data = json.load(json_file)
|
||||
except json.JSONDecodeError:
|
||||
sys.exit(f'ERROR: Failed parse malformed JSON file {OG_CLI_CFG_PATH}')
|
||||
except Exception as e:
|
||||
sys.exit(f'ERROR: cannot open {OG_CLI_CFG_PATH}: {e}')
|
||||
|
||||
required_cfg_params = {'api_token', 'ip', 'port'}
|
||||
difference_cfg_params = required_cfg_params - data.keys()
|
||||
if len(difference_cfg_params) > 0:
|
||||
sys.exit(f'Missing {difference_cfg_params} key in '
|
||||
f'json config file')
|
||||
return data
|
||||
|
||||
cfg = load_config()
|
|
@ -23,7 +23,10 @@ class OgCenter():
|
|||
payload = {'name': parsed_args.name}
|
||||
if parsed_args.desc:
|
||||
payload['comment'] = parsed_args.desc
|
||||
rest.post('/center/add', payload=payload)
|
||||
res = rest.post('/center/add', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def update_center(rest, args):
|
||||
|
@ -48,7 +51,10 @@ class OgCenter():
|
|||
}
|
||||
if parsed_args.comment:
|
||||
payload['comment'] = parsed_args.comment
|
||||
rest.post('/center/update', payload=payload)
|
||||
res = rest.post('/center/update', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def delete_center(rest, args):
|
||||
|
@ -60,4 +66,7 @@ class OgCenter():
|
|||
help='center id in database')
|
||||
parsed_args = parser.parse_args(args)
|
||||
payload = {'id': parsed_args.id}
|
||||
rest.post('/center/delete', payload=payload)
|
||||
res = rest.post('/center/delete', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -14,8 +14,11 @@ class OgClient():
|
|||
|
||||
@staticmethod
|
||||
def list_clients(rest):
|
||||
r = rest.get('/clients')
|
||||
print_json(r.text)
|
||||
res = rest.get('/clients')
|
||||
if not res:
|
||||
return 1
|
||||
print_json(res.text)
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def list_client_hardware(rest, args):
|
||||
|
@ -28,8 +31,11 @@ class OgClient():
|
|||
parsed_args = parser.parse_args(args)
|
||||
|
||||
payload = {'client': parsed_args.client_ip}
|
||||
r = rest.get('/hardware', payload=payload)
|
||||
print_json(r.text)
|
||||
res = rest.get('/hardware', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
print_json(res.text)
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def get_client_properties(rest, args):
|
||||
|
@ -41,8 +47,11 @@ class OgClient():
|
|||
parsed_args = parser.parse_args(args)
|
||||
|
||||
payload = {'client': parsed_args.client_ip}
|
||||
r = rest.get('/client/info', payload=payload)
|
||||
print_json(r.text)
|
||||
res = rest.get('/client/info', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
print_json(res.text)
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def request_refresh(rest, args):
|
||||
|
@ -55,7 +64,10 @@ class OgClient():
|
|||
parsed_args = parser.parse_args(args)
|
||||
|
||||
payload = {'clients': parsed_args.client_ip}
|
||||
rest.post('/refresh', payload=payload)
|
||||
res = rest.post('/refresh', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def add_client(rest, args):
|
||||
|
@ -82,8 +94,8 @@ class OgClient():
|
|||
nargs='?',
|
||||
required=True,
|
||||
help='specify the ip address of the client')
|
||||
r = rest.get('/mode')
|
||||
boot_choices = r.json()['modes']
|
||||
res = rest.get('/mode')
|
||||
boot_choices = res.json()['modes']
|
||||
parser.add_argument('--boot-mode',
|
||||
nargs='?',
|
||||
required=True,
|
||||
|
@ -100,7 +112,7 @@ class OgClient():
|
|||
err = True
|
||||
if err:
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
payload = {
|
||||
'name': parsed_args.hostname,
|
||||
'netmask': '0',
|
||||
|
@ -113,7 +125,10 @@ class OgClient():
|
|||
'netdriver': 'generic',
|
||||
'livedir': 'ogLive'
|
||||
}
|
||||
rest.post('/client/add', payload=payload)
|
||||
res = rest.post('/client/add', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def delete_client(rest, args):
|
||||
|
@ -127,4 +142,7 @@ class OgClient():
|
|||
payload = {
|
||||
'clients': parsed_args.ip,
|
||||
}
|
||||
rest.post('/client/delete', payload=payload)
|
||||
res = rest.post('/client/delete', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -23,8 +23,11 @@ class OgDisk():
|
|||
parsed_args = parser.parse_args(args)
|
||||
payload = {'client': [parsed_args.client_ip]}
|
||||
|
||||
r = rest.get('/client/setup', payload=payload)
|
||||
print_json(r.text)
|
||||
res = rest.get('/client/setup', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
print_json(res.text)
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def setup_disk(rest, args):
|
||||
|
@ -79,8 +82,8 @@ class OgDisk():
|
|||
help='Specific client IP')
|
||||
|
||||
parsed_args = parser.parse_args(args)
|
||||
r = rest.get('/scopes')
|
||||
scopes = r.json()
|
||||
res = rest.get('/scopes')
|
||||
scopes = res.json()
|
||||
ips = set()
|
||||
|
||||
for center in parsed_args.center_id:
|
||||
|
@ -93,11 +96,11 @@ class OgDisk():
|
|||
ips.add(l)
|
||||
if not ips:
|
||||
print("Missing --client-ip, or --room-id/--center-id. No clients provided.")
|
||||
return None
|
||||
return 1
|
||||
|
||||
if not parsed_args.num.isdigit():
|
||||
print(f'Invalid disk number: must be an integer value')
|
||||
return
|
||||
return 1
|
||||
|
||||
payload = {'clients': parsed_args.client_ip, 'type': disk_type_map[parsed_args.type], 'disk': str(parsed_args.num),
|
||||
'cache': '0', 'cache_size': '0', 'partition_setup': []}
|
||||
|
@ -109,31 +112,31 @@ class OgDisk():
|
|||
|
||||
if len(p) != 4:
|
||||
print(f'Invalid partition: requires "num,part_scheme,fs,size", "{",".join(p)}" provided')
|
||||
return
|
||||
return 1
|
||||
part_num, code, fs, size = p[0], p[1].upper(), p[2].upper(), p[3]
|
||||
|
||||
if not part_num.isdigit():
|
||||
print(f'Invalid partition: the first parameter must be a number, "{part_num}" provided')
|
||||
return
|
||||
return 1
|
||||
|
||||
if part_num in defined_part_indices:
|
||||
print(f'Invalid partition: partition number "{part_num}" has multiple definitions')
|
||||
return
|
||||
return 1
|
||||
|
||||
defined_part_indices.add(part_num)
|
||||
|
||||
if code not in part_types:
|
||||
print(f'Invalid partition {i}: specified partition type {code} is not supported. The supported formats are {part_types}')
|
||||
return
|
||||
return 1
|
||||
|
||||
if fs not in fs_types:
|
||||
print(f'Invalid partition {i}: specified filesystem {fs} is not supported. The supported formats are {fs_types}')
|
||||
return
|
||||
return 1
|
||||
try:
|
||||
size = parse_size(size)
|
||||
except ValueError as error:
|
||||
print(f'Invalid partition {i}: {str(error)}')
|
||||
return
|
||||
return 1
|
||||
|
||||
for j in range(i, int(part_num)):
|
||||
part = {'partition': str(j), 'code': 'EMPTY',
|
||||
|
@ -158,4 +161,7 @@ class OgDisk():
|
|||
'format': '0'}
|
||||
payload['partition_setup'].append(part)
|
||||
|
||||
rest.post('/setup', payload=payload)
|
||||
res = rest.post('/setup', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -39,7 +39,10 @@ class OgFolder():
|
|||
payload['room'] = parsed_args.room_id
|
||||
if parsed_args.center_id:
|
||||
payload['center'] = parsed_args.center_id
|
||||
rest.post('/folder/add', payload=payload)
|
||||
res = rest.post('/folder/add', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def update_folder(rest, args):
|
||||
|
@ -58,7 +61,10 @@ class OgFolder():
|
|||
'id': parsed_args.id,
|
||||
'name': parsed_args.name,
|
||||
}
|
||||
rest.post('/folder/update', payload=payload)
|
||||
res = rest.post('/folder/update', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def delete_folder(rest, args):
|
||||
|
@ -70,4 +76,7 @@ class OgFolder():
|
|||
help='folder id in database')
|
||||
parsed_args = parser.parse_args(args)
|
||||
payload = {'id': parsed_args.id}
|
||||
rest.post('/folder/delete', payload=payload)
|
||||
res = rest.post('/folder/delete', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -21,10 +21,10 @@ def get_repository(repository_id, rest):
|
|||
|
||||
|
||||
def get_repositories(rest):
|
||||
r = rest.get('/repositories')
|
||||
if not r or r.status_code != requests.codes.ok:
|
||||
res = rest.get('/repositories')
|
||||
if not res:
|
||||
return None
|
||||
repositories = r.json()['repositories']
|
||||
repositories = res.json()['repositories']
|
||||
return repositories
|
||||
|
||||
|
||||
|
@ -32,8 +32,11 @@ class OgImage():
|
|||
|
||||
@staticmethod
|
||||
def list_images(rest):
|
||||
r = rest.get('/images')
|
||||
print_json(r.text)
|
||||
res = rest.get('/images')
|
||||
if not res:
|
||||
return 1
|
||||
print_json(res.text)
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def restore_image(rest, args):
|
||||
|
@ -78,8 +81,8 @@ class OgImage():
|
|||
help='Specific client IP')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
r = rest.get('/scopes')
|
||||
scopes = r.json()
|
||||
res = rest.get('/scopes')
|
||||
scopes = res.json()
|
||||
ips = set()
|
||||
|
||||
for center in parsed_args.center_id:
|
||||
|
@ -93,34 +96,37 @@ class OgImage():
|
|||
|
||||
if not ips:
|
||||
print('Missing --client-ip, or --room-id/--center-id. No clients provided.')
|
||||
return
|
||||
return 1
|
||||
|
||||
r = rest.get('/images')
|
||||
images = r.json()
|
||||
res = rest.get('/images')
|
||||
images = res.json()
|
||||
found_image = [img for img in images['images']
|
||||
if img['id'] == parsed_args.id]
|
||||
if not found_image:
|
||||
print(f'Image with id {parsed_args.id} not found.')
|
||||
return
|
||||
return 1
|
||||
else:
|
||||
found_image = found_image[0]
|
||||
|
||||
selected_repo_id = 0
|
||||
for ip in parsed_args.client_ip:
|
||||
r = rest.get('/client/info', payload={'client': ip)
|
||||
repo_id = r.json()['repo_id']
|
||||
res = rest.get('/client/info', payload={'client': ip})
|
||||
repo_id = res.json()['repo_id']
|
||||
if selected_repo_id == 0:
|
||||
selected_repo_id = repo_id
|
||||
elif selected_repo_id != repo_id:
|
||||
print(f'cannot restore clients assigned to different repositories')
|
||||
return
|
||||
return 1
|
||||
|
||||
payload = {'disk': parsed_args.disk, 'partition': parsed_args.part,
|
||||
'id': str(parsed_args.id), 'name': found_image['name'],
|
||||
'profile': str(found_image['software_id']),
|
||||
'repository_id': repo_id,
|
||||
'type': parsed_args.type.upper(), 'clients': list(ips)}
|
||||
r = rest.post('/image/restore', payload=payload)
|
||||
res = rest.post('/image/restore', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def create_image(rest, args):
|
||||
|
@ -149,19 +155,19 @@ class OgImage():
|
|||
help='Specific client IP')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
r = rest.get('/client/info', payload={'client': parsed_args.client_ip})
|
||||
center_id = r.json()['center']
|
||||
repo_id = r.json()['repo_id']
|
||||
res = rest.get('/client/info', payload={'client': parsed_args.client_ip})
|
||||
center_id = res.json()['center']
|
||||
repo_id = res.json()['repo_id']
|
||||
|
||||
r = rest.get('/client/setup',
|
||||
res = rest.get('/client/setup',
|
||||
payload={'client': parsed_args.client_ip})
|
||||
if r.status_code == 200:
|
||||
if res.status_code == 200:
|
||||
part_info = list(filter(lambda x: x['disk'] == int(parsed_args.disk) and
|
||||
x['partition'] == int(parsed_args.part),
|
||||
r.json()['partitions']))
|
||||
res.json()['partitions']))
|
||||
if not part_info:
|
||||
print('Partition not found.')
|
||||
return
|
||||
return 1
|
||||
fs_code = list(part_info)[0]['code']
|
||||
|
||||
image_name = remove_accents(parsed_args.name)
|
||||
|
@ -171,7 +177,10 @@ class OgImage():
|
|||
if parsed_args.desc:
|
||||
payload['description'] = parsed_args.desc
|
||||
|
||||
rest.post('/image/create', payload=payload)
|
||||
res = rest.post('/image/create', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def update_image(rest, args):
|
||||
|
@ -202,32 +211,32 @@ class OgImage():
|
|||
help='Specific client IP')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
r = rest.get('/client/info', payload={'client': parsed_args.client_ip})
|
||||
center_id = r.json()['center']
|
||||
repo_id = int(r.json()['repo_id'])
|
||||
res = rest.get('/client/info', payload={'client': parsed_args.client_ip})
|
||||
center_id = res.json()['center']
|
||||
repo_id = int(res.json()['repo_id'])
|
||||
|
||||
r = rest.get('/client/setup',
|
||||
res = rest.get('/client/setup',
|
||||
payload={'client': parsed_args.client_ip})
|
||||
if r.status_code == 200:
|
||||
if res.status_code == 200:
|
||||
part_info = list(filter(lambda x: x['disk'] == int(parsed_args.disk) and
|
||||
x['partition'] == int(parsed_args.part),
|
||||
r.json()['partitions']))
|
||||
res.json()['partitions']))
|
||||
if not part_info:
|
||||
print('Partition not found.')
|
||||
return
|
||||
return 1
|
||||
fs_code = list(part_info)[0]['code']
|
||||
|
||||
r = rest.get('/images')
|
||||
res = rest.get('/images')
|
||||
image_name = None
|
||||
if r.status_code == 200:
|
||||
for image in r.json()['images']:
|
||||
if res.status_code == 200:
|
||||
for image in res.json()['images']:
|
||||
if image['id'] == int(parsed_args.id):
|
||||
image_name = image['name']
|
||||
break
|
||||
|
||||
if not image_name:
|
||||
print('Image not found')
|
||||
return
|
||||
return 1
|
||||
else:
|
||||
print(f'Updating {image_name} image')
|
||||
|
||||
|
@ -241,4 +250,7 @@ class OgImage():
|
|||
'code': str(fs_code),
|
||||
'name': image_name}
|
||||
|
||||
rest.post('/image/update', payload=payload)
|
||||
res = rest.post('/image/update', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -7,11 +7,317 @@
|
|||
|
||||
import argparse
|
||||
|
||||
from cli.config import cfg, OG_CLI_CFG_PATH
|
||||
from datetime import datetime
|
||||
from cli.utils import *
|
||||
import requests
|
||||
import shutil
|
||||
import sys
|
||||
import os
|
||||
|
||||
class OgLive():
|
||||
|
||||
live_files = [
|
||||
'initrd.img',
|
||||
'vmlinuz',
|
||||
'filesystem.squashfs',
|
||||
]
|
||||
tmp_extension = '.tmp'
|
||||
|
||||
@staticmethod
|
||||
def list_live(rest):
|
||||
r = rest.get('/oglive/list')
|
||||
print_json(r.text)
|
||||
def _parse_timestamps(payload):
|
||||
for elem in payload['oglive']:
|
||||
if 'date' in elem:
|
||||
timestamp = elem['date']
|
||||
readable_date = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
||||
elem['date'] = readable_date
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _get_local_live_dir():
|
||||
local_live_dir = cfg.get('local_live', '/var/www/html/ogrelive')
|
||||
if not local_live_dir:
|
||||
print(f'Error: local_live not defined in {OG_CLI_CFG_PATH}')
|
||||
return None
|
||||
|
||||
if not os.path.isdir(local_live_dir):
|
||||
print(f'Warning: {local_live_dir} directoy does not exist, creating directory')
|
||||
try:
|
||||
os.makedirs(local_live_dir, exist_ok=True)
|
||||
except OSError as e:
|
||||
print(f'ERROR: Failed to create directory {local_live_dir}: {e}')
|
||||
return None
|
||||
return local_live_dir
|
||||
|
||||
@staticmethod
|
||||
def _get_server_base_url():
|
||||
server_live = cfg.get('server_live', 'https://opengnsys.soleta.eu/ogrelive')
|
||||
if not server_live:
|
||||
print(f'Error: server_live not defined in {OG_CLI_CFG_PATH}')
|
||||
return None
|
||||
return server_live
|
||||
|
||||
def _is_live_in_server(live_name):
|
||||
server_live = OgLive._get_server_base_url()
|
||||
target_url = f'{server_live}/{live_name}/{OgLive.live_files[0]}'
|
||||
response = requests.head(target_url)
|
||||
return response.status_code == 200
|
||||
|
||||
@staticmethod
|
||||
def _delete_tmp_live_files(live_name):
|
||||
local_live_dir = OgLive._get_local_live_dir()
|
||||
|
||||
if not local_live_dir:
|
||||
return 1
|
||||
|
||||
folder_path = os.path.join(local_live_dir, live_name)
|
||||
|
||||
for file_name in os.listdir(folder_path):
|
||||
if not file_name.endswith(OgLive.tmp_extension):
|
||||
continue
|
||||
target_file = os.path.join(folder_path, file_name)
|
||||
try:
|
||||
os.remove(target_file)
|
||||
except OSError as e:
|
||||
print(f'ERROR: Failed to delete temporary file {target_file}: {e}')
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def _download_from_server(file_path, local_extension):
|
||||
live_file = os.path.basename(file_path)
|
||||
|
||||
server_live = OgLive._get_server_base_url()
|
||||
if not server_live:
|
||||
return 1
|
||||
file_url = f'{server_live}/{file_path}'
|
||||
|
||||
local_live_dir = OgLive._get_local_live_dir()
|
||||
|
||||
if not local_live_dir:
|
||||
return 1
|
||||
|
||||
local_path = os.path.join(local_live_dir, file_path)
|
||||
if local_extension:
|
||||
local_path += local_extension
|
||||
|
||||
try:
|
||||
response = requests.get(file_url, stream=True, timeout=(5, None))
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f'Request failed for {file_url}: {e}')
|
||||
return 1
|
||||
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
with open(local_path, 'wb') as f:
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
except OSError as e:
|
||||
print(f'File system error occurred: {e}')
|
||||
return 1
|
||||
else:
|
||||
print(f'ERROR: Failed to download {live_file}. Status code: {response.status_code}')
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def list_live(rest, args):
|
||||
parser = argparse.ArgumentParser(prog='ogcli list live')
|
||||
parser.add_argument('--remote',
|
||||
action='store_true',
|
||||
help='(Optional) Obtain the list of the remote instead of the local lives')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
if parsed_args.remote:
|
||||
local_live_dir = OgLive._get_local_live_dir()
|
||||
|
||||
if not local_live_dir:
|
||||
return 1
|
||||
|
||||
download_err = OgLive._download_from_server('ogrelive.json',
|
||||
local_extension=OgLive.tmp_extension)
|
||||
if download_err:
|
||||
OgLive._delete_tmp_live_files('')
|
||||
return 1
|
||||
|
||||
remote_json = os.path.join(local_live_dir, 'ogrelive.json')
|
||||
remote_json_tmp = remote_json + OgLive.tmp_extension
|
||||
try:
|
||||
shutil.move(remote_json_tmp, remote_json)
|
||||
except OSError as e:
|
||||
print(f'ERROR: cannot move {remote_json_tmp} into {remote_json}: {e}')
|
||||
return 1
|
||||
|
||||
try:
|
||||
with open(remote_json, 'r') as json_file:
|
||||
remote_data = json_file.read()
|
||||
except json.JSONDecodeError:
|
||||
print(f'ERROR: Failed parse malformed JSON file {remote_json}')
|
||||
return 1
|
||||
except OSError as e:
|
||||
print(f'ERROR: cannot open {remote_json}: {e}')
|
||||
return 1
|
||||
|
||||
print_json(remote_data)
|
||||
return 0
|
||||
|
||||
res = rest.get('/oglive/list')
|
||||
if not res:
|
||||
return 1
|
||||
|
||||
live_data = json.loads(res.text)
|
||||
OgLive._parse_timestamps(live_data)
|
||||
print_json(live_data)
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def _is_same_checksum(file_path, checksum_path):
|
||||
local_checksum = compute_md5(file_path)
|
||||
try:
|
||||
with open(checksum_path, 'r') as f:
|
||||
remote_checksum = f.read().strip()
|
||||
except (FileNotFoundError, PermissionError, OSError) as e:
|
||||
print(f'ERROR: Cannot read checksum file for {live_file}: {e}')
|
||||
return False
|
||||
return local_checksum == remote_checksum
|
||||
|
||||
@staticmethod
|
||||
def install_live(rest, args):
|
||||
parser = argparse.ArgumentParser(prog='ogcli install live')
|
||||
parser.add_argument('--name',
|
||||
nargs='?',
|
||||
required=True,
|
||||
help='Name of the center')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
live_name = parsed_args.name
|
||||
|
||||
local_live_dir = OgLive._get_local_live_dir()
|
||||
|
||||
if not local_live_dir:
|
||||
return 1
|
||||
|
||||
if not OgLive._is_live_in_server(live_name):
|
||||
print(f'{live_name} is not available on the server, it cannot be installed')
|
||||
return 1
|
||||
|
||||
local_dir = os.path.join(local_live_dir, live_name)
|
||||
if os.path.exists(local_dir):
|
||||
print(f'{live_name} already exists, checking for updates...')
|
||||
try:
|
||||
os.makedirs(local_dir, exist_ok=True)
|
||||
except OSError as e:
|
||||
print(f'ERROR: Failed to create directory {local_dir}: {e}')
|
||||
return 1
|
||||
|
||||
for live_file in OgLive.live_files:
|
||||
download_err = OgLive._download_from_server(os.path.join(live_name,
|
||||
live_file + '.full.sum'),
|
||||
local_extension=OgLive.tmp_extension)
|
||||
if download_err:
|
||||
OgLive._delete_tmp_live_files(live_name)
|
||||
return download_err
|
||||
|
||||
file_path = os.path.join(local_dir, live_file)
|
||||
file_path_tmp = file_path + OgLive.tmp_extension
|
||||
checksum_path_tmp = file_path + '.full.sum' + OgLive.tmp_extension
|
||||
|
||||
is_first_install = not os.path.exists(file_path)
|
||||
if is_first_install:
|
||||
print(f'Downloading {live_file}...')
|
||||
else:
|
||||
requires_update = not OgLive._is_same_checksum(file_path, checksum_path_tmp)
|
||||
if not requires_update:
|
||||
print(f'{live_file} is up-to-date, skipping')
|
||||
continue
|
||||
|
||||
print(f'Updating {live_file}...')
|
||||
download_err = OgLive._download_from_server(os.path.join(live_name,
|
||||
live_file),
|
||||
local_extension=OgLive.tmp_extension)
|
||||
if download_err:
|
||||
OgLive._delete_tmp_live_files(live_name)
|
||||
return download_err
|
||||
|
||||
if not OgLive._is_same_checksum(file_path_tmp, checksum_path_tmp):
|
||||
print(f'ERROR: Checksum mismatch for {live_file}')
|
||||
OgLive._delete_tmp_live_files(live_name)
|
||||
return 1
|
||||
|
||||
print(f'Checksum is OK for {live_file}')
|
||||
|
||||
for file_name in os.listdir(local_dir):
|
||||
if not file_name.endswith(OgLive.tmp_extension):
|
||||
continue
|
||||
file_path_tmp = os.path.join(local_dir, file_name)
|
||||
file_path = file_path_tmp[:-len(OgLive.tmp_extension)]
|
||||
try:
|
||||
shutil.move(file_path_tmp, file_path)
|
||||
except OSError as e:
|
||||
print(f'ERROR: cannot move {src_file} into {target_file}: {e}')
|
||||
OgLive._delete_tmp_live_files(live_name)
|
||||
return 1
|
||||
|
||||
payload = {'name': live_name}
|
||||
res = rest.post('/oglive/add', payload=payload)
|
||||
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def delete_live(rest, args):
|
||||
parser = argparse.ArgumentParser(prog='ogcli delete live')
|
||||
parser.add_argument('--name',
|
||||
nargs='?',
|
||||
required=True,
|
||||
help='Name of the center')
|
||||
parsed_args = parser.parse_args(args)
|
||||
live_name = parsed_args.name
|
||||
|
||||
payload = {'name': live_name}
|
||||
res = rest.post('/oglive/delete', payload=payload)
|
||||
|
||||
if not res:
|
||||
return 1
|
||||
|
||||
local_live_dir = OgLive._get_local_live_dir()
|
||||
|
||||
if not local_live_dir:
|
||||
return 1
|
||||
|
||||
local_dir = os.path.join(local_live_dir, live_name)
|
||||
|
||||
if os.path.exists(local_dir):
|
||||
try:
|
||||
shutil.rmtree(local_dir)
|
||||
except OSError as e:
|
||||
print(f'Error deleting directory {local_dir}: {e}')
|
||||
return 1
|
||||
else:
|
||||
print(f'Error: no directory found for {live_name}')
|
||||
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def set_live(rest, args):
|
||||
parser = argparse.ArgumentParser(prog='ogcli set live')
|
||||
parser.add_argument('--default',
|
||||
action='store_true',
|
||||
required=True,
|
||||
help='set the default live image')
|
||||
parser.add_argument('--name',
|
||||
nargs='?',
|
||||
required=True,
|
||||
help='Name of the live')
|
||||
parsed_args = parser.parse_args(args)
|
||||
live_name = parsed_args.name
|
||||
|
||||
payload = {'name': live_name}
|
||||
res = rest.post('/oglive/default', payload=payload)
|
||||
|
||||
if not res:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
|
|
@ -14,8 +14,11 @@ class OgModes():
|
|||
|
||||
@staticmethod
|
||||
def list_available_modes(rest):
|
||||
r = rest.get('/mode')
|
||||
print_json(r.text)
|
||||
res = rest.get('/mode')
|
||||
if not res:
|
||||
return 1
|
||||
print_json(res.text)
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def set_modes(rest, args):
|
||||
|
@ -44,8 +47,8 @@ class OgModes():
|
|||
help='Boot mode to be set')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
r = rest.get('/scopes')
|
||||
scopes = r.json()
|
||||
res = rest.get('/scopes')
|
||||
scopes = res.json()
|
||||
ips = set()
|
||||
|
||||
for center in parsed_args.center_id:
|
||||
|
@ -59,7 +62,10 @@ class OgModes():
|
|||
|
||||
if not ips:
|
||||
print("Missing --client-ip, or --room-id/--center-id. No clients provided.")
|
||||
return None
|
||||
return 1
|
||||
|
||||
payload = {'clients': list(ips), 'mode': parsed_args.mode[0]}
|
||||
r = rest.post('/mode', payload=payload)
|
||||
res = rest.post('/mode', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -36,8 +36,8 @@ class OgPoweroff():
|
|||
help='Specific client IP')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
r = rest.get('/scopes')
|
||||
scopes = r.json()
|
||||
res = rest.get('/scopes')
|
||||
scopes = res.json()
|
||||
ips = set()
|
||||
|
||||
for center in parsed_args.center_id:
|
||||
|
@ -51,7 +51,10 @@ class OgPoweroff():
|
|||
|
||||
if not ips:
|
||||
print("Missing --client-ip, or --room-id/--center-id. No clients provided.")
|
||||
return None
|
||||
return 1
|
||||
|
||||
payload = {'clients': list(ips)}
|
||||
r = rest.post('/poweroff', payload=payload)
|
||||
res = rest.post('/poweroff', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -36,8 +36,8 @@ class OgReboot():
|
|||
help='Specific client IP')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
r = rest.get('/scopes')
|
||||
scopes = r.json()
|
||||
res = rest.get('/scopes')
|
||||
scopes = res.json()
|
||||
ips = set()
|
||||
|
||||
for center in parsed_args.center_id:
|
||||
|
@ -51,7 +51,10 @@ class OgReboot():
|
|||
|
||||
if not ips:
|
||||
print("Missing --client-ip, or --room-id/--center-id. No clients provided.")
|
||||
return None
|
||||
return 1
|
||||
|
||||
payload = {'clients': list(ips)}
|
||||
r = rest.post('/reboot', payload=payload)
|
||||
res = rest.post('/reboot', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -13,37 +13,89 @@ class OgRepo():
|
|||
|
||||
@staticmethod
|
||||
def list_repos(rest):
|
||||
r = rest.get('/repositories')
|
||||
print_json(r.text)
|
||||
res = rest.get('/repositories')
|
||||
if not res:
|
||||
return 1
|
||||
print_json(res.text)
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def _add_repo(rest, parsed_args):
|
||||
payload = {
|
||||
'addr': parsed_args.ip,
|
||||
'name': parsed_args.name,
|
||||
}
|
||||
res = rest.post('/repository/add', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def _add_repo_ip(rest, parsed_args):
|
||||
for ip in parsed_args.ip:
|
||||
if not check_address(ip):
|
||||
print(f'Invalid IP address: {ip}')
|
||||
return 1
|
||||
|
||||
res = rest.get('/repositories')
|
||||
target_repo = None
|
||||
for repo in res.json()['repositories']:
|
||||
if repo['id'] == parsed_args.id:
|
||||
target_repo = repo
|
||||
break
|
||||
|
||||
if not target_repo:
|
||||
print('Invalid repository id specified')
|
||||
return 1
|
||||
|
||||
for ip in parsed_args.ip:
|
||||
if ip in target_repo['addr']:
|
||||
print(f'The repository already contains the address {ip}')
|
||||
return 1
|
||||
|
||||
payload = {
|
||||
'id': parsed_args.id,
|
||||
'addr': target_repo['addr'] + parsed_args.ip,
|
||||
'name': target_repo['name'],
|
||||
}
|
||||
res = rest.post('/repository/update', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def add_repo(rest, args):
|
||||
parser = argparse.ArgumentParser(prog='ogcli add repo')
|
||||
parser.add_argument('--address',
|
||||
|
||||
# Mutually exclusive group for adding new repo or adding address to existing repo
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument('--name',
|
||||
nargs='?',
|
||||
help='name of the repository')
|
||||
group.add_argument('--id',
|
||||
type=int,
|
||||
help='repo id (list repos using "ogcli list repos")')
|
||||
parser.add_argument('--ip',
|
||||
nargs='+',
|
||||
required=True,
|
||||
help='IP list separated by spaces')
|
||||
parser.add_argument('--name',
|
||||
nargs='?',
|
||||
required=True,
|
||||
help='name of the repository')
|
||||
parsed_args = parser.parse_args(args)
|
||||
payload = {
|
||||
'addr': parsed_args.address,
|
||||
'name': parsed_args.name,
|
||||
}
|
||||
|
||||
rest.post('/repository/add', payload=payload)
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
if parsed_args.name:
|
||||
OgRepo._add_repo(rest, parsed_args)
|
||||
elif parsed_args.id:
|
||||
OgRepo._add_repo_ip(rest, parsed_args)
|
||||
|
||||
@staticmethod
|
||||
def update_repo(rest, args):
|
||||
parser = argparse.ArgumentParser(prog='ogcli add repo')
|
||||
parser = argparse.ArgumentParser(prog='ogcli update repo')
|
||||
parser.add_argument('--id',
|
||||
type=int,
|
||||
nargs='?',
|
||||
required=True,
|
||||
help='repo id (list repos using "ogcli list repos")')
|
||||
parser.add_argument('--address',
|
||||
parser.add_argument('--ip',
|
||||
nargs='+',
|
||||
required=True,
|
||||
help='IP list separated by spaces')
|
||||
|
@ -54,23 +106,79 @@ class OgRepo():
|
|||
parsed_args = parser.parse_args(args)
|
||||
payload = {
|
||||
'id': parsed_args.id,
|
||||
'addr': parsed_args.address,
|
||||
'addr': parsed_args.ip,
|
||||
'name': parsed_args.name,
|
||||
}
|
||||
|
||||
rest.post('/repository/update', payload=payload)
|
||||
res = rest.post('/repository/update', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def _delete_repo_ip(rest, parsed_args):
|
||||
for ip in parsed_args.ip:
|
||||
if not check_address(ip):
|
||||
print(f'Invalid IP address: {ip}')
|
||||
return 1
|
||||
|
||||
res = rest.get('/repositories')
|
||||
target_repo = None
|
||||
for repo in res.json()['repositories']:
|
||||
if repo['id'] == parsed_args.id:
|
||||
target_repo = repo
|
||||
break
|
||||
|
||||
if not target_repo:
|
||||
print('Invalid repository id specified')
|
||||
return 1
|
||||
|
||||
for ip in parsed_args.ip:
|
||||
if ip not in target_repo['addr']:
|
||||
print(f'Invalid address {ip}: The repository has the following IPs: {target_repo["addr"]}')
|
||||
return 1
|
||||
|
||||
target_repo['addr'].remove(ip)
|
||||
|
||||
payload = {
|
||||
'id': parsed_args.id,
|
||||
'addr': target_repo['addr'],
|
||||
'name': target_repo['name'],
|
||||
}
|
||||
res = rest.post('/repository/update', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def _delete_repo(rest, parsed_args):
|
||||
payload = {'id': parsed_args.id}
|
||||
res = rest.post('/repository/delete', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def delete_repo(rest, args):
|
||||
parser = argparse.ArgumentParser(prog='ogcli delete repo')
|
||||
|
||||
parser.add_argument('--id',
|
||||
type=int,
|
||||
nargs='?',
|
||||
required=True,
|
||||
help='repo id (list repos using "ogcli list repos")')
|
||||
|
||||
# --ip is optional; if provided, the IP will be removed, otherwise,
|
||||
# the repo will be deleted
|
||||
parser.add_argument('--ip',
|
||||
nargs='+',
|
||||
help='IP address to remove from the repository')
|
||||
|
||||
parsed_args = parser.parse_args(args)
|
||||
payload = {'id': parsed_args.id}
|
||||
rest.post('/repository/delete', payload=payload)
|
||||
|
||||
if parsed_args.ip:
|
||||
OgRepo._delete_repo_ip(rest, parsed_args)
|
||||
else:
|
||||
OgRepo._delete_repo(rest, parsed_args)
|
||||
|
||||
@staticmethod
|
||||
def set_repo(rest, args):
|
||||
|
@ -94,8 +202,8 @@ class OgRepo():
|
|||
help='Any valid client ip address')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
r = rest.get('/scopes')
|
||||
scopes = r.json()
|
||||
res = rest.get('/scopes')
|
||||
scopes = res.json()
|
||||
ips = set()
|
||||
|
||||
for room in parsed_args.room_id:
|
||||
|
@ -105,7 +213,10 @@ class OgRepo():
|
|||
ips.add(l)
|
||||
if not ips:
|
||||
print('No valid clients specified.')
|
||||
return
|
||||
return 1
|
||||
|
||||
payload = {'id': parsed_args.id, 'clients': list(ips)}
|
||||
rest.post('/client/repo', payload=payload)
|
||||
res = rest.post('/client/repo', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -65,7 +65,7 @@ class OgRoom():
|
|||
err = True
|
||||
if err:
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
payload = {
|
||||
'name': parsed_args.name,
|
||||
|
@ -82,7 +82,10 @@ class OgRoom():
|
|||
payload['folder_id'] = parsed_args.folder
|
||||
if parsed_args.desc:
|
||||
payload['location'] = parsed_args.desc
|
||||
rest.post('/room/add', payload=payload)
|
||||
res = rest.post('/room/add', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def update_room(rest, args):
|
||||
|
@ -115,7 +118,7 @@ class OgRoom():
|
|||
err = True
|
||||
if err:
|
||||
parser.print_help(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return 1
|
||||
|
||||
payload = {
|
||||
'id': parsed_args.id,
|
||||
|
@ -124,7 +127,10 @@ class OgRoom():
|
|||
'gateway': parsed_args.gateway,
|
||||
}
|
||||
|
||||
rest.post('/room/update', payload=payload)
|
||||
res = rest.post('/room/update', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def delete_room(rest, args):
|
||||
|
@ -136,5 +142,8 @@ class OgRoom():
|
|||
help='room id in database')
|
||||
parsed_args = parser.parse_args(args)
|
||||
payload = {'id': parsed_args.id}
|
||||
rest.post('/room/delete', payload=payload)
|
||||
res = rest.post('/room/delete', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
|
|
@ -10,27 +10,30 @@ import argparse
|
|||
from cli.utils import print_json
|
||||
import json
|
||||
|
||||
def _find_client_path(json_data, client_ip, res):
|
||||
def _find_client_path(json_data, client_ip, client_name, res):
|
||||
if json_data['type'] == 'computer':
|
||||
if json_data['ip'] == client_ip:
|
||||
res.append(f'{json_data["type"]}: {client_ip}')
|
||||
return True
|
||||
elif json_data['name'] == client_name:
|
||||
res.append(f'{json_data["type"]}: {client_name}')
|
||||
return True
|
||||
return False
|
||||
|
||||
children = json_data['scope']
|
||||
for child in children:
|
||||
found = _find_client_path(child, client_ip, res)
|
||||
found = _find_client_path(child, client_ip, client_name, res)
|
||||
if found:
|
||||
res.append(f'{json_data["type"]}: {json_data["name"]}')
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _get_client_path(json_data, client_ip):
|
||||
def _get_client_path(json_data, client_ip, client_name):
|
||||
res = []
|
||||
children = json_data['scope']
|
||||
for child in children:
|
||||
_find_client_path(child, client_ip, res)
|
||||
_find_client_path(child, client_ip, client_name, res)
|
||||
res.reverse()
|
||||
return res
|
||||
|
||||
|
@ -39,27 +42,36 @@ class OgScope():
|
|||
@staticmethod
|
||||
def list_scopes(rest, args):
|
||||
parser = argparse.ArgumentParser(prog='ogcli list scopes')
|
||||
parser.add_argument('--client-ip',
|
||||
action='append',
|
||||
default=[],
|
||||
required=False,
|
||||
help='Client(s) IP')
|
||||
group = parser.add_mutually_exclusive_group(required=False)
|
||||
group.add_argument('--client-ip',
|
||||
action='append',
|
||||
default=[],
|
||||
help='Client(s) IP')
|
||||
group.add_argument('--name',
|
||||
nargs='?',
|
||||
help='Name of the client')
|
||||
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
ips = set()
|
||||
for ip in parsed_args.client_ip:
|
||||
ips.add(ip)
|
||||
|
||||
r = rest.get('/scopes')
|
||||
res = rest.get('/scopes')
|
||||
json_data = json.loads(res.text)
|
||||
|
||||
if not ips:
|
||||
print_json(r.text)
|
||||
return None
|
||||
|
||||
json_data = json.loads(r.text)
|
||||
for idx, client_ip in enumerate(ips):
|
||||
if idx != 0:
|
||||
print('\n')
|
||||
path = _get_client_path(json_data, client_ip)
|
||||
if parsed_args.name:
|
||||
path = _get_client_path(json_data, None, parsed_args.name)
|
||||
for i, item in enumerate(path):
|
||||
print(' ' * i + item)
|
||||
elif parsed_args.client_ip:
|
||||
for idx, client_ip in enumerate(parsed_args.client_ip):
|
||||
if idx != 0:
|
||||
print('\n')
|
||||
path = _get_client_path(json_data, client_ip, None)
|
||||
for i, item in enumerate(path):
|
||||
print(' ' * i + item)
|
||||
else:
|
||||
print_json(res.text)
|
||||
return 0
|
||||
|
||||
|
|
|
@ -13,19 +13,30 @@ class OgServer():
|
|||
|
||||
@staticmethod
|
||||
def list_servers(rest):
|
||||
r = rest.get('/server')
|
||||
print_json(r.text)
|
||||
res = rest.get('/server')
|
||||
if not res:
|
||||
return 1
|
||||
print_json(res.text)
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def add_server(rest, args):
|
||||
parser = argparse.ArgumentParser(prog='ogcli add server')
|
||||
parser.add_argument('--address',
|
||||
parser.add_argument('--ip',
|
||||
nargs='?',
|
||||
required=True,
|
||||
help='valid ogserver ip address')
|
||||
parsed_args = parser.parse_args(args)
|
||||
payload = {'address': parsed_args.address}
|
||||
rest.post('/server', payload=payload)
|
||||
|
||||
if not check_address(parsed_args.ip):
|
||||
print(f'Invalid IP address: {parsed_args.ip}')
|
||||
return 1
|
||||
|
||||
payload = {'address': parsed_args.ip}
|
||||
res = rest.post('/server', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def delete_server(rest, args):
|
||||
|
@ -37,4 +48,7 @@ class OgServer():
|
|||
help='server id in the database')
|
||||
parsed_args = parser.parse_args(args)
|
||||
payload = {'id': parsed_args.id}
|
||||
rest.delete('/server', payload=payload)
|
||||
res = rest.delete('/server', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -44,8 +44,8 @@ class OgSession():
|
|||
help='Specific client IP')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
r = rest.get('/scopes')
|
||||
scopes = r.json()
|
||||
res = rest.get('/scopes')
|
||||
scopes = res.json()
|
||||
ips = set()
|
||||
|
||||
for center in parsed_args.center_id:
|
||||
|
@ -59,11 +59,14 @@ class OgSession():
|
|||
|
||||
if not ips:
|
||||
print("Missing --client-ip, or --room-id/--center-id. No clients provided.")
|
||||
return None
|
||||
return 1
|
||||
|
||||
payload = {
|
||||
'clients': list(ips),
|
||||
'disk': parsed_args.disk,
|
||||
'partition': parsed_args.part,
|
||||
}
|
||||
r = rest.post('/session', payload=payload)
|
||||
res = rest.post('/session', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
|
@ -53,8 +53,8 @@ class OgWol():
|
|||
help='Specific client IP')
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
r = rest.get('/scopes')
|
||||
scopes = r.json()
|
||||
res = rest.get('/scopes')
|
||||
scopes = res.json()
|
||||
ips = set()
|
||||
|
||||
for center in parsed_args.center_id:
|
||||
|
@ -68,7 +68,10 @@ class OgWol():
|
|||
|
||||
if not ips:
|
||||
print("Missing --client-ip, or --room-id/--center-id. No clients provided.")
|
||||
return None
|
||||
return 1
|
||||
|
||||
payload = {'type': parsed_args.type, 'clients': list(ips)}
|
||||
r = rest.post('/wol', payload=payload)
|
||||
res = rest.post('/wol', payload=payload)
|
||||
if not res:
|
||||
return 1
|
||||
return 0
|
||||
|
|
27
cli/utils.py
27
cli/utils.py
|
@ -8,7 +8,9 @@
|
|||
import unicodedata
|
||||
import json
|
||||
import ipaddress
|
||||
import hashlib
|
||||
import re
|
||||
import os
|
||||
|
||||
def scope_lookup(scope_id, scope_type, d):
|
||||
if scope_id == d.get('id') and scope_type == d.get('type'):
|
||||
|
@ -47,8 +49,11 @@ def reorder_json_tree(payload):
|
|||
payload[k] = val
|
||||
reorder_json_tree(val)
|
||||
|
||||
def print_json(text):
|
||||
payload = json.loads(text)
|
||||
def print_json(data):
|
||||
if isinstance(data, (dict, list)):
|
||||
payload = data
|
||||
else:
|
||||
payload = json.loads(data)
|
||||
reorder_json_tree(payload)
|
||||
print(json.dumps(payload, indent=2, ensure_ascii=False))
|
||||
|
||||
|
@ -69,3 +74,21 @@ def check_mac_address(addr):
|
|||
def remove_accents(text):
|
||||
normalized_text = unicodedata.normalize('NFD', text)
|
||||
return ''.join(c for c in normalized_text if unicodedata.category(c) != 'Mn')
|
||||
|
||||
def compute_md5(path, bs=2**20):
|
||||
if not os.path.exists(path):
|
||||
print(f"Failed to calculate checksum, image file {path} does not exist")
|
||||
return None
|
||||
|
||||
m = hashlib.md5()
|
||||
try:
|
||||
with open(path, 'rb') as f:
|
||||
while True:
|
||||
buf = f.read(bs)
|
||||
if not buf:
|
||||
break
|
||||
m.update(buf)
|
||||
except Exception as e:
|
||||
print(f'Failed to calculate checksum for {path}: {e}')
|
||||
return None
|
||||
return m.hexdigest()
|
||||
|
|
21
ogcli
21
ogcli
|
@ -15,8 +15,6 @@ import argparse
|
|||
import json
|
||||
import sys
|
||||
|
||||
OG_CLI_CFG_PATH = "/opt/opengnsys/etc/ogcli.json"
|
||||
|
||||
def sigint_handler(signum, frame):
|
||||
print("User has pressed ctrl-C, interrupting...")
|
||||
sys.exit(1)
|
||||
|
@ -25,22 +23,8 @@ class CLI():
|
|||
def __init__(self):
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
signal.signal(signal.SIGINT, sigint_handler)
|
||||
try:
|
||||
with open(OG_CLI_CFG_PATH, 'r') as json_file:
|
||||
self.cfg = json.load(json_file)
|
||||
except json.JSONDecodeError:
|
||||
sys.exit(f'ERROR: Failed parse malformed JSON file '
|
||||
f'{OG_CLI_CFG_PATH}')
|
||||
except:
|
||||
sys.exit(f'ERROR: cannot open {OG_CLI_CFG_PATH}')
|
||||
|
||||
required_cfg_params = {'api_token', 'ip', 'port'}
|
||||
difference_cfg_params = required_cfg_params - self.cfg.keys()
|
||||
if len(difference_cfg_params) > 0:
|
||||
sys.exit(f'Missing {difference_cfg_params} key in '
|
||||
f'json config file')
|
||||
|
||||
self.ogcli = OgCLI(self.cfg)
|
||||
self.ogcli = OgCLI()
|
||||
|
||||
parser = argparse.ArgumentParser(prog='ogcli')
|
||||
parser.add_argument('command', help='Subcommand to run', nargs='?',
|
||||
|
@ -59,7 +43,8 @@ class CLI():
|
|||
sys.exit(1)
|
||||
|
||||
# Call the command with the same name.
|
||||
getattr(self.ogcli, args.command)(sys.argv[2:])
|
||||
res = getattr(self.ogcli, args.command)(sys.argv[2:])
|
||||
sys.exit(res)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Loading…
Reference in New Issue