class Registrar():
    def register(self, dispatch): class DispatchSREG(DNSDispatch):
    object_url = "/en-US/core/api/v1_core/{1}/{2}/"
    object_list_url = "/en-US/core/api/v1_core/{1}/"
    resource_name = 'staticreg'
    dtype = 'SREG'
    dgroup = 'dns'
    ip_type = '4'  # Used for testing

    common_args = [
        fqdn_argument('fqdn', dtype),
        ttl_argument('ttl'),
        ip_argument('ip_str', ip_type),
        view_arguments('views'),
        description_argument('description'),
        comment_argument('comment')
    ]

    update_args = common_args + [
        name_argument('name', prefix='nic'),
        update_pk_argument('pk', dtype)
    ]

    create_args = common_args + [
        system_argument('system_hostname'),
    ]

    delete_args = [
        delete_pk_argument('pk', dtype)
    ]

    detail_args = [detail_pk_argument('pk', dtype)]

    def get_create_data(self, nas):
        data = super(DispatchSREG, self).get_create_data(nas)
        return set_ip_type('ip_str', data)

    def get_update_data(self, nas):
        data = super(DispatchSREG, self).get_update_data(nas)
        return set_ip_type('ip_str', data)

    def format_hwadapters(self, hws):
        # This is never called if p_json is true
        resp_list = []
        for i, hw in enumerate(hws):
            for k, v in hw.iteritems():
                resp_list.append("\t{0} | {1}: {2}".format(i, k, v))
        return resp_list

    def format_response(self, nas, resp_msg, user_msg):
        # Override this so we can display hwadapters better
        resp_list = []
        if nas.p_json:
            resp_list.append(json.dumps(resp_msg, indent=2))
        else:
            resp_list.append(user_msg)
            for k, v in resp_msg.iteritems():
                if k == 'hwadapter_set':
                    continue  # handle these last
                    # indent these
                else:
                    resp_list.append("{0}: {1}".format(k, v))
            if resp_msg.get('hwadapter_set', None):
                resp_list.append("Hardware Adapters: {0}".format('-' * 20))
                resp_list += self.format_hwadapters(resp_msg['hwadapter_set'])
        return resp_list class DispatchHW(ObjectDispatch):
    object_url = "/en-US/core/api/v1_core/{1}/{2}/"
    object_list_url = "/en-US/core/api/v1_core/{1}/"
    resource_name = 'hwadapter'
    dtype = 'HW'
    dgroup = 'dhcp'

    common_args = [
        mac_argument('mac'),
        group_argument('group'),
        enable_dhcp_argument('enable_dhcp'),
        description_argument('description'),
        comment_argument('comment')
    ]

    update_args = common_args + [
        name_argument('name', prefix='hw'),
        update_pk_argument('pk', dtype)
    ]

    create_args = common_args + [
        sreg_argument('sreg'),
    ]

    delete_args = [
        delete_pk_argument('pk', dtype)
    ]

    detail_args = [detail_pk_argument('pk', dtype)]

    def format_response(self, nas, resp_msg, user_msg):
        resp_list = []
        if nas.p_json:
            resp_list.append(json.dumps(resp_msg, indent=2))
        else:
            resp_list.append(user_msg)
            for k, v in resp_msg.iteritems():
                resp_list.append("{0}: {1}".format(k, v))
        return resp_list def call_to_json(command_str):
    """
    Given a string, this function will shell out, execute the command
    and parse the json returned by that command
    """
    print command_str
    p = subprocess.Popen(shlex.split(command_str),
                         stderr=subprocess.PIPE, stdout=subprocess.PIPE)
    stdout, stderr = p.communicate()
    if stderr:
        return None, stderr, p.returncode

    stdout = stdout.replace('u\'', '"').replace('\'', '"').strip('\n')
    try:
        return json.loads(stdout, 'unicode'), None, p.returncode
    except json.decoder.JSONDecodeError, e:
        return (None,
                "Ret was: {0}. Got error: {1}".format(stdout, str(e)),
                p.returncode) def test_method_to_params(test_case):
    if not test_case:
        return ''
    elif not test_case[0]:
        return test_case[1]
    else:
        return "--{0} {1}".format(*test_case) EXEC = "./inv --json" class DispatchKV(Dispatch):
    def route(self, nas):
        if self.dtype.lower() == nas.dtype.lower():
            return getattr(self, nas.action.lower())(nas)

    def build_parser(self, base_parser):
        record_base_parser = base_parser.add_parser(
            self.dtype,
            help="The interface for CRUDing {0} Key Value "
            "pairs".format(self.dtype),
            add_help=True
        )
        action_parser = record_base_parser.add_subparsers(
            help="{0} record actions".format(self.dtype),
            dest='action'
        )
        build_create_parser(
            self, action_parser, help="Create a {0} KV pair".format(self.dtype)
        )
        build_update_parser(
            self, action_parser, help="Update a {0} KV pair".format(self.dtype)
        )
        build_delete_parser(
            self, action_parser, help="Delete a {0} KV pair".format(self.dtype)
        )
        build_detail_parser(
            self, action_parser, help="Detail a {0} KV pair".format(self.dtype)
        )
        build_kvlist_parser(self, action_parser)

    def action(self, nas, url, method, data, **kwargs):
        kwargs.update(**{'form_encode': False})
        return super(DispatchKV, self).action(nas, url, method, data, **kwargs)

    def update(self, nas):
        # We have to override this because the default update uses PATCH, we
        # need to use POST because this dispatch isn't for a tastypie endpoint
        # and doesn't support PATCH yet.
        data = self.get_update_data(nas)  # Dispatch defined Hook
        url = "{0}{1}".format(REMOTE, self.update_url(nas))
        return self.action(nas, url, requests.post, data)

    def format_response(self, nas, resp_msg, user_msg):
        resp_list = []
        if nas.p_json:
            resp_list.append(json.dumps(resp_msg, indent=2))
        else:
            resp_list.append(user_msg)
            for k, v in resp_msg.iteritems():
                if k == 'kvs':  # process these last
                    continue
                resp_list.append("{0}: {1}".format(k, v))

            if 'kvs' in resp_msg:
                resp_list += self.format_kvs(resp_msg['kvs'], resp_list)

        return resp_list

    def format_kvs(self, bundles, resp_list):
        resp_list = []
        for i, bundle in enumerate(bundles):
            for ikey, ivalue in bundle.iteritems():
                resp_list.append("{0} {1}: {2}".format(
                    '-' if i % 2 else '=', ikey, ivalue
                ))
        return resp_list

    def list(self, nas):
        url = "{0}{1}?format=json".format(REMOTE, self.kvlist_url(nas))
        return self.action(nas, url, requests.get, {})

    def create_url(self, nas):
        return '/en-US/core/keyvalue/api/{kv_class}/{obj_pk}/create/'.format(
            **{'kv_class': self.kv_class, 'obj_pk': nas.obj_pk}
        )

    def detail_url(self, nas):
        return '/en-US/core/keyvalue/api/{kv_class}/{kv_pk}/detail/'.format(
            **{'kv_class': self.kv_class, 'kv_pk': nas.kv_pk}
        )

    def kvlist_url(self, nas):
        return '/en-US/core/keyvalue/api/{kv_class}/{obj_pk}/list/'.format(
            **{'kv_class': self.kv_class, 'obj_pk': nas.obj_pk}
        )

    def update_url(self, nas):
        return '/en-US/core/keyvalue/api/{kv_class}/{kv_pk}/update/'.format(
            **{'kv_class': self.kv_class, 'kv_pk': nas.kv_pk}
        )

    def delete_url(self, nas):
        return '/en-US/core/keyvalue/api/{kv_class}/{kv_pk}/delete/'.format(
            **{'kv_class': self.kv_class, 'kv_pk': nas.kv_pk}
        ) class TestKVSetupMixin(object):
    def do_setup(self, DispatchType, test_case):
        """Create an object and return it's pk so the KV api can test itself"""
        command = [EXEC, DispatchType.dtype, 'create']
        for add_arg, extract_arg, tm in DispatchType.create_args:
            command.append(test_method_to_params(tm()))
        command_str = ' '.join(command)
        ret, errors, rc = call_to_json(command_str)
        if errors:
            test_case.fail(errors)
        return ret['pk'] def key_argument(field_name):
    def add_key_argument(parser, required=True):
        parser.add_argument(
            '--key/-k', default=None, type=str, dest='key',
            help="The key.", required=required
        )

    def test_data():
        return 'key', 'testkey'

    return add_key_argument, build_extractor(field_name, 'key'), test_data def value_argument(field_name):
    def add_value_argument(parser, required=True):
        parser.add_argument(
            '--value/-v', default=None, type=str, dest='value',
            help="The value.", required=required
        )

    def test_data():
        return 'value', 'testvalue'

    return add_value_argument, build_extractor(field_name, 'value'), test_data def update_pk_argument(field_name, dtype):
    display_name = field_name.replace('_', '-')

    def add_update_pk_argument(parser, **kwargs):
        parser.add_argument(
            '--{0}'.format(display_name), required=True,
            default=None, type=int, dest=field_name, help="The database id of "
            "the {0} KV being updated.".format(dtype)
        )
        return parser

    def _extract_pk(nas):
        return extract_pk(nas, field_name)

    def test_data():
        # kv_pk will need to be evaluated in at test time
        return display_name, '{{ kv_pk }}'

    return add_update_pk_argument, _extract_pk, test_data def delete_pk_argument(field_name, dtype):
    display_name = field_name.replace('_', '-')

    # Required has no affect.
    def add_delete_pk_argument(parser, **kwargs):
        parser.add_argument(
            '--{0}'.format(display_name), default=None,
            type=int, dest=field_name, help="Delete the Key Value pair with "
            "the corresponding database primary key.".format(dtype),
            required=True
        )
        return parser

    def _extract_pk(nas):
        return extract_pk(nas, field_name)

    def test_data():
        # kv_pk will need to be evaluated in at test time
        return display_name, '{{ kv_pk }}'

    return add_delete_pk_argument, _extract_pk, test_data def detail_pk_argument(field_name, dtype):
    display_name = field_name.replace('_', '-')

    def add_detail_pk_argument(parser, **kwargs):
        parser.add_argument(
            '--{0}'.format(display_name), required=True,
            default=None, type=int, dest=field_name, help="database id of the "
            "Key Value.".format(dtype)
        )
        return parser

    def _extract_pk(nas):
        return extract_pk(nas, field_name)

    def test_data():
        # kv_pk will need to be evaluated in at test time
        return display_name, '{{ kv_pk }}'

    return add_detail_pk_argument, _extract_pk, test_data def kvlist_pk_argument(field_name, dtype):
    display_name = field_name.replace('_', '-')

    def add_detail_pk_argument(parser, **kwargs):
        parser.add_argument(
            '--{0}'.format(display_name), required=True,
            default=None, type=int, dest=field_name, help="database id of the "
            "object who's Key Value " "store will be returned.".format(dtype)
        )
        return parser

    def _extract_pk(nas):
        return extract_pk(nas, field_name)

    def test_data():
        # obj_pk will need to be evaluated in at test time
        return display_name, '{{ obj_pk }}'

    return add_detail_pk_argument, _extract_pk, test_data def create_pk_argument(field_name, dtype):
    display_name = field_name.replace('_', '-')

    def add_update_pk_argument(parser, **kwargs):
        parser.add_argument(
            '--{0}'.format(display_name), required=True,
            default=None, type=int, dest=field_name, help="The Database id of "
            "the object that the new Key Value pair is for.".format(dtype)
        )
        return parser

    def _extract_pk(nas):
        return extract_pk(nas, field_name)

    def test_data():
        # kv_pk will need to be evaluated in at test time
        return display_name, '{{ obj_pk }}'

    return add_update_pk_argument, _extract_pk, test_data
from invtool.lib.registrar import registrar
from invtool.sreg_dispatch import DispatchSREG, DispatchHW
from invtool.tests.utils import call_to_json, test_method_to_params, EXEC
from invtool.kv.kv_dispatch import DispatchKV
from invtool.tests.utils import TestKVSetupMixin
from invtool.lib.kv_options import (
    key_argument, value_argument, update_pk_argument,
    delete_pk_argument, detail_pk_argument, kvlist_pk_argument,
    create_pk_argument
)



class StaticRegKV(DispatchKV, TestKVSetupMixin):
    kv_class = 'staticregkeyvalue'
    dtype = 'SREG_kv'
    dgroup = 'kv'
    create_args = [
        key_argument('key'),
        value_argument('value'),
        create_pk_argument('obj_pk', dtype)
    ]

    update_args = [
        key_argument('key'),
        value_argument('value'),
        update_pk_argument('kv_pk', dtype)
    ]

    delete_args = [
        delete_pk_argument('kv_pk', dtype)
    ]

    detail_args = [detail_pk_argument('kv_pk', dtype)]

    kvlist_args = [kvlist_pk_argument('obj_pk', dtype)]

    def do_test_setup(self, test_case):
        return self.do_setup(DispatchSREG, test_case)


class HWAdapterKV(DispatchKV, TestKVSetupMixin):
    kv_class = 'hwadapterkeyvalue'
    dtype = 'HW_kv'
    dgroup = 'kv'
    create_args = [
        key_argument('key'),
        value_argument('value'),
        create_pk_argument('obj_pk', dtype)
    ]
    update_args = [
        key_argument('key'),
        value_argument('value'),
        update_pk_argument('kv_pk', dtype)
    ]

    delete_args = [
        delete_pk_argument('kv_pk', dtype)
    ]

    detail_args = [detail_pk_argument('kv_pk', dtype)]

    kvlist_args = [kvlist_pk_argument('obj_pk', dtype)]

    def do_test_setup(self, test_case):
        sreg_pk = self.do_setup(DispatchSREG, test_case)

        # Create a HW

        command = [EXEC, DispatchHW.dtype, 'create']