###############################################################################
#
# Crossbar.io Shell
# Copyright (c) typedef int GmbH. Licensed under EUPLv1.2.
#
###############################################################################
import hashlib
import importlib
import json
import os
import platform
import re
import sys
# Notes:
#
# click does NOT allow to document CLI arguments! this is ridiculous, but true:
# https://github.com/pallets/click/issues/587
# to document, refer to arguments in UPPERCASE in the main command help. yeah, awesome.
# the justification is: "this has been the Unix way" - yeah, fuck that.
# this is for pyinstaller! otherwise it fails to find this dep.
# see: http://cffi.readthedocs.io/en/latest/cdef.html
import _cffi_backend # noqa
import click
import six
# import and select network framework in txaio _before_ any further crossbar.shell imports
import txaio
txaio.use_twisted() # noqa
from cfxdb.exporter import Exporter
from crossbar.shell import __build__, __version__, command # noqa: E402
from crossbar.shell.app import Application
[docs]
_HEXKEY = re.compile(r"^[0-9a-fA-F]{64,64}$")
[docs]
USAGE = """
Examples:
To start the interactive shell, use the "shell" command:
cbf shell
You can run the shell under different user profile
using the "--profile" option:
cbf --profile mister-test1 shell
"""
# the global, singleton app object
[docs]
def hl(text):
if not isinstance(text, six.text_type):
text = "{}".format(text)
return click.style(text, fg="yellow", bold=True)
[docs]
class Config(object):
"""
Command configuration object where we collect all the parameters,
options etc for later processing.
"""
def __init__(self, app, dotdir, profile, realm, role):
[docs]
self.resource_type = None
[docs]
def __str__(self):
return "Config(resource_type={}, resource={})".format(self.resource_type, self.resource)
@click.group(help="Crossbar.io Command Line", invoke_without_command=True)
@click.option(
"--dotdir",
envvar="CBF_DOTDIR",
default=None,
help="Set the dot directory (with config and profile) to be used",
)
@click.option(
"--profile",
envvar="CBF_PROFILE",
default="default",
help="Set the profile to be used",
)
@click.option(
"--realm",
envvar="CBF_REALM",
default=None,
help="Set the realm to join",
)
@click.option(
"--role",
envvar="CBF_ROLE",
default=None,
help="Set the role requested to authenticate as",
)
@click.option("--debug", is_flag=True, help="Enable debug output")
@click.pass_context
[docs]
def cli(ctx, dotdir, profile, realm, role, debug):
if debug:
txaio.start_logging(level="info")
ctx.obj = Config(_app, dotdir, profile, realm, role)
# Allowing a command group to specifiy a default subcommand can be done using
# https://github.com/click-contrib/click-default-group
#
# However, this breaks the click-repl integration for prompt-toolkit:
#
# https://github.com/pallets/click/issues/430#issuecomment-282015177
#
# Hence, we are using a different (probably less clean) trick - this works.
#
if ctx.invoked_subcommand is None:
ctx.invoke(cmd_shell)
@cli.command(name="monitor", help="monitor master node")
@click.pass_context
[docs]
def cmd_monitor(ctx):
cfg = ctx.obj
# key: userkey.UserKey
# profile: config.Profile
key, profile = ctx.obj.app.load_profile(profile=cfg.profile)
from .monitor import run
run(management_url=profile.url, privkey_file=profile.privkey)
@cli.command(name="version", help="print version information")
@click.pass_context
[docs]
def cmd_version(ctx):
def get_version(name_or_module):
if isinstance(name_or_module, str):
name_or_module = importlib.import_module(name_or_module)
try:
return name_or_module.__version__
except AttributeError:
return ""
# Python (language)
py_ver = ".".join([str(x) for x in list(sys.version_info[:3])])
# Python (implementation)
if hasattr(sys, "pypy_version_info"):
pypy_version_info = getattr(sys, "pypy_version_info")
py_impl_str = ".".join(str(x) for x in pypy_version_info[:3])
py_ver_detail = "{}-{}".format(platform.python_implementation(), py_impl_str)
else:
py_ver_detail = platform.python_implementation()
# Autobahn
ab_ver = get_version("autobahn")
# Pyinstaller (frozen EXE)
py_is_frozen = getattr(sys, "frozen", False)
if py_is_frozen:
m = hashlib.sha256()
with open(sys.executable, "rb") as fd:
m.update(fd.read())
fingerprint = m.hexdigest()
else:
fingerprint = None
# Docker Compose
try:
import compose
except ImportError:
compose_ver = "not installed"
else:
compose_ver = compose.__version__
# Sphinx
try:
import sphinx
except ImportError:
sphinx_ver = "not installed"
else:
sphinx_ver = sphinx.__version__
platform_str = platform.platform(terse=True, aliased=True)
click.echo()
click.echo(hl(" Crossbar.io Shell") + " - Command line tool for Crossbar.io")
click.echo()
click.echo(" {:<24}: {}".format("Version", hl("{} (build {})".format(__version__, __build__))))
click.echo(" {:<24}: {}".format("Platform", hl(platform_str)))
click.echo(" {:<24}: {}".format("Python (language)", hl(py_ver)))
click.echo(" {:<24}: {}".format("Python (implementation)", hl(py_ver_detail)))
click.echo(" {:<24}: {}".format("Autobahn", hl(ab_ver)))
click.echo(" {:<24}: {}".format("Docker Compose", hl(compose_ver)))
click.echo(" {:<24}: {}".format("Sphinx", hl(sphinx_ver)))
click.echo(" {:<24}: {}".format("Frozen executable", hl("yes" if py_is_frozen else "no")))
if py_is_frozen:
click.echo(" {:<24}: {}".format("Executable SHA256", hl(fingerprint)))
click.echo()
@cli.command(name="init", help="create a new user profile / key-pair if none exists")
@click.option(
"--yes",
is_flag=True,
default=False,
help="Answer yes / use default for anything prompted",
)
@click.pass_context
[docs]
def cmd_init(ctx, yes):
cfg = ctx.obj
cfg.yes_to_all = yes
ctx.obj.app.run_context(ctx)
@cli.command(name="auth", help="authenticate user with Crossbar.io")
@click.option(
"--code",
default=None,
help="Supply authentication code (received by email)",
)
@click.option(
"--new-code",
is_flag=True,
default=False,
# noqa: E251
help="Request sending of a new authentication code (even though an old one is still pending)",
)
@click.option(
"--yes",
is_flag=True,
default=False,
# noqa: E251
help="Answer yes / use default for anything prompted",
)
@click.pass_context
[docs]
def cmd_auth(ctx, code, new_code, yes):
cfg = ctx.obj
cfg.code = code
cfg.new_code = new_code
cfg.yes_to_all = yes
ctx.obj.app.run_context(ctx)
@cli.command(name="shell", help="run an interactive Crossbar.io Shell")
@click.pass_context
[docs]
def cmd_shell(ctx):
ctx.obj.app.run_context(ctx)
@cli.command(name="clear", help="clear screen")
[docs]
def cmd_clear():
click.clear()
@cli.command(name="help", help="general help")
@click.pass_context
[docs]
def cmd_help(ctx):
click.echo(ctx.parent.get_help())
click.echo(USAGE)
@cli.group(name="set", help="change shell settings")
@click.pass_context
#
# set output-verbosity
#
@cmd_set.group(name="output-verbosity", help="command output verbosity")
@click.pass_context
[docs]
def cmd_set_output_verbosity(ctx):
pass
@cmd_set_output_verbosity.command(name="silent", help="swallow everything including result, but error messages")
@click.pass_context
[docs]
def cmd_set_output_verbosity_silent(ctx):
ctx.obj.app.set_output_verbosity("silent")
@cmd_set_output_verbosity.command(name="result-only", help="only output the plain command result")
@click.pass_context
[docs]
def cmd_set_output_verbosity_result_only(ctx):
ctx.obj.app.set_output_verbosity("result-only")
@cmd_set_output_verbosity.command(name="normal", help="output result and short run-time message")
@click.pass_context
[docs]
def cmd_set_output_verbosity_normal(ctx):
ctx.obj.app.set_output_verbosity("normal")
@cmd_set_output_verbosity.command(name="extended", help="output result and extended execution information.")
@click.pass_context
[docs]
def cmd_set_output_verbosity_extended(ctx):
ctx.obj.app.set_output_verbosity("extended")
#
# set output-format
#
@cmd_set.group(name="output-format", help="command output format")
@click.pass_context
for output_format in Application.OUTPUT_FORMAT:
_make_set_output_format(output_format)
#
# set output-style
#
@cmd_set.group(name="output-style", help="command output style")
@click.pass_context
[docs]
def cmd_set_output_style(ctx):
pass
[docs]
def _make_set_output_style(output_style):
@cmd_set_output_style.command(name=output_style, help="set {} output style".format(output_style.upper()))
@click.pass_context
def f(ctx):
ctx.obj.app.set_output_style(output_style)
return f
for output_style in Application.OUTPUT_STYLE:
_make_set_output_style(output_style)
@cli.group(name="show", help="show resources")
@click.pass_context
[docs]
def cmd_show(ctx):
pass
@cmd_show.command(name="status", help="show domain or mrealm status")
@click.pass_context
[docs]
def cmd_show_domain_status(ctx):
_command = command.CmdGetDomainStatus(ctx.obj.realm)
ctx.obj.app.run_context(ctx, _command)
@cmd_show.command(name="version", help="get domain controller software version")
@click.pass_context
[docs]
def cmd_show_domain_version(ctx):
_command = command.CmdGetDomainVersion(ctx.obj.realm)
ctx.obj.app.run_context(ctx, _command)
@cmd_show.command(name="license", help="get domain software stack license")
@click.pass_context
[docs]
def cmd_show_domain_license(ctx):
_command = command.CmdGetDomainLicense(ctx.obj.realm)
ctx.obj.app.run_context(ctx, _command)
@cmd_show.command(name="database", help="open and show embedded database details")
@click.argument("dbpath")
@click.option("--include-slots/--no-include-slots", default=False, type=bool, help="show database slots")
@click.pass_context
[docs]
def cmd_show_database(ctx, dbpath, include_slots):
exporter = Exporter(dbpath)
exporter.print_config()
exporter.print_stats(include_slots=include_slots)
@cli.group(name="export", help="export resources")
@click.pass_context
[docs]
def cmd_export(ctx):
pass
@cmd_export.command(name="database", help="export embedded database")
@click.argument("dbpath")
@click.option("--filename")
@click.option("--include-indexes/--no-include-indexes", default=False, type=bool, help="export index tables")
@click.option(
"--include-schemata",
type=str,
help="list of schemata to export (meta, globalschema, mrealmschema, xbr, xbrmm, xbrnetwork)",
)
@click.option("--exclude-tables", type=str)
@click.option("--use-json/--no-use-json", default=False, type=bool)
@click.option("--use-binary-hex-encoding/--no-use-binary-hex-encoding", default=False, type=bool)
@click.option("--quiet/--no-quiet", default=False, type=bool)
@click.pass_context
[docs]
def cmd_export_database(
ctx, dbpath, filename, include_indexes, include_schemata, exclude_tables, use_json, use_binary_hex_encoding, quiet
):
if include_schemata:
include_schemata = include_schemata.split(",")
if exclude_tables:
exclude_tables = exclude_tables.split(",")
exporter = Exporter(dbpath)
exporter.export_database(
filename,
include_indexes=include_indexes,
include_schemata=include_schemata,
exclude_tables=exclude_tables,
use_json=use_json,
use_binary_hex_encoding=use_binary_hex_encoding,
quiet=quiet,
)
@cli.group(name="import", help="import resources")
@click.pass_context
[docs]
def cmd_import(ctx):
pass
@cmd_import.command(name="database", help="import embedded database")
@click.argument("dbpath")
@click.argument("filename")
@click.option("--include-indexes/--no-include-indexes", default=False, type=bool, help="import index tables")
@click.option("--include-schemata", type=str)
@click.option("--exclude-tables", type=str)
@click.option("--use-json/--no-use-json", default=False, type=bool)
@click.option("--use-binary-hex-encoding/--no-use-binary-hex-encoding", default=False, type=bool)
@click.option("--quiet/--no-quiet", default=False, type=bool)
@click.pass_context
[docs]
def cmd_import_database(
ctx, dbpath, filename, include_indexes, include_schemata, exclude_tables, use_json, use_binary_hex_encoding, quiet
):
if include_schemata:
include_schemata = include_schemata.split(",")
if exclude_tables:
exclude_tables = exclude_tables.split(",")
exporter = Exporter(dbpath)
exporter.print_stats()
exporter.import_database(
filename,
include_indexes=include_indexes,
include_schemata=include_schemata,
exclude_tables=exclude_tables,
use_json=use_json,
use_binary_hex_encoding=use_binary_hex_encoding,
quiet=quiet,
)
exporter.print_stats()
@cli.group(name="add", help="add resources")
@click.pass_context
@cmd_add.command(name="principal", help="add a principal to an application realm")
@click.argument("realm")
@click.argument("principal")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_add_principal(ctx, realm, principal, config=None):
if config:
config = json.loads(config)
else:
config = {}
config["authid"] = principal
cmd = command.CmdAddPrincipal(realm, principal, config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_add.command(name="principal-credential", help="add a credential to a principal")
@click.argument("realm")
@click.argument("principal")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_add_principal_credential(ctx, realm, principal, config=None):
if config:
config = json.loads(config)
else:
config = {}
cmd = command.CmdAddPrincipalCredential(realm, principal, config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_add.command(name="role-permission", help="add a permission to a role")
@click.argument("role")
@click.argument("uri")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_add_role_permission(ctx, role, uri, config=None):
if config:
config = json.loads(config)
else:
config = {}
config["uri"] = uri
cmd = command.CmdAddRolePermission(role, uri, config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_add.command(name="arealm-role", help="add a role to an application realm")
@click.argument("realm")
@click.argument("role")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_add_realm_role(ctx, realm, role, config=None):
if config:
config = json.loads(config)
cmd = command.CmdAddApplicationRealmRole(realm, role, config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_add.command(name="routercluster-node", help="add a node to a routercluster")
@click.argument("cluster")
@click.argument("node", default="all")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_add_routercluster_node(ctx, cluster, node, config=None):
if config:
config = json.loads(config)
cmd = command.CmdAddRouterClusterNode(cluster, node, config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_add.command(name="routercluster-workergroup", help="add a workergroup to a routercluster")
@click.argument("cluster")
@click.argument("workergroup")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_add_routercluster_workergroup(ctx, cluster, workergroup, config=None):
if config:
config = json.loads(config)
else:
config = {}
config["name"] = workergroup
cmd = command.CmdAddRouterClusterWorkerGroup(cluster, workergroup, config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_add.command(name="webcluster-node", help="add a node to a webcluster")
@click.argument("cluster")
@click.argument("node", default="all")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_add_webcluster_node(ctx, cluster, node, config=None):
if config:
config = json.loads(config)
cmd = command.CmdAddWebClusterNode(cluster, node, config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_add.command(name="webcluster-service", help="add a web service to a webcluster")
@click.argument("cluster")
@click.argument("path")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_add_webcluster_service(ctx, cluster, path, config=None):
if config:
webservice = json.loads(config)
else:
webservice = {}
webservice["path"] = path
cmd = command.CmdAddWebClusterService(cluster, path, webservice)
ctx.obj.app.run_context(ctx, cmd)
@cli.group(name="create", help="create resources")
@click.pass_context
[docs]
def cmd_create(ctx):
pass
@cmd_create.command(name="mrealm", help="create a new management realm")
@click.argument("realm")
@click.pass_context
[docs]
def cmd_create_management_realm(ctx, realm):
cmd = command.CmdCreateManagementRealm(realm=realm)
ctx.obj.app.run_context(ctx, cmd)
@cmd_create.command(name="arealm", help="create a new application realm")
@click.argument("realm")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_create_application_realm(ctx, realm, config=None):
if config:
config = json.loads(config)
else:
config = {}
config["name"] = realm
cmd = command.CmdCreateApplicationRealm(config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_create.command(name="role", help="create a new role")
@click.argument("role")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_create_role(ctx, role, config=None):
if config:
config = json.loads(config)
else:
config = {}
config["name"] = role
cmd = command.CmdCreateRole(config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_create.command(name="routercluster", help="create a new routercluster")
@click.argument("cluster")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_create_routercluster(ctx, cluster, config=None):
if config:
config = json.loads(config)
else:
config = {}
config["name"] = cluster
cmd = command.CmdCreateRouterCluster(config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_create.command(name="webcluster", help="create a new webcluster")
@click.argument("cluster")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_create_webcluster(ctx, cluster, config=None):
if config:
config = json.loads(config)
else:
config = {}
config["name"] = cluster
cmd = command.CmdCreateWebCluster(config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_create.command(name="webservice", help="create a webservice within a webcluster")
@click.argument("cluster")
@click.argument("config")
@click.pass_context
[docs]
def cmd_create_webservice(ctx, cluster, config):
config = json.loads(config)
cmd = command.CmdCreateWebService(cluster, config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_create.command(name="docker-container", help="create a new Docker container on a node")
@click.argument("node")
@click.argument("image")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_create_docker_container(ctx, node, image, config=None):
if config:
config = json.loads(config)
else:
config = {}
cmd = command.CmdCreateDockerContainer(node, image, config)
ctx.obj.app.run_context(ctx, cmd)
@cli.group(name="remove", help="remove resources")
@click.pass_context
[docs]
def cmd_remove(ctx):
pass
@cmd_remove.command(name="routercluster-node", help="remove a node from a routercluster")
@click.argument("cluster")
@click.argument("node")
@click.pass_context
[docs]
def cmd_remove_routercluster_node(ctx, cluster, node):
cmd = command.CmdRemoveRouterClusterNode(cluster, node)
ctx.obj.app.run_context(ctx, cmd)
@cmd_remove.command(name="routercluster-workergroup", help="remove a workergroup from a routercluster")
@click.argument("cluster")
@click.argument("workergroup")
@click.pass_context
[docs]
def cmd_remove_routeercluster_workergroup(ctx, cluster, workergroup):
cmd = command.CmdRemoveRouterClusterWorkerGroup(cluster, workergroup)
ctx.obj.app.run_context(ctx, cmd)
@cmd_remove.command(name="webcluster-node", help="remove a node from a webcluster")
@click.argument("cluster")
@click.argument("node")
@click.pass_context
[docs]
def cmd_remove_webcluster_node(ctx, cluster, node):
cmd = command.CmdRemoveWebClusterNode(cluster, node)
ctx.obj.app.run_context(ctx, cmd)
@cmd_remove.command(name="webcluster-service", help="remove a service from a webcluster")
@click.argument("cluster")
@click.argument("path")
@click.pass_context
[docs]
def cmd_remove_webcluster_service(ctx, cluster, path):
cmd = command.CmdRemoveWebClusterService(cluster, path)
ctx.obj.app.run_context(ctx, cmd)
@cmd_remove.command(name="arealm-principal", help="remove a principal from an application realm")
@click.argument("arealm")
@click.argument("principal")
@click.pass_context
[docs]
def cmd_remove_arealm_principal(ctx, arealm, principal):
cmd = command.CmdRemoveArealmPrincipal(arealm, principal)
ctx.obj.app.run_context(ctx, cmd)
@cmd_remove.command(name="principal-credential", help="remove credentials from a principal on an application realm")
@click.argument("arealm")
@click.argument("principal")
@click.argument("credential")
@click.pass_context
[docs]
def cmd_remove_arealm_principal_credential(ctx, arealm, principal, credential):
cmd = command.CmdRemoveArealmPrincipalCredential(arealm, principal, credential)
ctx.obj.app.run_context(ctx, cmd)
@cmd_remove.command(name="role-permission", help="remove a permission from a role")
@click.argument("role")
@click.argument("path")
@click.pass_context
[docs]
def cmd_remove_role_permission(ctx, role, path):
cmd = command.CmdRemoveRolePermission(role, path)
ctx.obj.app.run_context(ctx, cmd)
@cmd_remove.command(name="arealm-role", help="remove a role from an application realm")
@click.argument("arealm")
@click.argument("role")
@click.pass_context
[docs]
def cmd_remove_arealm_role(ctx, arealm, role):
cmd = command.CmdRemoveArealmRole(arealm, role)
ctx.obj.app.run_context(ctx, cmd)
@cli.group(name="delete", help="delete resources")
@click.pass_context
[docs]
def cmd_delete(ctx):
pass
@cmd_delete.command(name="mrealm", help="Delete an existing management REALM by name.")
@click.argument("realm", type=str)
@click.option(
"--cascade",
is_flag=True,
help="Automatically unpair (but not delete) any nodes currently paired with and unassign (but not delete) any users currently assigned to the management realm to be deleted.",
)
@click.pass_context
[docs]
def cmd_delete_management_realm(ctx, realm, cascade=False):
cmd = command.CmdDeleteManagementRealm(realm, cascade)
ctx.obj.app.run_context(ctx, cmd)
@cmd_delete.command(name="routercluster", help="delete an existing webrouterclustercluster")
@click.argument("cluster")
@click.pass_context
[docs]
def cmd_delete_routercluster(ctx, cluster):
cmd = command.CmdDeleteRouterCluster(cluster)
ctx.obj.app.run_context(ctx, cmd)
@cmd_delete.command(name="webcluster", help="delete an existing webcluster")
@click.argument("cluster")
@click.pass_context
[docs]
def cmd_delete_webcluster(ctx, cluster):
cmd = command.CmdDeleteWebCluster(cluster)
ctx.obj.app.run_context(ctx, cmd)
@cmd_delete.command(name="arealm", help="delete an existing application realm")
@click.argument("arealm")
@click.option("--cascade", is_flag=True, help="Automatically delete dependent resources of the application realm.")
@click.pass_context
[docs]
def cmd_delete_arealm(ctx, arealm, cascade=False):
cmd = command.CmdDeleteApplicationRealm(arealm, cascade)
ctx.obj.app.run_context(ctx, cmd)
@cmd_delete.command(name="role", help="delete an existing application role")
@click.argument("role")
@click.pass_context
[docs]
def cmd_delete_role(ctx, role):
cmd = command.CmdDeleteRole(role)
ctx.obj.app.run_context(ctx, cmd)
@cli.group(name="pair", help="pair nodes and devices")
@click.pass_context
[docs]
def cmd_pair(ctx):
pass
[docs]
def _read_pubkey(pubkey):
if not _HEXKEY.match(pubkey):
fn = os.path.expanduser(pubkey)
if not os.path.exists(fn):
raise Exception("could not open node public key file {}".format(fn))
public_hex = None
with open(fn, "r") as f:
data = f.read()
for line in data.splitlines():
if line.startswith("public-key-ed25519"):
public_hex = line.split(":")[1].strip()
break
if not public_hex:
raise Exception("no public key found in node public key file {}".format(fn))
pubkey = public_hex
return pubkey
@cmd_pair.command(name="node", help="pair a node")
@click.argument("pubkey")
@click.argument("realm")
@click.argument("node_id")
@click.option("--authextra", type=str)
@click.pass_context
[docs]
def cmd_pair_node(ctx, pubkey, realm, node_id, authextra=None):
"""
:param ctx:
:param pubkey: the public key of the node, a 32 bytes Ed25519 public key provided as a HEX string (64 characters),
or alternatively a filename to read the public key from
:param realm: management realm the node is to be paired with
:param node_id: the ID of node assigned when the node is connecting
:param authextra: authextra to be provided to the node when connecting, must be a JSON string
:return:
"""
assert isinstance(pubkey, str)
assert isinstance(realm, str)
assert isinstance(node_id, str)
assert authextra is None or isinstance(authextra, str)
pubkey = _read_pubkey(pubkey)
if authextra:
authextra = json.loads(authextra)
cmd = command.CmdPairNode(pubkey=pubkey, realm=realm, node_id=node_id, authextra=authextra)
ctx.obj.app.run_context(ctx, cmd)
@cli.group(name="unpair", help="unpair nodes and devices")
@click.pass_context
[docs]
def cmd_unpair(ctx):
pass
@cmd_unpair.command(name="node", help="unpair a node")
@click.argument("pubkey")
@click.pass_context
[docs]
def cmd_unpair_node(ctx, pubkey):
"""
:param ctx:
:param pubkey:
:return:
"""
assert isinstance(pubkey, str)
pubkey = _read_pubkey(pubkey)
cmd = command.CmdUnpairNode(pubkey=pubkey)
ctx.obj.app.run_context(ctx, cmd)
@cli.group(name="start", help="start workers, components, ..")
@click.pass_context
[docs]
def cmd_start(ctx):
pass
# @cmd_start.command(name='worker', help='start a worker')
# @click.argument('node')
# @click.argument('worker')
# @click.argument('worker-type')
# @click.option('--options', help='worker options', default=None)
# @click.pass_context
# def cmd_start_worker(ctx, node, worker, worker_type, options=None):
# cmd = command.CmdStartWorker(node, worker, worker_type, worker_options=options)
# ctx.obj.app.run_context(ctx, cmd)
# from crossbar.shell.command import CmdStartContainerWorker, CmdStartContainerComponent
@cmd_start.command(name="docker-container", help="start a Docker container on a node")
@click.argument("node")
@click.argument("container")
@click.pass_context
[docs]
def cmd_start_docker_container(ctx, node, container):
cmd = command.CmdStartDockerContainer(node, container)
ctx.obj.app.run_context(ctx, cmd)
@cmd_start.command(name="container-worker", help="start a container worker")
@click.option("--process-title", help="worker process title (at OS level)", default=None)
@click.argument("node")
@click.argument("worker")
@click.pass_context
[docs]
def cmd_start_container_worker(ctx, node, worker, process_title=None):
cmd = command.CmdStartContainerWorker(node, worker, process_title=process_title)
ctx.obj.app.run_context(ctx, cmd)
@cmd_start.command(name="container-component", help="start a container component")
@click.argument("node")
@click.argument("worker")
@click.argument("component")
@click.pass_context
[docs]
def cmd_start_container_component(ctx, node, worker, component):
cmd = command.CmdStartContainerComponent(node, worker, component)
ctx.obj.app.run_context(ctx, cmd)
@cmd_start.command(name="router-worker", help="start a router worker")
@click.option("--process-title", help="worker process title (at OS level)", default=None)
@click.argument("node")
@click.argument("worker")
@click.pass_context
[docs]
def cmd_start_router_worker(ctx, node, worker, process_title=None):
cmd = command.CmdStartRouterWorker(node, worker, process_title=process_title)
ctx.obj.app.run_context(ctx, cmd)
@cmd_start.command(name="router-realm", help="start a router realm")
@click.argument("node")
@click.argument("worker")
@click.argument("realm")
@click.pass_context
[docs]
def cmd_start_router_realm(ctx, node, worker, realm):
cmd = command.CmdStartRouterRealm(node, worker, realm)
ctx.obj.app.run_context(ctx, cmd)
@cmd_start.command(name="router-transport", help="start a router transport")
@click.argument("node")
@click.argument("worker")
@click.argument("transport")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_start_router_transport(ctx, node, worker, transport, config=None):
if config:
config = json.loads(config)
cmd = command.CmdStartRouterTransport(node, worker, transport, config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_start.command(name="webtransport-service", help="start a web-transport service")
@click.argument("node")
@click.argument("worker")
@click.argument("transport")
@click.argument("path")
@click.option("--config", type=str)
@click.pass_context
[docs]
def cmd_start_web_transport_service(ctx, node, worker, transport, path, config=None):
if config:
config = json.loads(config)
cmd = command.CmdStartWebTransportService(node, worker, transport, path, config)
ctx.obj.app.run_context(ctx, cmd)
@cmd_start.command(name="guest-worker", help="start a guest worker")
@click.argument("node")
@click.argument("worker")
@click.pass_context
[docs]
def cmd_start_guest_worker(ctx, node, worker):
cmd = command.CmdStartGuestWorker(node, worker)
ctx.obj.app.run_context(ctx, cmd)
@cmd_start.command(name="routercluster", help="start a routercluster")
@click.argument("cluster")
@click.pass_context
[docs]
def cmd_start_routercluster(ctx, cluster):
cmd = command.CmdStartRouterCluster(cluster)
ctx.obj.app.run_context(ctx, cmd)
@cmd_start.command(name="arealm", help="start an application realm on a router worker group")
@click.argument("arealm")
@click.argument("routercluster")
@click.argument("workergroup")
@click.argument("webcluster")
@click.pass_context
[docs]
def cmd_start_arealm(ctx, arealm, routercluster, workergroup, webcluster):
cmd = command.CmdStartApplicationRealm(arealm, routercluster, workergroup, webcluster)
ctx.obj.app.run_context(ctx, cmd)
@cmd_start.command(name="webcluster", help="start a webcluster")
@click.argument("cluster")
@click.pass_context
[docs]
def cmd_start_webcluster(ctx, cluster):
cmd = command.CmdStartWebCluster(cluster)
ctx.obj.app.run_context(ctx, cmd)
@cli.group(name="stop", help="stop workers, components, ..")
@click.pass_context
[docs]
def cmd_stop(ctx):
pass
@cmd_stop.command(name="docker-container", help="stop a Docker container running on a node")
@click.argument("node")
@click.argument("container")
@click.pass_context
[docs]
def cmd_stop_docker_container(ctx, node, container):
cmd = command.CmdStopDockerContainer(node, container)
ctx.obj.app.run_context(ctx, cmd)
@cmd_stop.command(name="worker", help="stop a worker")
@click.argument("node")
@click.argument("worker")
@click.pass_context
[docs]
def cmd_stop_worker(ctx, node, worker):
cmd = command.CmdStopWorker(node, worker)
ctx.obj.app.run_context(ctx, cmd)
@cmd_stop.command(name="router-realm", help="stop a router realm")
@click.argument("node")
@click.argument("worker")
@click.argument("realm")
@click.pass_context
[docs]
def cmd_stop_router_realm(ctx, node, worker, realm):
cmd = command.CmdStopRouterRealm(node, worker, realm)
ctx.obj.app.run_context(ctx, cmd)
@cmd_stop.command(name="router-transport", help="stop a router transport")
@click.argument("node")
@click.argument("worker")
@click.argument("transport")
@click.pass_context
[docs]
def cmd_stop_router_transport(ctx, node, worker, transport):
cmd = command.CmdStopRouterTransport(node, worker, transport)
ctx.obj.app.run_context(ctx, cmd)
@cmd_stop.command(name="routercluster", help="stop a routercluster")
@click.argument("cluster")
@click.pass_context
[docs]
def cmd_stop_routercluster(ctx, cluster):
cmd = command.CmdStopRouterCluster(cluster)
ctx.obj.app.run_context(ctx, cmd)
@cmd_stop.command(name="webcluster", help="stop a webcluster")
@click.argument("cluster")
@click.pass_context
[docs]
def cmd_stop_webcluster(ctx, cluster):
cmd = command.CmdStopWebCluster(cluster)
ctx.obj.app.run_context(ctx, cmd)
@cli.group(name="list", help="list resources")
@click.pass_context
[docs]
def cmd_list(ctx):
pass
@cmd_list.command(name="mrealms", help="list management realms")
@click.option("--names/--no-names", default=False, type=bool, help="return node names (authid) instead of object IDs")
@click.pass_context
[docs]
def cmd_list_management_realms(ctx, names):
cmd = command.CmdListManagementRealms(names=names)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="nodes", help="list nodes")
@click.option("--names/--no-names", default=False, type=bool, help="return node names (authid) instead of object IDs")
@click.option(
"--online",
is_flag=True,
default=False,
# noqa: E251
help="List only nodes that are currently online",
)
@click.option(
"--offline",
is_flag=True,
default=False,
# noqa: E251
help="List only nodes that are currently offline",
)
@click.pass_context
[docs]
def cmd_list_nodes(ctx, names=None, online=None, offline=None):
assert not (online and offline)
cmd = command.CmdListNodes(online=online, offline=offline, names=names)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="workers", help="list workers")
@click.argument("node")
@click.pass_context
[docs]
def cmd_list_workers(ctx, node):
cmd = command.CmdListWorkers(node)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="router-realms", help="list router realms")
@click.argument("node")
@click.argument("worker")
@click.pass_context
[docs]
def cmd_list_router_realms(ctx, node, worker):
cmd = command.CmdListRouterRealms(node, worker)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="router-transports", help="list router transports")
@click.argument("node")
@click.argument("worker")
@click.pass_context
[docs]
def cmd_list_router_transports(ctx, node, worker):
cmd = command.CmdListRouterTransports(node, worker)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="arealms", help="list application realms")
@click.option(
"--names/--no-names", default=False, type=bool, help="return application realm names instead of object IDs"
)
@click.pass_context
[docs]
def cmd_list_arealms(ctx, names):
cmd = command.CmdListARealms(names=names)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="arealm-roles", help="list roles associated with application realm")
@click.argument("arealm")
@click.option("--names/--no-names", default=False, type=bool, help="return role names instead of object IDs")
@click.pass_context
[docs]
def cmd_list_arealm_roles(ctx, arealm, names):
cmd = command.CmdListARealmRoles(arealm, names=names)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="roles", help="list roles")
@click.option("--names/--no-names", default=False, type=bool, help="return role names instead of object IDs")
@click.pass_context
[docs]
def cmd_list_roles(ctx, names):
cmd = command.CmdListRoles(names=names)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="role-permissions", help="list role permissions")
@click.argument("role")
@click.pass_context
[docs]
def cmd_list_role_permissions(ctx, role):
cmd = command.CmdListRolePermissions(role)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="principals", help="list principals")
@click.argument("arealm")
@click.option("--names/--no-names", default=False, type=bool, help="return principals names instead of object IDs")
@click.pass_context
[docs]
def cmd_list_principals(ctx, arealm, names):
cmd = command.CmdListPrincipals(arealm, names=names)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="principal-credentials", help="list credentials of a principal")
@click.argument("arealm")
@click.argument("principal")
@click.pass_context
[docs]
def cmd_list_principal_credentials(ctx, arealm, principal):
cmd = command.CmdListPrincipalCredentials(arealm, principal)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="routerclusters", help="list routerclusters")
@click.option("--names/--no-names", default=False, type=bool, help="return routerclusters names instead of object IDs")
@click.pass_context
[docs]
def cmd_list_routerclusters(ctx, names):
cmd = command.CmdListRouterClusters(names=names)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="routercluster-nodes", help="list nodes of a routercluster")
@click.argument("cluster")
@click.option(
"--names/--no-names",
default=False,
type=bool,
help="return routercluster nodes names (= authid) instead of object IDs",
)
@click.option(
"--filter-status", default=None, type=str, help='filter nodes returned by node status given, eg "online"'
)
@click.pass_context
[docs]
def cmd_list_routercluster_nodes(ctx, cluster, names, filter_status):
cmd = command.CmdListRouterClusterNodes(cluster, names=names, filter_status=filter_status)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="routercluster-workergroups", help="list workergroups of a routercluster")
@click.argument("cluster")
@click.option(
"--names/--no-names", default=False, type=bool, help="return routercluster workergroup names instead of object IDs"
)
@click.option(
"--filter-status",
default=None,
type=str,
help='filter workergroups returned by workergroup status given, eg "online"',
)
@click.pass_context
[docs]
def cmd_list_routercluster_workergroups(ctx, cluster, names, filter_status):
cmd = command.CmdListRouterClusterWorkerGroups(cluster, names=names, filter_status=filter_status)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="webclusters", help="list webclusters")
@click.option("--names/--no-names", default=False, type=bool, help="return webcluster names instead of object IDs")
@click.pass_context
[docs]
def cmd_list_webclusters(ctx, names):
cmd = command.CmdListWebClusters(names=names)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="webcluster-nodes", help="list nodes of a webcluster")
@click.argument("cluster")
@click.option(
"--names/--no-names",
default=False,
type=bool,
help="return webcluster nodes names (= authid) instead of object IDs",
)
@click.option(
"--filter-status", default=None, type=str, help='filter nodes returned by node status given, eg "online"'
)
@click.pass_context
[docs]
def cmd_list_webcluster_nodes(ctx, cluster, names, filter_status):
cmd = command.CmdListWebClusterNodes(cluster, names=names, filter_status=filter_status)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="webcluster-services", help="list webservices of a webcluster")
@click.argument("cluster")
@click.pass_context
[docs]
def cmd_list_webcluster_webservices(ctx, cluster):
cmd = command.CmdListWebClusterWebService(cluster)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="docker-images", help="list Docker images available on a node")
@click.argument("node")
@click.pass_context
[docs]
def cmd_list_docker_images(ctx, node):
cmd = command.CmdListDockerImages(node)
ctx.obj.app.run_context(ctx, cmd)
@cmd_list.command(name="docker-containers", help="list Docker containers on a node")
@click.argument("node")
@click.pass_context
[docs]
def cmd_list_docker_containers(ctx, node):
cmd = command.CmdListDockerContainers(node)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="fabric", help="show fabric")
@click.pass_context
[docs]
def cmd_show_fabric(ctx):
cmd = command.CmdShowFabric()
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="mrealm", help="show management realm (for domains)")
@click.argument("realm", default="any")
@click.pass_context
[docs]
def cmd_show_mrealm(ctx, realm):
cmd = command.CmdShowManagementRealm(realm)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="docker", help="show Docker information")
@click.argument("node")
@click.pass_context
[docs]
def cmd_show_docker(ctx, node):
cmd = command.CmdShowDocker(node)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="docker-image", help="show Docker image information")
@click.argument("node")
@click.argument("image")
@click.pass_context
[docs]
def cmd_show_docker_image(ctx, node, image):
cmd = command.CmdShowDockerImage(node, image)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="docker-container", help="show Docker container information")
@click.argument("node")
@click.argument("container")
@click.pass_context
[docs]
def cmd_show_docker_container(ctx, node, container):
cmd = command.CmdShowDockerContainer(node, container)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="node", help="show node")
@click.argument("node", default="all")
@click.pass_context
[docs]
def cmd_show_node(ctx, node):
cmd = command.CmdShowNode(node)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="worker", help="show worker")
@click.argument("node")
@click.argument("worker")
@click.pass_context
[docs]
def cmd_show_worker(ctx, node, worker):
cmd = command.CmdShowWorker(node, worker)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="transport", help="show transport (for router workers)")
@click.argument("node")
@click.argument("worker")
@click.argument("transport")
@click.pass_context
[docs]
def cmd_show_transport(ctx, node, worker, transport):
cmd = command.CmdShowTransport(node, worker, transport)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="realm", help="show realm (for router workers)")
@click.argument("node")
@click.argument("worker")
@click.argument("realm")
@click.pass_context
[docs]
def cmd_show_realm(ctx, node, worker, realm):
cmd = command.CmdShowRealm(node, worker, realm)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="component", help="show component (for container and router workers)")
@click.argument("node")
@click.argument("worker")
@click.argument("component")
@click.pass_context
[docs]
def cmd_show_component(ctx, node, worker, component):
cmd = command.CmdShowComponent(node, worker, component)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="arealm", help="show application realm")
@click.argument("arealm", default="all")
@click.pass_context
[docs]
def cmd_show_arealm(ctx, arealm):
cmd = command.CmdShowApplicationRealm(arealm)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="principal", help="show principal")
@click.argument("realm")
@click.argument("principal")
@click.pass_context
[docs]
def cmd_show_principal(ctx, realm, principal):
cmd = command.CmdShowPrincipal(realm, principal)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="role", help="show role")
@click.argument("role")
@click.pass_context
[docs]
def cmd_show_role(ctx, role):
cmd = command.CmdShowRole(role)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="role-permission", help="show role permission")
@click.argument("role")
@click.argument("uri")
@click.pass_context
[docs]
def cmd_show_role_permission(ctx, role, uri):
cmd = command.CmdShowRolePermission(role, uri)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="arealm-role", help="show arealm-role association")
@click.argument("arealm")
@click.argument("role")
@click.pass_context
[docs]
def cmd_show_arealm_role(ctx, arealm, role):
cmd = command.CmdShowARealmRole(arealm, role)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="routercluster", help="show routercluster")
@click.argument("cluster", default="all")
@click.pass_context
[docs]
def cmd_show_routercluster(ctx, cluster):
cmd = command.CmdShowRouterCluster(cluster)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="routercluster-node", help="show routercluster node")
@click.argument("cluster")
@click.argument("node", default="all")
@click.pass_context
[docs]
def cmd_show_routercluster_node(ctx, cluster, node):
cmd = command.CmdShowRouterClusterNode(cluster, node)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="routercluster-workergroup", help="show routercluster workergroup")
@click.argument("cluster")
@click.argument("workergroup")
@click.pass_context
[docs]
def cmd_show_routercluster_workergroup(ctx, cluster, workergroup):
cmd = command.CmdShowRouterClusterWorkerGroup(cluster, workergroup)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="webcluster", help="show webcluster")
@click.argument("cluster", default="all")
@click.pass_context
[docs]
def cmd_show_webcluster(ctx, cluster):
cmd = command.CmdShowWebCluster(cluster)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="webcluster-node", help="show webcluster node")
@click.argument("cluster")
@click.argument("node", default="all")
@click.pass_context
[docs]
def cmd_show_webcluster_node(ctx, cluster, node):
cmd = command.CmdShowWebClusterNode(cluster, node)
ctx.obj.app.run_context(ctx, cmd)
ctx.obj.app.run_context(ctx, cmd)
@cmd_show.command(name="webcluster-service", help="show webcluster webservice")
@click.argument("cluster")
@click.argument("webservice", default=None)
@click.pass_context
[docs]
def cmd_show_webcluster_service(ctx, cluster, webservice):
cmd = command.CmdShowWebClusterWebService(cluster, webservice)
ctx.obj.app.run_context(ctx, cmd)
@cli.command(name="current", help="currently selected resource")
@click.pass_context
[docs]
def cmd_current(ctx):
_app.print_selected()
@cli.group(name="select", help="change current resource")
@click.pass_context
[docs]
def cmd_select(ctx):
pass
@cmd_select.command(name="node", help="change current node")
@click.argument("resource")
@click.pass_context
[docs]
def cmd_select_node(ctx, resource):
_app.current_resource_type = "node"
_app.current_resource = resource
_app.print_selected()
@cmd_select.command(name="worker", help="change current worker")
@click.argument("resource")
@click.pass_context
[docs]
def cmd_select_worker(ctx, resource):
_app.current_resource_type = "worker"
_app.current_resource = resource
_app.print_selected()
@cmd_select.command(name="transport", help="change current transport")
@click.argument("resource")
@click.pass_context
[docs]
def cmd_select_transport(ctx, resource):
_app.current_resource_type = "transport"
_app.current_resource = resource
_app.print_selected()
[docs]
def run():
"""
Main entry point into CLI.
"""
cli() # pylint: disable=E1120
if __name__ == "__main__":
run()