diff --git a/moniker/manage/base.py b/moniker/manage/base.py new file mode 100644 index 000000000..0545f4844 --- /dev/null +++ b/moniker/manage/base.py @@ -0,0 +1,83 @@ +# Copyright 2012 Managed I.T. +# +# Author: Kiall Mac Innes +# +# 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 abc +from cliff.command import Command as CliffCommand +from cliff.lister import Lister +from cliff.show import ShowOne +from moniker import utils +from moniker.context import MonikerContext + + +class Command(CliffCommand): + __metaclass__ = abc.ABCMeta + + def run(self, parsed_args): + self.context = MonikerContext.get_admin_context() + + return super(Command, self).run(parsed_args) + + @abc.abstractmethod + def execute(self, parsed_args): + """ + Execute something, this is since we overload self.take_action() + in order to format the data + + This method __NEEDS__ to be overloaded! + + :param parsed_args: The parsed args that are given by take_action() + """ + + def post_execute(self, data): + """ + Format the results locally if needed, by default we just return data + + :param data: Whatever is returned by self.execute() + """ + return data + + def take_action(self, parsed_args): + # TODO: Common Exception Handling Here + results = self.execute(parsed_args) + return self.post_execute(results) + + +class ListCommand(Command, Lister): + def post_execute(self, results): + if len(results) > 0: + columns = utils.get_columns(results) + data = [utils.get_item_properties(i, columns) for i in results] + return columns, data + else: + return [], () + + +class GetCommand(Command, ShowOne): + def post_execute(self, results): + return results.keys(), results.values() + + +class CreateCommand(Command, ShowOne): + def post_execute(self, results): + return results.keys(), results.values() + + +class UpdateCommand(Command, ShowOne): + def post_execute(self, results): + return results.keys(), results.values() + + +class DeleteCommand(Command): + pass diff --git a/moniker/manage/servers.py b/moniker/manage/servers.py new file mode 100644 index 000000000..c5a168035 --- /dev/null +++ b/moniker/manage/servers.py @@ -0,0 +1,115 @@ +# Copyright 2012 Managed I.T. +# +# Author: Kiall Mac Innes +# +# 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. +from moniker.openstack.common import log as logging +from moniker.manage import base +from moniker.central import api as central_api + +LOG = logging.getLogger(__name__) + + +class ListServersCommand(base.ListCommand): + """ List Servers """ + + def execute(self, parsed_args): + return central_api.get_servers(self.context) + + +class GetServerCommand(base.GetCommand): + """ Get Server """ + + def get_parser(self, prog_name): + parser = super(GetServerCommand, self).get_parser(prog_name) + + parser.add_argument('id', help="Server ID") + + return parser + + def execute(self, parsed_args): + return central_api.get_server(self.context, parsed_args.id) + + +class CreateServerCommand(base.CreateCommand): + """ Create Server """ + + def get_parser(self, prog_name): + parser = super(CreateServerCommand, self).get_parser(prog_name) + + parser.add_argument('--name', help="Server Name", required=True) + parser.add_argument('--ipv4', help="Server IPv4 Address") + parser.add_argument('--ipv6', help="Server IPv6 Address") + + return parser + + def execute(self, parsed_args): + server = dict( + name=parsed_args.name, + ipv4=parsed_args.ipv4, + ipv6=parsed_args.ipv6, + ) + + return central_api.create_server(self.context, server) + + +class UpdateServerCommand(base.UpdateCommand): + """ Update Server """ + + def get_parser(self, prog_name): + parser = super(UpdateServerCommand, self).get_parser(prog_name) + + parser.add_argument('id', help="Server ID") + parser.add_argument('--name', help="Server Name") + + ipv4_group = parser.add_mutually_exclusive_group() + ipv4_group.add_argument('--ipv4', help="Server IPv4 Address") + ipv4_group.add_argument('--no-ipv4', action='store_true') + + ipv6_group = parser.add_mutually_exclusive_group() + ipv6_group.add_argument('--ipv6', help="Server IPv6 Address") + ipv6_group.add_argument('--no-ipv6', action='store_true') + + return parser + + def execute(self, parsed_args): + server = {} + + if parsed_args.name: + server['name'] = parsed_args.name + + if parsed_args.no_ipv4: + server['ipv4'] = None + elif parsed_args.ipv4: + server['ipv4'] = parsed_args.ipv4 + + if parsed_args.no_ipv6: + server['ipv6'] = None + elif parsed_args.ipv6: + server['ipv6'] = parsed_args.ipv6 + + return central_api.update_server(self.context, parsed_args.id, server) + + +class DeleteServerCommand(base.DeleteCommand): + """ Delete Server """ + + def get_parser(self, prog_name): + parser = super(DeleteServerCommand, self).get_parser(prog_name) + + parser.add_argument('id', help="Server ID") + + return parser + + def execute(self, parsed_args): + return central_api.delete_server(self.context, parsed_args.id) diff --git a/moniker/manage/tsigkeys.py b/moniker/manage/tsigkeys.py new file mode 100644 index 000000000..86c3e891c --- /dev/null +++ b/moniker/manage/tsigkeys.py @@ -0,0 +1,107 @@ +# Copyright 2012 Managed I.T. +# +# Author: Kiall Mac Innes +# +# 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. +from moniker.openstack.common import log as logging +from moniker.manage import base +from moniker.central import api as central_api + +LOG = logging.getLogger(__name__) + + +class ListTsigKeysCommand(base.ListCommand): + """ List TsigKeys """ + + def execute(self, parsed_args): + return central_api.get_tsigkeys(self.context) + + +class GetTsigKeyCommand(base.GetCommand): + """ Get TsigKey """ + + def get_parser(self, prog_name): + parser = super(GetTsigKeyCommand, self).get_parser(prog_name) + + parser.add_argument('id', help="TSIG Key ID") + + return parser + + def execute(self, parsed_args): + return central_api.get_tsigkey(self.context, parsed_args.id) + + +class CreateTsigKeyCommand(base.CreateCommand): + """ Create TsigKey """ + + def get_parser(self, prog_name): + parser = super(CreateTsigKeyCommand, self).get_parser(prog_name) + + parser.add_argument('--name', help="TSIG Key Name", required=True) + parser.add_argument('--algorithm', help="TSIG Key Algorithm", + required=True) + parser.add_argument('--secret', help="TSIG Key Secret", required=True) + + return parser + + def execute(self, parsed_args): + tsigkey = dict( + name=parsed_args.name, + algorithm=parsed_args.algorithm, + secret=parsed_args.secret, + ) + + return central_api.create_tsigkey(self.context, tsigkey) + + +class UpdateTsigKeyCommand(base.UpdateCommand): + """ Update TsigKey """ + + def get_parser(self, prog_name): + parser = super(UpdateTsigKeyCommand, self).get_parser(prog_name) + + parser.add_argument('id', help="TSIG Key ID") + parser.add_argument('--name', help="TSIG Key Name") + parser.add_argument('--algorithm', help="TSIG Key Algorithm") + parser.add_argument('--secret', help="TSIG Key Secret") + + return parser + + def execute(self, parsed_args): + tsigkey = {} + + if parsed_args.name: + tsigkey['name'] = parsed_args.name + + if parsed_args.algorithm: + tsigkey['algorithm'] = parsed_args.algorithm + + if parsed_args.secret: + tsigkey['secret'] = parsed_args.secret + + return central_api.update_tsigkey(self.context, parsed_args.id, + tsigkey) + + +class DeleteTsigKeyCommand(base.DeleteCommand): + """ Delete TsigKey """ + + def get_parser(self, prog_name): + parser = super(DeleteTsigKeyCommand, self).get_parser(prog_name) + + parser.add_argument('id', help="TSIG Key ID") + + return parser + + def execute(self, parsed_args): + return central_api.delete_tsigkey(self.context, parsed_args.id) diff --git a/moniker/utils.py b/moniker/utils.py index 493825ea4..32b219402 100644 --- a/moniker/utils.py +++ b/moniker/utils.py @@ -126,3 +126,49 @@ def execute(*cmd, **kw): run_as_root = kw.pop('run_as_root', True) return processutils.execute(*cmd, run_as_root=run_as_root, root_helper=root_helper, **kw) + + +def get_item_properties(item, fields, mixed_case_fields=[], formatters={}): + """Return a tuple containing the item properties. + + :param item: a single item resource (e.g. Server, Tenant, etc) + :param fields: tuple of strings with the desired field names + :param mixed_case_fields: tuple of field names to preserve case + :param formatters: dictionary mapping field names to callables + to format the values + """ + row = [] + + for field in fields: + if field in formatters: + row.append(formatters[field](item)) + else: + if field in mixed_case_fields: + field_name = field.replace(' ', '_') + else: + field_name = field.lower().replace(' ', '_') + if not hasattr(item, field_name) and \ + (isinstance(item, dict) and field_name in item): + data = item[field_name] + else: + data = getattr(item, field_name, '') + if data is None: + data = '' + row.append(data) + return tuple(row) + + +def get_columns(data): + """ + Some row's might have variable count of columns, ensure that we have the + same. + + :param data: Results in [{}, {]}] + """ + columns = set() + + def _seen(col): + columns.add(str(col)) + + map(lambda item: map(_seen, item.keys()), data) + return list(columns) diff --git a/setup.py b/setup.py index ae33bdec1..d510d092e 100755 --- a/setup.py +++ b/setup.py @@ -76,6 +76,18 @@ setup( database-sync = moniker.manage.database:SyncCommand powerdns database-init = moniker.manage.powerdns:DatabaseInitCommand powerdns database-sync = moniker.manage.powerdns:DatabaseSyncCommand + + server-list = moniker.manage.servers:ListServersCommand + server-get = moniker.manage.servers:GetServerCommand + server-create = moniker.manage.servers:CreateServerCommand + server-update = moniker.manage.servers:UpdateServerCommand + server-delete = moniker.manage.servers:DeleteServerCommand + + tsigkey-list = moniker.manage.tsigkeys:ListTsigKeysCommand + tsigkey-get = moniker.manage.tsigkeys:GetTsigKeyCommand + tsigkey-create = moniker.manage.tsigkeys:CreateTsigKeyCommand + tsigkey-update = moniker.manage.tsigkeys:UpdateTsigKeyCommand + tsigkey-delete = moniker.manage.tsigkeys:DeleteTsigKeyCommand """), classifiers=[ 'Development Status :: 3 - Alpha',