| #!/usr/bin/env ${PY_STRING} |
| |
| # |
| # Licensed to the Apache Software Foundation (ASF) undeugr one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you 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. |
| # |
| |
| from __future__ import unicode_literals |
| from __future__ import division |
| from __future__ import absolute_import |
| from __future__ import print_function |
| |
| import sys, json, re |
| import qpid_dispatch_site |
| import argparse |
| try: |
| from collections.abc import Mapping, Sequence |
| except ImportError: |
| from collections import Mapping, Sequence |
| |
| from qpid_dispatch.management.client import Node, Url |
| from qpid_dispatch_internal.tools.command import (UsageError, _qdmanage_parser, check_args, |
| main, opts_ssl_domain, opts_url, opts_sasl) |
| from qpid_dispatch_internal.management.qdrouter import QdSchema |
| from qpid_dispatch_internal.compat import PY_TEXT_TYPE |
| from qpid_dispatch_internal.compat import PY_STRING_TYPE |
| |
| INTEGER_TYPE = "integer" |
| |
| def attr_split(attrstr, qd_schema, type): |
| """Split an attribute string of the form name=value or name to indicate None""" |
| nv = attrstr.split("=", 1) |
| if len(nv) == 1: |
| return [nv[0], None] |
| else: |
| if nv[1] == "true": |
| nv[1] = True |
| elif nv[1] == "false": |
| nv[1] = False |
| elif type and qd_schema.entity_type(type) and qd_schema.entity_type(type).attribute(nv[0]).type == INTEGER_TYPE: |
| nv[1] = int(nv[1]) |
| |
| return nv |
| |
| |
| class QdManage(): |
| def __init__(self): |
| self.qd_schema = QdSchema() |
| self.prefix = 'org.apache.qpid.dispatch.' |
| self.operations = ['QUERY', 'CREATE', 'READ', 'UPDATE', 'DELETE', |
| 'GET-TYPES', 'GET-OPERATIONS', 'GET-ATTRIBUTES', 'GET-ANNOTATIONS', |
| 'GET-MGMT-NODES', 'GET-SCHEMA', 'GET-LOG'] |
| self.op = _qdmanage_parser(self.operations) |
| |
| def clean_opts(self): |
| attr_type = self.opts.type |
| if attr_type: |
| self.opts.type = self.long_type(attr_type) |
| |
| def run(self, argv): |
| # Make all args unicode to avoid encoding arg values as AMQP bytes. |
| al = [x.decode() if not isinstance(x, PY_TEXT_TYPE) else x |
| for x in argv[1:]] |
| self.opts, self.args = self.op.parse_known_args(al) |
| self.clean_opts() |
| if self.opts.indent == -1: |
| self.opts.indent = None |
| if len(self.args) == 0: |
| raise UsageError("No operation specified") |
| self.node = Node.connect(opts_url(self.opts), self.opts.router, self.opts.timeout, |
| opts_ssl_domain(self.opts), |
| opts_sasl(self.opts)) |
| |
| operation = self.args.pop(0) |
| method = operation.lower().replace('-','_') |
| if operation.upper() in self.operations and hasattr(self, method): |
| getattr(self, method)() # Built-in operation |
| else: |
| self.operation(operation) # Custom operation |
| |
| def main(self, argv): |
| return main(self.run, argv, self.op) |
| |
| def print_json(self, data): |
| """Print data as JSON""" |
| print("%s" % json.dumps(data, indent=self.opts.indent)) |
| |
| def print_result(self, result): |
| """Print a string result as-is, else try json dump, else print as-is""" |
| if not result: return |
| if isinstance(result, (PY_STRING_TYPE, PY_TEXT_TYPE)): |
| print("%s" % result) |
| else: |
| try: |
| self.print_json(result) |
| except ValueError: |
| print("%s" % result) |
| |
| def long_type(self, type): |
| if not type or "." in type: |
| return type |
| |
| if type in ('link', 'node'): |
| return self.prefix + "router." + type |
| |
| if type in ('linkRoute', 'address', 'autoLink'): |
| return self.prefix + "router.config." + type |
| |
| return self.prefix + type |
| |
| def call_node(self, method, *argnames, **kwargs): |
| """Call method on node, use opts named in argnames""" |
| names = set(argnames) |
| |
| attributes = kwargs.get('attributes') |
| if attributes and attributes.get('type'): |
| attributes['type'] = self.long_type(attributes['type']) |
| |
| for k in self.opts.__dict__: |
| if k in names and hasattr(self.opts, k): |
| kwargs[k] = getattr(self.opts, k) |
| |
| return getattr(self.node, method)(**kwargs) |
| |
| def call_bulk(self, func): |
| """Call function for attributes from stdin or --attributes option""" |
| if self.opts.stdin: |
| data = json.load(sys.stdin) |
| if isinstance(data, Mapping): |
| self.print_json(func(data).attributes) |
| elif isinstance(data, Sequence): |
| self.print_json([func(attrs).attributes for attrs in data]) |
| else: raise ValueError("stdin is not a JSON map or list") |
| else: |
| self.print_json(func(self.opts.attributes).attributes) |
| |
| def query(self): |
| """query [ATTR...] Print attributes of entities.""" |
| if self.args: |
| self.opts.attribute_names = self.args |
| result = self.call_node('query', 'type', 'attribute_names') |
| self.print_json(result.get_dicts(clean=True)) |
| |
| def create(self): |
| """create [ATTR=VALUE...] Create a new entity.""" |
| if self.args: |
| self.opts.attributes = dict(attr_split(arg, self.qd_schema, self.opts.type) for arg in self.args) |
| self.call_bulk(lambda attrs: self.call_node('create', 'type', 'name', attributes=attrs)) |
| |
| def read(self): |
| """read Print attributes of selected entity.""" |
| check_args(self.args, 0) |
| self.print_json(self.call_node('read', 'type', 'name', 'identity').attributes) |
| |
| def update(self): |
| """update [ATTR=VALUE...] Update an entity.""" |
| if self.args: |
| self.opts.attributes = dict(attr_split(arg, self.qd_schema, self.opts.type) for arg in self.args) |
| self.call_bulk( |
| lambda attrs: self.call_node('update', 'type', 'name', 'identity', attributes=attrs)) |
| |
| def delete(self): |
| """delete Delete an entity""" |
| check_args(self.args, 0) |
| self.call_node('delete', 'type', 'name', 'identity') |
| |
| def get_types(self): |
| """get-types [TYPE] List entity types with their base types.""" |
| if not self.opts.type: |
| self.opts.type = check_args(self.args, 1)[0] |
| self.print_json(self.call_node('get_types', 'type')) |
| |
| def get_annotations(self): |
| """get-annotations [TYPE] List entity types with the annotations they implement.""" |
| if not self.opts.type: |
| self.opts.type = check_args(self.args, 1)[0] |
| self.print_json(self.call_node('get_annotations', 'type')) |
| |
| def get_attributes(self): |
| """get-attributes [TYPE] List entity types with their attributes.""" |
| if not self.opts.type: |
| self.opts.type = check_args(self.args, 1)[0] |
| self.print_json(self.call_node('get_attributes', 'type')) |
| |
| def get_operations(self): |
| """get-operations [TYPE] List entity types with their operations.""" |
| if not self.opts.type: |
| self.opts.type = check_args(self.args, 1)[0] |
| self.print_json(self.call_node('get_operations', 'type')) |
| |
| def get_mgmt_nodes(self): |
| """get-mgmt-nodes List of other known management nodes""" |
| check_args(self.args, 0) |
| self.print_json(self.call_node('get_mgmt_nodes')) |
| |
| def json_arg(self, value): |
| try: |
| return json.loads(value) |
| except ValueError as e: |
| if not re.search(r'["{}\[\]]', value): # Doesn't look like attempted JSON. |
| return value # Just treat as plain string |
| raise ValueError("Invalid JSON '%s': %s" % (value, e)) |
| |
| def operation(self, operation): |
| """operation [ATTR=VALUE...] Call custom operation with ATTR=VALUE as request properties. Use --body and --properties if specified.""" |
| properties = dict(attr_split(arg, self.qd_schema, self.opts.type) for arg in self.args or []) |
| if self.opts.properties: |
| properties.update(self.json_arg(self.opts.properties)) |
| body = None |
| if self.opts.body: |
| body = self.json_arg(self.opts.body) |
| request = self.call_node('request', 'type', 'name', 'identity', operation=operation, body=body, **properties) |
| self.print_result(self.node.call(request).body) |
| |
| if __name__ == "__main__": |
| sys.exit(QdManage().main(sys.argv)) |