fuel-web/nailgun/fuel-cli/fuel
Alexandr Notchenko 0c88b5e7fe added snapshot action
fuel snapshot simplified

fixed progress bars

api version now explicit
Change-Id: I62a7202fe6e7914895604be1b3fb38de5a7a3141
2013-10-11 14:52:24 +04:00

1317 lines
39 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright 2013 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import argparse
import curses
from functools import partial
from itertools import chain
import json
import math
import os
import shutil
import sys
from time import sleep
import urllib2
ROOT = "http://127.0.0.1:8000"
API_ROOT = ROOT + "/api/v1/"
DEBUG = False
JSON = False
passive_arguments = ["rel", "env", "action", "debug", "json"]
class SetAction(argparse.Action):
"""Custom argparse.Action subclass to store distinct values
:returns: Set of arguments
"""
def __call__(self, _parser, namespace, values, option_string=None):
try:
getattr(namespace, self.dest).update(values)
except AttributeError:
setattr(namespace, self.dest, set(values))
def recur_get(multi_level_dict, key_chain):
"""Method accesses some field in nested dictionaries
:returns: value for last key in key_chain in last dictionary
"""
if not isinstance(multi_level_dict[key_chain[0]], dict):
return multi_level_dict[key_chain[0]]
else:
return recur_get(multi_level_dict[key_chain[0]], key_chain[1:])
def format_table(data, acceptable_keys=None, subdict_keys=None):
"""Format list of dicts to ascii table
:acceptable_keys list(str): list of keys for which to create table
also specifies their order
:subdict_keys list(tuple(str)): list of key chains (tuples of key strings)
which are applied to dictionaries
to extract values
"""
if JSON:
print(json.dumps(data, indent=4))
exit(0)
if subdict_keys:
for key_chain in subdict_keys:
for data_dict in data:
data_dict[key_chain[0]] = recur_get(data_dict, key_chain)
if acceptable_keys:
rows = [tuple([value[key] for key in acceptable_keys])
for value in data]
header = tuple(acceptable_keys)
else:
rows = [tuple(x.values()) for x in data]
header = tuple(data[0].keys())
number_of_columns = len(header)
column_widths = dict(
zip(
range(number_of_columns),
(len(str(x)) for x in header)
)
)
for row in rows:
column_widths.update(
(index, max(column_widths[index], len(str(element))))
for index, element in enumerate(row)
)
row_template = ' | '.join(
'%%-%ss' % column_widths[i] for i in range(number_of_columns)
)
return '\n'.join(
(row_template % header,
'-|-'.join(column_widths[column_index]*'-'
for column_index in range(number_of_columns)),
'\n'.join(row_template % x for x in rows))
)
def json_api_delete_request(api):
"""Make DELETE request to specific API with some data
"""
print_debug(
"DELETE {0}".format(API_ROOT + api)
)
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(API_ROOT + api)
request.add_header('Content-Type', ' application/json')
request.get_method = lambda: 'DELETE'
try:
return json.loads(
opener.open(request).read()
)
except ValueError:
return {}
except urllib2.HTTPError as e:
print_error(str(e) + "\n")
def json_api_put_request(api, data):
"""Make PUT request to specific API with some data
"""
data_json = json.dumps(data)
print_debug(
"PUT {0} data={1}"
.format(API_ROOT + api, data_json)
)
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(API_ROOT + api, data=data_json)
request.add_header('Content-Type', ' application/json')
request.get_method = lambda: 'PUT'
try:
return json.loads(
opener.open(request).read()
)
except urllib2.HTTPError as e:
print_error(str(e) + "\n")
def json_api_get_request(api):
"""Make GET request to specific API
"""
print_debug(
"GET {0}"
.format(API_ROOT + api)
)
try:
request = urllib2.urlopen(API_ROOT + api)
return json.loads(
request.read()
)
except urllib2.HTTPError as e:
print_error(str(e) + "\n")
def json_api_post_request(api, data):
"""Make POST request to specific API with some data
"""
data_json = json.dumps(data)
print_debug(
"POST {0} data={1}"
.format(API_ROOT + api, data_json)
)
try:
return json.loads(
urllib2.urlopen(
url=API_ROOT + api,
data=data_json
).read()
)
except urllib2.HTTPError as e:
print_error(str(e) + "\n")
def has_arguments(params):
current_arguments = [v for k, v in params.__dict__.iteritems()
if k not in passive_arguments]
return any(current_arguments)
def print_error(message):
sys.stderr.write(message + "\n")
exit(1)
def print_debug(message):
if DEBUG:
print(message)
def check_for_attributes(params, attributes):
not_paseed_checks = [attribute for attribute in attributes
if not getattr(params, attribute)]
if len(not_paseed_checks):
print_error(
"{0} required!".format(
", ".join(
map(
lambda attr: "--" + attr,
not_paseed_checks
)
)
)
)
def check_for_one_attribute(params, attributes):
if not any(map(
lambda attr: getattr(params, attr),
attributes
)):
print_error(
"At least one of {0} is required!".format(
", ".join(
map(
lambda attr: "--" + attr,
attributes)
)
)
)
def release(params):
"""List and modify currently available releases
"""
acceptable_keys = ["id", "name", "state", "operating_system", "version"]
if not has_arguments(params) or params.list:
if params.rel:
data = [json_api_get_request(
"releases/{0}/"
.format(params.rel)
)]
else:
data = json_api_get_request("releases/")
if params.json:
print(json.dumps(data, indent=4))
else:
print(format_table(data, acceptable_keys=acceptable_keys))
elif params.config:
check_for_attributes(params, ["rel", "username", "password"])
data = {
"release_id": params.rel,
"license_type": "rhsm",
"username": params.username,
"password": params.password,
"satellite": "",
"activation_key": ""
}
release_response = json_api_post_request(
"redhat/setup/",
data
)
if params.json:
print(json.dumps(release_response, indent=4))
else:
print(
"Credentials for release with id={0}"
" were successfully modified!"
.format(params.rel)
)
def environment(params):
"""Create, list and modify currently existing environments(clusters)
"""
if not has_arguments(params) or params.list:
acceptable_keys = ["id", "status", "name", "mode",
"release", "changes"]
data = json_api_get_request("clusters/")
if params.env:
data = filter(
lambda x: x[u"id"] == int(params.env),
data
)
print(format_table(data, acceptable_keys=acceptable_keys,
subdict_keys=[("release", u"id")]))
elif params.create:
check_for_attributes(params, ["ename", "rel"])
data = {
"nodes": [],
"tasks": [],
"name": params.ename,
"release": int(params.rel)
}
cluster_response = json_api_post_request("clusters/", data)
cluster_id = cluster_response[u"id"]
if params.mode:
data = {"mode": params.mode}
cluster_response = json_api_put_request(
"clusters/{0}/".format(cluster_id),
data
)
if params.json:
print(json.dumps(cluster_response, indent=4))
else:
print(
"Environment with id={id} in mode={mode}"
" was successfully created!"
.format(**cluster_response)
)
elif params.set:
check_for_attributes(params, ["env"])
if params.mode or params.ename:
data = {}
if params.mode:
data["mode"] = params.mode
if params.ename:
data["name"] = params.ename
put_response = json_api_put_request(
"clusters/{0}/".format(params.env),
data
)
if params.json:
print(json.dumps(put_response, indent=4))
else:
if params.ename:
print(
"Environment with id={0} was "
"successfully renamed to {1}"
.format(
params.env,
params.ename
)
)
if params.mode:
print(
"Mode of environment with id={0} was "
"successfully set to {1}."
.format(
params.env,
params.mode
)
)
elif params.delete:
check_for_attributes(params, ["env"])
delete_response = json_api_delete_request(
"clusters/{0}/".format(params.env)
)
if params.json:
print(json.dumps(delete_response, indent=4))
else:
print(
"Environment with id={0} was "
"successfully deleted."
.format(params.env)
)
def node(params):
"""List and assign available nodes to environments
"""
if not has_arguments(params) or params.list:
acceptable_keys = ["id", "status", "name", "cluster",
"mac", "roles", "pending_roles", "online"]
data = json_api_get_request("nodes/")
if params.env:
data = filter(
lambda x: x[u"cluster"] == int(params.env),
data
)
print(
format_table(data, acceptable_keys=acceptable_keys)
)
elif params.set:
check_for_attributes(params, ["node", "role", "env"])
node_ids = list(chain(*params.node))
roles = map(str.lower, params.role)
data = map(
lambda _node_id: {
"id": _node_id,
"cluster_id": params.env,
"pending_roles": roles,
"pending_addition": True,
"pending_deletion": False
},
node_ids
)
put_response = json_api_put_request("nodes/", data)
if params.json:
print(json.dumps(put_response, indent=4))
else:
print(
"Nodes {0} with roles {1} "
"were successfully added to environment {2}"
.format(node_ids, roles, params.env)
)
elif params.delete:
check_for_one_attribute(params, ["env", "node"])
nodes_clusters = dict((_node["id"], _node["cluster"])
for _node in json_api_get_request("nodes/"))
if not params.node and params.env:
node_ids = [k for k, v in nodes_clusters.iteritems()
if v == int(params.env)]
else:
node_ids = list(chain(*params.node))
data = map(
lambda _node_id: {
"id": _node_id,
"cluster_id": None,
"pending_roles": [],
"pending_addition": False,
"pending_deletion": True
},
node_ids
)
put_response = json_api_put_request("nodes/", data)
if params.json:
print(json.dumps(put_response, indent=4))
else:
print(
"Nodes with ids {0} were removed from environment with id {1}."
.format(
node_ids,
nodes_clusters[node_ids[0]]
)
)
elif params.network or params.disk:
check_for_one_attribute(params, ["default", "download", "upload"])
check_for_attributes(params, ["node"])
node_ids = list(chain(*params.node))
for node_id in node_ids:
if params.network:
get_node_attribute(
params,
node_id,
"interfaces",
[("interfaces", "default_assignment")]
)
elif params.disk:
get_node_attribute(
params,
node_id,
"disks",
[("disks", "defaults")]
)
def get_node_attribute(params, node_id, upload_attribute, attributes):
default_url_template = "nodes/{0}/{1}/{2}".format(node_id, "{0}", "{1}")
dir_path = os.path.join(
os.path.abspath(params.dir or os.path.curdir),
"node_{0}".format(node_id)
)
if params.upload:
dir_path = folder_or_one_up(dir_path)
if not os.path.exists(dir_path):
print_error(
"Folder {0} doesn't contain node folder '{1}'"
.format(dir_path, "node_{0}".format(node_id))
)
upload_node_attribute(
default_url_template.format(upload_attribute, ""),
node_id,
dir_path,
upload_attribute
)
elif params.default or params.download:
if not os.path.exists(dir_path):
os.makedirs(dir_path)
for attribute, default_tail in attributes:
write_node_attribute(
default_url_template.format(
attribute,
default_tail if params.default else ""
),
dir_path,
attribute
)
def upload_node_attribute(url, node_id, node_dir_path, attribute):
data = read_json(
os.path.join(
node_dir_path,
attribute + ".json"
)
)
if attribute == "interfaces":
url = "nodes/interfaces"
data = [{
"interfaces": data,
"id": node_id
}]
put_response = json_api_put_request(
url,
data
)
if JSON:
print(json.dumps(put_response, indent=4))
else:
print("{0} configuration uploaded successfully.".format(attribute))
def write_node_attribute(url, node_dir_path, attribute):
attribute_path = os.path.join(
node_dir_path,
attribute + ".json"
)
get_response = json_api_get_request(url)
if os.path.exists(attribute_path):
os.remove(attribute_path)
write_json(
attribute_path,
get_response
)
if JSON:
print(json.dumps(get_response, indent=4))
else:
print(
"{0} configuration successfully downloaded to {1}."
.format(attribute, attribute_path)
)
def network(params):
"""Show or modify network settings of specific environments
"""
check_for_attributes(params, ["env"])
network_url = "clusters/{0}/network_configuration".format(params.env)
network_data = json_api_get_request(network_url)
network_file_path = os.path.join(
os.path.abspath(params.dir or os.path.curdir),
"network_{0}.json".format(params.env)
)
if params.upload:
put_response = json_api_put_request(
network_url,
read_json(network_file_path)
)
if params.json:
print(json.dumps(put_response, indent=4))
else:
print("Network configuration uploaded successfully.")
elif params.verify:
verify_url = network_url + "/verify"
put_response = json_api_put_request(verify_url, network_data)
if params.json:
print(json.dumps(put_response, indent=4))
else:
print(
"Verification status is '{status}'. message: {message}"
.format(**put_response)
)
else:
if params.download:
write_json(network_file_path, network_data)
print(
"Network configuration for environment with id={0}"
" downloaded to {1} successfully."
.format(params.env, network_file_path)
)
else:
if params.json:
print(json.dumps(network_data, indent=4))
def settings(params):
"""Show or modify environment settings
"""
check_for_attributes(params, ["env"])
settings_url = "clusters/{0}/attributes".format(params.env)
settings_data = json_api_get_request(settings_url)
settings_file_path = os.path.join(
os.path.abspath(params.dir or os.path.curdir),
"settings_{0}.json".format(params.env)
)
if params.upload:
put_response = json_api_put_request(
settings_url,
read_json(settings_file_path)
)
if params.json:
print(json.dumps(put_response, indent=4))
else:
print("Settings configuration uploaded successfully.")
elif params.default:
default_url = settings_url + "/defaults"
get_response = json_api_get_request(default_url)
write_json(settings_file_path, get_response)
if params.json:
print(json.dumps(get_response, indent=4))
else:
print(
"Default settings configuration downloaded successfully."
)
else:
if params.download:
write_json(settings_file_path, settings_data)
print(
"Settings configuration for environment with id={0}"
" downloaded to {1} successfully."
.format(params.env, settings_file_path)
)
else:
if params.json:
print(json.dumps(settings_data, indent=4))
def task(params):
"""Show tasks
"""
if params.delete:
check_for_attributes(params, ["tid"])
task_ids = list(chain(*params.tid))
delete_response = map(
lambda tid: json_api_delete_request("tasks/{0}/".format(tid)),
task_ids
)
if params.json:
print(json.dumps(delete_response, indent=4))
else:
print(
"Tasks with id's {0} deleted successfully!"
.format(','.join(map(str, task_ids)))
)
else:
acceptable_keys = ["id", "status", "name", "cluster", "progress"]
tasks = json_api_get_request("tasks/")
if params.json:
print(json.dumps(tasks, indent=4))
else:
print(
format_table(tasks, acceptable_keys=acceptable_keys)
)
def snapshot(params):
"""Generate and download snapshot.
"""
if params.download:
dump_task = json_api_put_request(
"logs/package",
"{}"
)
task_id = dump_task["id"]
print("Generating dump...")
while dump_task["progress"] < 100:
dump_task = json_api_get_request("tasks/{0}/".format(task_id))
sleep(0.5)
download_snapshot_with_progress_bar(
ROOT + dump_task["message"],
params.dir
)
def download_snapshot_with_progress_bar(url, directory):
directory = directory or os.path.curdir
if not os.path.exists(directory):
print_error("Folder {0} doesn't exist.".format(directory))
file_name = os.path.join(
os.path.abspath(directory),
url.split('/')[-1]
)
download_handle = urllib2.urlopen(url)
with open(file_name, 'wb') as file_handle:
meta = download_handle.info()
file_size = int(meta.getheaders("Content-Length")[0])
print("Downloading: {0} Bytes: {1}".format(url, file_size))
file_size_dl = 0
block_size = 8192
bar = partial(get_bar_for_progress, 80)
while True:
data_buffer = download_handle.read(block_size)
if not data_buffer:
break
file_size_dl += len(data_buffer)
file_handle.write(data_buffer)
progress = int(100 * float(file_size_dl) / file_size)
sys.stdout.write("\r{0}".format(
bar(progress)
))
sys.stdout.flush()
sleep(1/10)
print
def deploy(params):
"""Deploy changes to environments
"""
check_for_attributes(params, ["env"])
put_response = json_api_put_request(
"clusters/{0}/changes".format(params.env),
{}
)
if params.json:
print(json.dumps(put_response, indent=4))
else:
print_deploy_progress(params.env)
def get_bar_for_progress(full_width, progress):
number_of_equal_signs = int(
math.ceil(progress * float(full_width-2)/100)
)
return "[{0}{1}{2}]".format(
"=" * number_of_equal_signs,
">" if number_of_equal_signs < full_width-2 else "",
" " * (full_width-3-number_of_equal_signs)
)
def print_deploy_progress(env):
stdscr = curses.initscr()
scr_width = stdscr.getmaxyx()[1]
curses.noecho()
curses.cbreak()
tasks = json_api_get_request("tasks?cluster_id={0}".format(env))
deploy_task = filter(
lambda x: x.get("name", "") == "deploy",
tasks
)[0]
deploy_task_id = deploy_task["id"]
progress = 0
total_progress_bar = partial(get_bar_for_progress, scr_width - 17)
node_bar = partial(get_bar_for_progress, scr_width - 28)
nodes = []
try:
while progress != 100 or \
any(map(lambda _node: _node["progress"] != 100, nodes)):
task_response = json_api_get_request(
"tasks/{0}/".format(deploy_task_id)
)
progress = task_response["progress"]
nodes = json_api_get_request(
"nodes?cluster_id={0}"
.format(env)
)
nodes.sort(key=lambda _node: _node.get("id"))
stdscr.addstr(0, 0,
"Deploying changes to environment with id={0}"
.format(env)
)
stdscr.addstr(1, 0,
"Deployment: {0} {1:4.0%}".format(
total_progress_bar(progress),
progress / 100.0
))
for index, node in enumerate(nodes):
stdscr.addstr(index + 2, 0,
"Node{0:3} {1:13}: {2} {3:4.0%}".format(
node["id"],
node["status"],
node_bar(node["progress"]),
node["progress"] / 100.0
)
)
sleep(0.25)
stdscr.refresh()
sleep(1)
finally:
curses.echo()
curses.nocbreak()
curses.endwin()
def provisioning(params):
"""Show computed provisioning facts for orchestrator
"""
fact(params, "provisioning")
def deployment(params):
"""Show computed deployment facts for orchestrator
"""
fact(params, "deployment")
def fact(params, info_type):
check_for_attributes(params, ["env"])
dir_name = os.path.join(
os.path.abspath(params.dir or os.path.curdir),
"{0}_{1}".format(info_type, params.env)
)
facts_default_url = "clusters/{0}/orchestrator/{1}/defaults".format(
params.env,
info_type
)
facts_url = "clusters/{0}/orchestrator/{1}/".format(
params.env,
info_type
)
if params.default:
facts = json_api_get_request(facts_default_url)
write_facts_to_dir(facts, dir_name)
elif params.upload:
put_response = json_api_put_request(
facts_url,
read_deployment_info(dir_name)
if info_type == "deployment" else
read_provisioning_info(dir_name)
)
if params.json:
print(json.dumps(put_response, indent=4))
else:
print("{0} facts uploaded successfully.".format(info_type))
elif params.delete:
json_api_delete_request(facts_url)
print("{0} facts deleted successfully.".format(info_type))
else:
facts = json_api_get_request(facts_url)
if not facts:
print(
"Environment with id={0} has no {1} info."
.format(params.env, info_type)
)
return
if params.download:
write_facts_to_dir(facts, dir_name)
else:
if params.json:
print(json.dumps(facts, indent=4))
def write_json(path, data):
with open(path, "w+") as json_file:
json_file.write(json.dumps(data, indent=4))
def read_json(path):
with open(path, "r") as json_file:
return json.loads(json_file.read())
def folder_or_one_up(dir_path):
if not os.path.exists(dir_path):
path_to_folder = dir_path.split(os.sep)
one_folder_up = path_to_folder[:-2] + path_to_folder[-2:-1]
dir_path = os.sep.join(one_folder_up)
return dir_path
def read_provisioning_info(dir_name):
dir_name = folder_or_one_up(dir_name)
if "engine.json" not in os.listdir(dir_name):
print_error(
"engine.json was not found in {0}"
.format(dir_name)
)
try:
node_facts = map(
read_json,
[os.path.join(dir_name, json_file)
for json_file in os.listdir(dir_name)
if json_file.endswith('.json') and json_file != "engine.json"]
)
engine = read_json(os.path.join(dir_name, "engine.json"))
return {
"engine": engine,
"nodes": node_facts
}
except OSError:
print_error(
"Directory {0} doesn't exist."
.format(dir_name)
)
def read_deployment_info(dir_name):
dir_name = folder_or_one_up(dir_name)
try:
return map(
read_json,
[os.path.join(dir_name, json_file)
for json_file in os.listdir(dir_name)
if json_file.endswith('.json')]
)
except OSError:
print_error(
"Directory {0} doesn't exist."
.format(dir_name)
)
def write_facts_to_dir(facts, dir_name):
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
print("old directory {0} was removed".format(dir_name))
os.makedirs(dir_name)
print("directory {0} was created".format(dir_name))
if isinstance(facts, dict):
engine_fd = open(os.path.join(dir_name, "engine.json"), "w+")
engine_fd.write(json.dumps(facts["engine"], indent=4))
print("Created {0}".format(engine_fd.name))
engine_fd.close()
facts = facts["nodes"]
name_template = "{name}.json"
else:
name_template = "{role}_{uid}.json"
for fact in facts:
fd = open(
os.path.join(
dir_name,
name_template.format(**fact)
),
"w+"
)
fd.write(json.dumps(fact, indent=4))
print("Created {0}".format(fd.name))
fd.close()
def parse_node_ids(x):
"""Parse arguments with commas and spaces
:returns: list of lists with numbers
"""
filtered = [y for y in x.split(",") if y.strip() != '']
if len(filtered) > 1:
return map(int, filtered)
elif len(filtered) == 1:
return [int(filtered[0])]
else:
return None
def get_download_arg(help_msg):
return {
"args": ["-d", "--download"],
"params": {
"dest": "download",
"action": "store_true",
"help": help_msg,
"default": False
}
}
def get_list_arg(help_msg):
return {
"args": ["-l", "--list"],
"params": {
"dest": "list",
"action": "store_true",
"help": help_msg,
"default": False
}
}
def get_dir_arg(help_msg):
return {
"args": ["--dir"],
"params": {
"dest": "dir",
"action": "store",
"help": help_msg,
"default": None
}
}
def get_verify_arg(help_msg):
return {
"args": ["-v", "--verify"],
"params": {
"dest": "verify",
"action": "store_true",
"help": help_msg,
"default": False
}
}
def get_upload_arg(help_msg):
return {
"args": ["-u", "--upload"],
"params": {
"dest": "upload",
"action": "store_true",
"help": help_msg,
"default": False
}
}
def get_default_arg(help_msg):
return {
"args": ["--default"],
"params": {
"dest": "default",
"action": "store_true",
"help": help_msg,
"default": False
}
}
def get_set_arg(help_msg):
return {
"args": ["-s", "--set"],
"params": {
"dest": "set",
"action": "store_true",
"help": help_msg,
"default": False
}
}
def get_delete_arg(help_msg):
return {
"args": ["--delete"],
"params": {
"dest": "delete",
"action": "store_true",
"help": help_msg,
"default": False
}
}
def get_release_arg(help_msg):
return {
"args": ["--rel", "--release"],
"params": {
"dest": "rel",
"action": "store",
"type": str,
"help": help_msg,
"default": None
}
}
actions = {
"release": {
"action": release,
"args": [
get_list_arg("List all available releases."),
get_release_arg("Specify release id to configure"),
{
"args": ["-c", "--config"],
"params": {
"dest": "config",
"action": "store_true",
"help": "Configure release with --release",
"default": False
}
}, {
"args": ["-U", "--user", "--user-name"],
"params": {
"dest": "username",
"action": "store",
"type": str,
"help": "Username for release credentials",
"default": None
}
}, {
"args": ["-P", "--pass", "--password"],
"params": {
"dest": "password",
"action": "store",
"type": str,
"help": "Password for release credentials",
"default": None
}
}]
},
"environment": {
"action": environment,
"args": [
get_list_arg("List all available environments."),
get_set_arg("Set environment parameters (e.g name, deployment mode)"),
get_delete_arg("Delete environment with specific env or name"),
get_release_arg("Release id"),
{
"args": ["-c", "--env-create", "--create"],
"params": {
"dest": "create",
"action": "store_true",
"help": "Create a new environment with specific "
"release id and name.",
"default": False
}
}, {
"args": ["--name", "--env-name"],
"params": {
"dest": "ename",
"action": "store",
"type": str,
"help": "environment name",
"default": None
}
}, {
"args": ["-m", "--mode", "--deployment-mode"],
"params": {
"dest": "mode",
"action": "store",
"choices": ["multinode", "ha_compact"],
"help": "Set deployment mode for specific environment.",
"default": False
}
}]
},
"node": {
"action": node,
"args": [
get_list_arg("List all nodes."),
get_set_arg("Set role for specific node."),
get_delete_arg("Delete specific node from environment."),
get_default_arg("Get default network configuration of some node"),
get_download_arg("Download configuration of specific node"),
get_upload_arg("Upload configuration to specific node"),
get_dir_arg("Select directory to which download node attributes"),
{
"args": ["--node", "--node-id"],
"params": {
"dest": "node",
"action": "store",
"nargs": '+',
"type": parse_node_ids,
"help": "Node id.",
"default": None
}
}, {
"args": ["-r", "--role"],
"params": {
"dest": "role",
"type": lambda v: v.split(','),
"action": SetAction,
"help": "Role to assign for node.",
"default": None
}
}, {
"args": ["--net", "--network"],
"params": {
"dest": "network",
"action": "store_true",
"help": "Node network configuration.",
"default": False
}
}, {
"args": ["--disk"],
"params": {
"dest": "disk",
"action": "store_true",
"help": "Node disk configuration.",
"default": False
}
}]
},
"network": {
"action": network,
"args": [
get_download_arg("Download current network configuration."),
get_dir_arg("Directory with network data."),
get_verify_arg("Verify current network configuration."),
get_upload_arg("Upload changed network configuration.")
]
},
"settings": {
"action": settings,
"args": [
get_download_arg("Modify current configuration."),
get_default_arg("Open default configuration."),
get_upload_arg("Save current changes in configuration."),
get_dir_arg("Directory with configuration data.")
]
},
"task": {
"action": task,
"args": [{
"args": ["-d", "--delete"],
"params": {
"dest": "delete",
"action": "store_true",
"help": "delete",
"default": False
}
}, {
"args": ["--id", "--task-id"],
"params": {
"dest": "tid",
"action": "store",
"nargs": '+',
"type": parse_node_ids,
"help": "Task id.",
"default": None
}
}]
},
"snapshot": {
"action": snapshot,
"args": [
get_dir_arg("Directory to which download snapshot."),
get_download_arg("Download snapshot.")
]
},
"deploy": {
"action": deploy,
"args": []
}
}
def get_args_for_facts(fact_type):
return [
get_delete_arg("Delete current {0} data.".format(fact_type)),
get_download_arg("Download current {0} data.".format(fact_type)),
get_upload_arg("Upload current {0} data.".format(fact_type)),
get_default_arg("Download default {0} data.".format(fact_type)),
get_dir_arg("Directory with {0} data.".format(fact_type))
]
substitutions = {
#replace from: to
"env": "environment",
"nodes": "node",
"net": "network",
"rel": "release",
"list": "--list",
"set": "--set",
"delete": "--delete",
"download": "--download",
"upload": "--upload",
"default": "--default",
"create": "--create",
"remove": "--delete",
"config": "--config",
"--roles": "--role"
}
def prepare_args():
# replace some args from dict substitutions
sys.argv = map(
lambda x: substitutions.get(x, x),
sys.argv
)
# move --json and --debug flags before any action
for argument in ["--json", "--debug"]:
if argument in sys.argv:
sys.argv.remove(argument)
sys.argv.insert(1, argument)
# move --env or --env-id flags to beginning if declared after action
for arg in sys.argv:
if "--env" in arg:
# if declaration with '=' sign (e.g. --env-id=1)
if "=" in arg:
index_of_env = sys.argv.index(arg)
env = sys.argv.pop(index_of_env)
sys.argv.insert(1, env)
else:
index_of_env = sys.argv.index(arg)
sys.argv.pop(index_of_env)
env = sys.argv.pop(index_of_env)
sys.argv.insert(1, env)
sys.argv.insert(1, arg)
break
for fact_type in [deployment, provisioning]:
actions[fact_type.__name__] = {
"action": fact_type,
"args": get_args_for_facts(fact_type.__name__)
}
if __name__ == '__main__':
prepare_args()
parser = argparse.ArgumentParser()
parser.add_argument("--env", "--env-id",
dest="env",
action="store",
type=str,
help="environment id",
default=None
)
parser.add_argument("--json",
dest="json",
action="store_true",
help="prints to only json to stdout",
default=False
)
parser.add_argument("--debug",
dest="debug",
action="store_true",
help="prints details of all HTTP request",
default=False
)
subparsers = parser.add_subparsers(
dest="action", help='actions'
)
for action, parameters in actions.iteritems():
action_parser = subparsers.add_parser(
action, help=parameters["action"].__doc__
)
for arg in parameters.get("args", []):
action_parser.add_argument(
*arg["args"],
**arg["params"]
)
parsed_params, other_params = parser.parse_known_args()
sys.argv.pop(1)
DEBUG = parsed_params.debug
JSON = parsed_params.json
if parsed_params.action not in actions:
parser.print_help()
sys.exit(0)
current_action = getattr(parsed_params, "action")
delattr(parsed_params, "action")
actions[current_action]["action"](parsed_params)