Пример #1
0
    def __init__(self):

        _load_params()
        # define user inputs from playbook
        self.module_arg_spec = dict(
            resource_group=dict(type='str', required=True),
            relative_name=dict(type='str', required=True),
            zone_name=dict(type='str', required=True),
            record_type=dict(choices=RECORD_ARGSPECS.keys(),
                             required=True,
                             type='str'),
            record_mode=dict(choices=['append', 'purge'], default='purge'),
            state=dict(choices=['present', 'absent'],
                       default='present',
                       type='str'),
            time_to_live=dict(type='int', default=3600),
            records=dict(type='list', elements='dict'))

        required_if = [('state', 'present', ['records'])]

        self.results = dict(changed=False)

        # Argument validation
        super(AzureRMPrivateDNSRecordSet,
              self).__init__(self.module_arg_spec,
                             required_if=required_if,
                             supports_check_mode=True,
                             skip_exec=True)

        # check the subspec and metadata
        record_subspec = RECORD_ARGSPECS.get(self.module.params['record_type'])

        # patch the right record shape onto the argspec
        self.module_arg_spec['records']['options'] = record_subspec

        self.resource_group = None
        self.relative_name = None
        self.zone_name = None
        self.record_type = None
        self.record_mode = None
        self.state = None
        self.time_to_live = None
        self.records = None

        # rerun validation and module
        super(AzureRMPrivateDNSRecordSet,
              self).__init__(self.module_arg_spec,
                             required_if=required_if,
                             supports_check_mode=True)
    def __init__(self):

        # we're doing two-pass arg validation, sample and store the args internally to allow this
        _load_params()

        self.module_arg_spec = dict(
            resource_group=dict(type='str', required=True),
            relative_name=dict(type='str', required=True),
            zone_name=dict(type='str', required=True),
            record_type=dict(choices=RECORD_ARGSPECS.keys(),
                             required=True,
                             type='str'),
            record_mode=dict(choices=['append', 'purge'], default='purge'),
            state=dict(choices=['present', 'absent'],
                       default='present',
                       type='str'),
            time_to_live=dict(type='int', default=3600),
            records=dict(type='list', elements='dict'))

        required_if = [('state', 'present', ['records'])]

        self.results = dict(changed=False)

        # first-pass arg validation so we can get the record type- skip exec_module
        super(AzureRMRecordSet, self).__init__(self.module_arg_spec,
                                               required_if=required_if,
                                               supports_check_mode=True,
                                               skip_exec=True)

        # look up the right subspec and metadata
        record_subspec = RECORD_ARGSPECS.get(self.module.params['record_type'])

        # patch the right record shape onto the argspec
        self.module_arg_spec['records']['options'] = record_subspec

        self.resource_group = None
        self.relative_name = None
        self.zone_name = None
        self.record_type = None
        self.record_mode = None
        self.state = None
        self.time_to_live = None
        self.records = None

        # rerun validation and actually run the module this time
        super(AzureRMRecordSet, self).__init__(self.module_arg_spec,
                                               required_if=required_if,
                                               supports_check_mode=True)
Пример #3
0
def do_echo():
    p = _load_params()
    d = json.loads(basic._ANSIBLE_ARGS)
    d['ANSIBLE_MODULE_ARGS'] = {}
    basic._ANSIBLE_ARGS = json.dumps(d).encode('utf-8')
    module = AnsibleModule(argument_spec={})
    module.exit_json(args_in=p)
Пример #4
0
    def _pre_init(self, object_type):
        """To know the arguments, they must be loaded from the schema. This is
        only possible once the client has been initialized (otherwise the
        opsview version is not known). This function gathers the connection
        arguments before the module is initialized so that the argument spec
        can be set.
        It also gets a list of all parameters which have been specified so that
        we can differentiate between null parameters and omitted parameters.
        """
        # Internal Ansible function to load the arguments
        pre_params = _load_params()

        login_params = {}
        for key in six.iterkeys(self.login_argument_spec):
            if key not in pre_params or not pre_params[key]:
                raise ValueError('Argument [%s] is required' % key)

            login_params[key] = pre_params[key]

        # Save a list of all the specified parameters so we know which null
        # parameters to remove
        self.specified_params = [k for k in six.iterkeys(pre_params)
                                 if not k.startswith('_')]

        # Save the manager as, for example `client.config.hostgroups`
        client = OpsviewClient(**login_params)
        self.manager = getattr(client.config, object_type)

        # Build the argument spec with the necessary values
        self.argspec = self._build_argspec()
        self.argspec.update(self.login_argument_spec)
        self.argspec.update(self.additional_argument_spec)
Пример #5
0
    def resource_factory(cls):
        params = basic._load_params()
        storage_data = cls._pop_param(common.STORAGE_DATA)

        specs = {
            'resource': {
                'choices': list(cls.RESOURCES.keys())
            },
            'provider': {
                'type': 'str',
                'choices': ['cinderlib', 'cinderclient']
            },
            'backend': {
                'type': 'str'
            }
        }

        resource = params.get('resource')
        resource_class = cls.RESOURCES.get(resource)
        if resource_class and resource_class.STATES:
            specs['state'] = {
                'choices': resource_class.STATES,
                'default': resource_class.DEFAULT_STATE
            }

        module = basic.AnsibleModule(specs,
                                     check_invalid_arguments=False,
                                     supports_check_mode=False)
        resource = cls.RESOURCES[resource](module, storage_data)
        return resource
Пример #6
0
def check_galaxy_version(schema):
    params = _load_params()
    params_keys = list(params.keys())
    if 'method' in params_keys and 'method' not in schema:
        error_message = 'Legacy playbook detected, please revise the playbook or install latest legacy'
        error_message += ' fortimanager galaxy collection: #ansible-galaxy collection install -f fortinet.fortimanager:1.0.5'
        sys.stderr.write(error_message)
        sys.exit(1)
Пример #7
0
def check_legacy_fortiosapi():
    params = _load_params()
    legacy_schemas = ['host', 'username', 'password', 'ssl_verify', 'https']
    legacy_params = []
    for param in legacy_schemas:
        if param in params:
            legacy_params.append(param)
    if len(legacy_params):
        error_message = 'Legacy fortiosapi parameters %s detected, please use HTTPAPI instead!' % (str(legacy_params))
        sys.stderr.write(error_message)
        sys.exit(1)
Пример #8
0
    def __init__(self):

        # we're doing two-pass arg validation, sample and store the args internally to allow this
        _load_params()

        self.module_arg_spec = dict(
            resource_group=dict(type='str', required=True),
            relative_name=dict(type='str', required=True),
            zone_name=dict(type='str', required=True),
            record_type=dict(choices=RECORD_ARGSPECS.keys(), required=True, type='str'),
            record_mode=dict(choices=['append', 'purge'], default='purge'),
            state=dict(choices=['present', 'absent'], default='present', type='str'),
            time_to_live=dict(type='int', default=3600),
            records=dict(type='list', elements='dict')
        )

        required_if = [
            ('state', 'present', ['records'])
        ]

        self.results = dict(
            changed=False
        )

        # first-pass arg validation so we can get the record type- skip exec_module
        super(AzureRMRecordSet, self).__init__(self.module_arg_spec, required_if=required_if, supports_check_mode=True, skip_exec=True)

        # look up the right subspec and metadata
        record_subspec = RECORD_ARGSPECS.get(self.module.params['record_type'])
        self.record_type_metadata = RECORDSET_VALUE_MAP.get(self.module.params['record_type'])

        # patch the right record shape onto the argspec
        self.module_arg_spec['records']['options'] = record_subspec

        # monkeypatch __hash__ on SDK model objects so we can safely use them in sets
        for rvm in RECORDSET_VALUE_MAP.values():
            rvm['classobj'].__hash__ = gethash

        # rerun validation and actually run the module this time
        super(AzureRMRecordSet, self).__init__(self.module_arg_spec, required_if=required_if, supports_check_mode=True)
Пример #9
0
def check_parameter_bypass(schema, module_level2_name):
    params = _load_params()
    if 'bypass_validation' in params and params['bypass_validation'] is True:
        top_level_schema = dict()
        for key in schema:
            if key != module_level2_name:
                top_level_schema[key] = schema[key]
            else:
                top_level_schema[module_level2_name] = dict()
                top_level_schema[module_level2_name]['required'] = False
                top_level_schema[module_level2_name]['type'] = 'dict'
        return top_level_schema
    return schema
def new_module(argspec, always_include=_MODULE_ONLY_FIELDS):
    """Initializes a new AnsibleModule with the specified argument spec,
    removing any fields from the final params unless they've been explicitly
    specified.

    Keys in `always_include` will never be omitted.
    """
    pre_params = copy.deepcopy(_load_params())
    module = AnsibleModule(supports_check_mode=True, argument_spec=argspec)

    # dict(...) creates a shallow copy to prevent changed size during iter
    for (k, v) in iteritems(dict(module.params)):
        if k not in pre_params and v is None and k not in always_include:
            del module.params[k]

    return module
Пример #11
0
def init_module():
    # Load the parameters before they are parsed to identify which parameters
    # were actually specified so that values which were omitted can be removed
    # from the payload. Makes a deep copy to be 100% that the structure doesn't
    # get changed externally
    initial_params = copy.deepcopy(_load_params())

    global module
    module = AnsibleModule(supports_check_mode=True, argument_spec=ARG_SPEC)

    # dict(...) creates a shallow copy to prevent changing size during iter
    for (key, value) in six.iteritems(dict(module.params)):
        if key not in initial_params and value is None and \
                key not in NON_OBJECT_FIELDS:
            del module.params[key]

    return module
 def __init__(self, *args, **kwargs):
     super(RouteTableHelperCustom, self).__init__(*args, **kwargs)
     try:
         # since the purge and delete options have default values we cannot check directly if a user has provided
         # value for it or not. _load_params returns only the params that user has provided.
         user_provided_params = _load_params()
         if (user_provided_params.get("purge_route_rules") is not None
                 and user_provided_params.get("delete_route_rules")
                 is not None):
             self.module.fail_json(
                 msg=
                 "purge_route_rules and delete_route_rules are mutually exclusive"
             )
     except Exception as ex:
         logger.debug(
             "Error checking the load params for purge and delete validation: {0}. Skipping validation."
             .format(str(ex)))
         pass
def check_parameter_bypass(schema, module_level2_name):
    params = _load_params()
    if 'bypass_validation' in params and params['bypass_validation'] is True:
        top_level_schema = dict()
        for key in schema:
            if key != module_level2_name:
                top_level_schema[key] = schema[key]
            elif not params[module_level2_name] or type(params[module_level2_name]) is dict:
                top_level_schema[module_level2_name] = dict()
                top_level_schema[module_level2_name]['required'] = False
                top_level_schema[module_level2_name]['type'] = 'dict'
            elif type(params[module_level2_name]) is list:
                top_level_schema[module_level2_name] = dict()
                top_level_schema[module_level2_name]['required'] = False
                top_level_schema[module_level2_name]['type'] = 'list'
            else:
                raise Exception('Value of %s must be a dict or list' % (module_level2_name))
        return top_level_schema
    return schema
Пример #14
0
def main():

    # If ANSIBLE_VERSION is not defined we know that
    # we are using 2.1 and greater.  At least we hope so
    if 'ANSIBLE_VERSION' in globals():
        if int(ANSIBLE_VERSION[0]) < 2:
            module_args = MODULE_ARGS
        else:
            module_args = MODULE_COMPLEX_ARGS

        (base_url, model_template) = peek_params(module_args)
    else:
        module_args = _load_params()
        base_url = module_args['base_url']
        model_template = module_args['template']

    argument_spec, metadata_hash = create_argument_spec(base_url, model_template)
    module = AnsibleModule(argument_spec=argument_spec)

    HanlonModel(module, metadata_hash)
Пример #15
0
def main():

    # If ANSIBLE_VERSION is not defined we know that
    # we are using 2.1 and greater.  At least we hope so
    if 'ANSIBLE_VERSION' in globals():
        if int(ANSIBLE_VERSION[0]) < 2:
            module_args = MODULE_ARGS
        else:
            module_args = MODULE_COMPLEX_ARGS

        (base_url, model_template) = peek_params(module_args)
    else:
        module_args = _load_params()
        base_url = module_args['base_url']
        model_template = module_args['template']

    argument_spec, metadata_hash = create_argument_spec(
        base_url, model_template)
    module = AnsibleModule(argument_spec=argument_spec)

    HanlonModel(module, metadata_hash)
Пример #16
0
def main():

    # Create the module.
    argument_spec = dict(
        state=dict(type='str',
                   default='present',
                   choices=['present', 'absent']),
        api_endpoint=dict(type='str', required=True),
        api_authtype=dict(type='str',
                          required=True,
                          choices=['ibmcloud', 'basic']),
        api_key=dict(type='str', required=True, no_log=True),
        api_secret=dict(type='str', no_log=True),
        api_timeout=dict(type='int', default=60),
        api_token_endpoint=dict(
            type='str', default='https://iam.cloud.ibm.com/identity/token'),
        name=dict(type='str', required=True),
        msp_id=dict(type='str'),
        state_db=dict(type='str',
                      default='couchdb',
                      choices=['couchdb', 'leveldb']),
        certificate_authority=dict(type='raw'),
        enrollment_id=dict(type='str'),
        enrollment_secret=dict(type='str', no_log=True),
        admins=dict(type='list',
                    elements='str',
                    aliases=['admin_certificates']),
        config=dict(type='dict'),
        config_override=dict(type='dict', default=dict()),
        resources=dict(
            type='dict',
            default=dict(),
            options=dict(
                peer=dict(
                    type='dict',
                    default=dict(),
                    options=dict(requests=dict(
                        type='dict',
                        default=dict(),
                        options=dict(cpu=dict(type='str', default='200m'),
                                     memory=dict(type='str', default='1G'))))),
                proxy=dict(
                    type='dict',
                    default=dict(),
                    options=dict(requests=dict(
                        type='dict',
                        default=dict(),
                        options=dict(cpu=dict(type='str', default='100m'),
                                     memory=dict(type='str',
                                                 default='200M'))))),
                couchdb=dict(
                    type='dict',
                    default=dict(),
                    options=dict(requests=dict(
                        type='dict',
                        default=dict(),
                        options=dict(cpu=dict(type='str', default='200m'),
                                     memory=dict(type='str',
                                                 default='400M'))))),
                dind=dict(
                    type='dict',
                    default=dict(),
                    options=dict(requests=dict(
                        type='dict',
                        default=dict(),
                        options=dict(cpu=dict(type='str', default='1'),
                                     memory=dict(type='str', default='1G'))))),
                chaincodelauncher=dict(
                    type='dict',
                    default=dict(),
                    options=dict(requests=dict(
                        type='dict',
                        default=dict(),
                        options=dict(cpu=dict(type='str', default='200m'),
                                     memory=dict(type='str',
                                                 default='400M'))))))),
        storage=dict(type='dict',
                     default=dict(),
                     options=dict(peer=dict(type='dict',
                                            default=dict(),
                                            options={
                                                'size':
                                                dict(type='str',
                                                     default='100Gi'),
                                                'class':
                                                dict(type='str',
                                                     default='default')
                                            }),
                                  statedb=dict(type='dict',
                                               default=dict(),
                                               options={
                                                   'size':
                                                   dict(type='str',
                                                        default='100Gi'),
                                                   'class':
                                                   dict(type='str',
                                                        default='default')
                                               }))),
        hsm=dict(type='dict',
                 options=dict(pkcs11endpoint=dict(type='str', required=True),
                              label=dict(type='str',
                                         required=True,
                                         no_log=True),
                              pin=dict(type='str', required=True,
                                       no_log=True))),
        zone=dict(type='str'),
        version=dict(type='str'),
        wait_timeout=dict(type='int', default=60))
    required_if = [('api_authtype', 'basic', ['api_secret']),
                   ('state', 'present', ['name', 'msp_id'])]
    # Ansible doesn't allow us to say "require one of X and Y only if condition A is true",
    # so we need to handle this ourselves by seeing what was passed in.
    actual_params = _load_params()
    if actual_params.get('state', 'present') == 'present':
        required_one_of = [['certificate_authority', 'config']]
    else:
        required_one_of = []
    required_together = [['certificate_authority', 'enrollment_id'],
                         ['certificate_authority', 'enrollment_secret'],
                         ['certificate_authority', 'admins']]
    mutually_exclusive = [['certificate_authority', 'config']]
    module = BlockchainModule(argument_spec=argument_spec,
                              supports_check_mode=True,
                              required_if=required_if,
                              required_one_of=required_one_of,
                              required_together=required_together,
                              mutually_exclusive=mutually_exclusive)

    # Ensure all exceptions are caught.
    try:

        # Log in to the IBP console.
        console = get_console(module)

        # Determine if the peer exists.
        name = module.params['name']
        peer = console.get_component_by_display_name(
            name, deployment_attrs='included')
        peer_exists = peer is not None
        peer_corrupt = peer is not None and 'deployment_attrs_missing' in peer

        # If this is a free cluster, we cannot accept resource/storage configuration,
        # as these are ignored for free clusters. We must also delete the defaults,
        # otherwise they cause a mismatch with the values that actually get set.
        if console.is_free_cluster():
            actual_params = _load_params()
            if 'resources' in actual_params or 'storage' in actual_params:
                raise Exception(
                    'Cannot specify resources or storage for a free IBM Kubernetes Service cluster'
                )
            if peer_exists:
                module.params['resources'] = dict()
                module.params['storage'] = dict()

        # If the peer should not exist, handle that now.
        state = module.params['state']
        if state == 'absent' and peer_exists:

            # The peer should not exist, so delete it.
            if peer_corrupt:
                console.delete_ext_peer(peer['id'])
            else:
                console.delete_peer(peer['id'])
            return module.exit_json(changed=True)

        elif state == 'absent':

            # The peer should not exist and doesn't.
            return module.exit_json(changed=False)

        # Extract the expected peer configuration.
        expected_peer = dict(display_name=name,
                             msp_id=module.params['msp_id'],
                             state_db=module.params['state_db'],
                             config_override=module.params['config_override'],
                             resources=module.params['resources'],
                             storage=module.params['storage'])

        # Add the HSM configuration if it is specified.
        hsm = module.params['hsm']
        if hsm is not None:
            pkcs11endpoint = hsm['pkcs11endpoint']
            expected_peer['hsm'] = dict(pkcs11endpoint=pkcs11endpoint)
            hsm_config_override = dict(peer=dict(
                BCCSP=dict(Default='PKCS11',
                           PKCS11=dict(Label=hsm['label'], Pin=hsm['pin']))))
            merge_dicts(expected_peer['config_override'], hsm_config_override)

        # Add the zone if it is specified.
        zone = module.params['zone']
        if zone is not None:
            expected_peer['zone'] = zone

        # Add the version if it is specified.
        version = module.params['version']
        if version is not None:
            expected_peer['version'] = version

        # If the peer is corrupt, delete it first. This may happen if somebody imported an external peer
        # with the same name, or if somebody deleted the Kubernetes resources directly.
        changed = False
        if peer_corrupt:
            module.warn(
                'Peer exists in console but not in Kubernetes, deleting it before continuing'
            )
            console.delete_ext_peer(peer['id'])
            peer_exists = peer_corrupt = False
            changed = True

        # Either create or update the peer.
        changed = False
        if state == 'present' and not peer_exists:

            # Delete the resources and storage configuration for a new peer
            # being deployed to a free cluster.
            if console.is_free_cluster():
                del expected_peer['resources']
                del expected_peer['storage']

            # Get the config.
            expected_peer['config'] = get_config(console, module)

            # Create the peer.
            peer = console.create_peer(expected_peer)
            changed = True

        elif state == 'present' and peer_exists:

            # HACK: never send the limits back, as they are rejected.
            for thing in [
                    'peer', 'proxy', 'couchdb', 'dind', 'chaincodelauncher'
            ]:
                if thing in peer['resources']:
                    if 'limits' in peer['resources'][thing]:
                        del peer['resources'][thing]['limits']

            # HACK: never send the fluentd and init resources back, as they are rejected.
            peer['resources'].pop('fluentd', None)
            peer['resources'].pop('init', None)

            # Update the peer configuration.
            new_peer = copy_dict(peer)
            merge_dicts(new_peer, expected_peer)

            # We should only send dind resources if the peer is running Fabric v1.4.
            # We should only send chaincodelauncher resources if the peer is running Fabric v2.x.
            if LooseVersion(new_peer['version']) >= LooseVersion('2.0'):
                new_peer['resources'].pop('dind', None)
            elif LooseVersion(new_peer['version']) < LooseVersion('2.0'):
                new_peer['resources'].pop('chaincodelauncher', None)

            # Check to see if any banned changes have been made.
            # HACK: zone is documented as a permitted change, but it has no effect.
            permitted_changes = ['resources', 'config_override', 'version']
            diff = diff_dicts(peer, new_peer)
            for change in diff:
                if change not in permitted_changes:
                    raise Exception(
                        f'{change} cannot be changed from {peer[change]} to {new_peer[change]} for existing peer'
                    )

            # HACK: the BCCSP section of the config overrides cannot be specified,
            # even if it has not changed, so never send it in as part of an update.
            peer['config_override'].get('peer', dict()).pop('BCCSP', None)
            new_peer['config_override'].get('peer', dict()).pop('BCCSP', None)

            # HACK: if the version has not changed, do not send it in. The current
            # version may not be supported by the current version of IBP.
            if peer['version'] == new_peer['version']:
                del peer['version']
                del new_peer['version']

            # If the peer has changed, apply the changes.
            peer_changed = not equal_dicts(peer, new_peer)
            if peer_changed:
                peer = console.update_peer(new_peer['id'], new_peer)
                changed = True

            # Now need to compare the list of admin certs. The admin certs may be passed in via
            # three different parameters (admins, config.enrollment.component.admincerts, and
            # config.msp.component.admincerts) so we need to find them.
            # HACK: if the admin certs did not get returned, we're running on IBP v2.1.3
            # and it does not support this feature.
            expected_admins = module.params['admins']
            if not expected_admins:
                config = module.params['config']
                if config:
                    for config_type in ['enrollment', 'msp']:
                        expected_admins = config.get(config_type, dict()).get(
                            'component', dict()).get('admincerts', None)
                        if expected_admins:
                            break
            if expected_admins:
                expected_admins_set = set(
                    map(normalize_whitespace, expected_admins))
                actual_admins = peer.get('admin_certs', None)
                if actual_admins is not None:
                    actual_admins_set = set(
                        map(normalize_whitespace, actual_admins))
                    append_admin_certs = list(
                        expected_admins_set.difference(actual_admins_set))
                    remove_admin_certs = list(
                        actual_admins_set.difference(expected_admins_set))
                    if append_admin_certs or remove_admin_certs:
                        console.edit_admin_certs(peer['id'],
                                                 append_admin_certs,
                                                 remove_admin_certs)
                        changed = True

        # Wait for the peer to start.
        peer = Peer.from_json(console.extract_peer_info(peer))
        timeout = module.params['wait_timeout']
        peer.wait_for(timeout)

        # Return the peer.
        module.exit_json(changed=changed, peer=peer.to_json())

    # Notify Ansible of the exception.
    except Exception as e:
        module.fail_json(msg=to_native(e))
def main():

    # Create the module.
    argument_spec = dict(
        state=dict(type='str',
                   default='present',
                   choices=['present', 'absent']),
        api_endpoint=dict(type='str', required=True),
        api_authtype=dict(type='str',
                          required=True,
                          choices=['ibmcloud', 'basic']),
        api_key=dict(type='str', required=True, no_log=True),
        api_secret=dict(type='str', no_log=True),
        api_timeout=dict(type='int', default=60),
        api_token_endpoint=dict(
            type='str', default='https://iam.cloud.ibm.com/identity/token'),
        name=dict(type='str', required=True),
        config_override=dict(type='dict',
                             default=dict(),
                             options=dict(
                                 ca=dict(type='dict'),
                                 tlsca=dict(type='dict'),
                             )),
        resources=dict(
            type='dict',
            default=dict(),
            options=dict(ca=dict(
                type='dict',
                default=dict(),
                options=dict(requests=dict(
                    type='dict',
                    default=dict(),
                    options=dict(cpu=dict(type='str', default='100m'),
                                 memory=dict(type='str', default='200M'))))))),
        storage=dict(
            type='dict',
            default=dict(),
            options=dict(ca=dict(type='dict',
                                 default=dict(),
                                 options={
                                     'size': dict(type='str', default='20Gi'),
                                     'class': dict(type='str')
                                 }))),
        hsm=dict(type='dict',
                 options=dict(pkcs11endpoint=dict(type='str'),
                              label=dict(type='str',
                                         required=True,
                                         no_log=True),
                              pin=dict(type='str', required=True,
                                       no_log=True))),
        zone=dict(type='str'),
        replicas=dict(type='int'),
        version=dict(type='str'),
        wait_timeout=dict(type='int', default=60))
    required_if = [('api_authtype', 'basic', ['api_secret'])]
    module = BlockchainModule(argument_spec=argument_spec,
                              supports_check_mode=True,
                              required_if=required_if)

    # Ensure all exceptions are caught.
    try:

        # Log in to the IBP console.
        console = get_console(module)

        # Determine if the certificate authority exists.
        name = module.params['name']
        certificate_authority = console.get_component_by_display_name(
            name, deployment_attrs='included')
        certificate_authority_exists = certificate_authority is not None
        certificate_authority_corrupt = certificate_authority is not None and 'deployment_attrs_missing' in certificate_authority

        # If this is a free cluster, we cannot accept resource/storage configuration,
        # as these are ignored for free clusters. We must also delete the defaults,
        # otherwise they cause a mismatch with the values that actually get set.
        if console.is_free_cluster():
            actual_params = _load_params()
            if 'resources' in actual_params or 'storage' in actual_params:
                raise Exception(
                    'Cannot specify resources or storage for a free IBM Kubernetes Service cluster'
                )
            if certificate_authority_exists:
                module.params['resources'] = dict()
                module.params['storage'] = dict()

        # If the certificate authority should not exist, handle that now.
        state = module.params['state']
        if state == 'absent' and certificate_authority_exists:

            # The certificate authority should not exist, so delete it.
            if certificate_authority_corrupt:
                console.delete_ext_ca(certificate_authority['id'])
            else:
                console.delete_ca(certificate_authority['id'])
            return module.exit_json(changed=True)

        elif state == 'absent':

            # The certificate authority should not exist and doesn't.
            return module.exit_json(changed=False)

        # If config overrides are provided for the CA, but not the TLSCA, copy
        # the CA ones into place. This is what the REST API does.
        config_override = module.params['config_override']
        ca = config_override['ca']
        tlsca = config_override['tlsca']
        if ca is not None and tlsca is None:
            tlsca = config_override['tlsca'] = ca

        # HACK: strip out the storage class if it is not specified. Can't pass null as the API barfs.
        storage = module.params['storage']
        for storage_type in storage:
            if 'class' not in storage[storage_type]:
                continue
            storage_class = storage[storage_type]['class']
            if storage_class is None:
                del storage[storage_type]['class']

        # Extract the expected certificate authority configuration.
        expected_certificate_authority = dict(
            display_name=name,
            config_override=config_override,
            resources=module.params['resources'],
            storage=storage)

        # Add the HSM configuration if it is specified.
        hsm = module.params['hsm']
        if hsm is not None:
            pkcs11endpoint = hsm['pkcs11endpoint']
            if pkcs11endpoint:
                expected_certificate_authority['hsm'] = dict(
                    pkcs11endpoint=pkcs11endpoint)
            bccsp = dict(
                BCCSP=dict(Default='PKCS11',
                           PKCS11=dict(Label=hsm['label'], Pin=hsm['pin'])))
            if ca is None:
                config_override['ca'] = ca = dict()
            if tlsca is None:
                config_override['tlsca'] = tlsca = dict()
            merge_dicts(ca, bccsp)
            merge_dicts(tlsca, bccsp)

        # Add the zone if it is specified.
        zone = module.params['zone']
        if zone is not None:
            expected_certificate_authority['zone'] = zone

        # Add the number of replicas if it is specified.
        replicas = module.params['replicas']
        if replicas is not None:
            ca_db = expected_certificate_authority.get(
                'config_override',
                dict()).get('ca', dict()).get('db',
                                              dict()).get('type', 'sqlite3')
            tlsca_db = expected_certificate_authority.get(
                'config_override',
                dict()).get('tlsca',
                            dict()).get('db', dict()).get('type', 'sqlite3')
            if replicas > 1 and (ca_db != 'postgres'
                                 or tlsca_db != 'postgres'):
                raise Exception(
                    'Certificate authority must use PostgreSQL in order to have multiple replicas'
                )
            expected_certificate_authority['replicas'] = replicas

        # Add the version if it is specified.
        version = module.params['version']
        if version is not None:
            resolved_version = console.resolve_ca_version(version)
            expected_certificate_authority['version'] = resolved_version

        # If the certificate authority is corrupt, delete it first. This may happen if somebody imported an external certificate
        # authority with the same name, or if somebody deleted the Kubernetes resources directly.
        changed = False
        if certificate_authority_corrupt:
            module.warn(
                'Certificate authority exists in console but not in Kubernetes, deleting it before continuing'
            )
            console.delete_ext_ca(certificate_authority['id'])
            certificate_authority_exists = certificate_authority_corrupt = False
            changed = True

        # Either create or update the certificate authority.
        if state == 'present' and not certificate_authority_exists:

            # Delete the resources and storage configuration for a new certificate
            # authority being deployed to a free cluster.
            if console.is_free_cluster():
                del expected_certificate_authority['resources']
                del expected_certificate_authority['storage']

            # Create the certificate authority.
            certificate_authority = console.create_ca(
                expected_certificate_authority)
            changed = True

        elif state == 'present' and certificate_authority_exists:

            # HACK: never send the limits back, as they are rejected.
            for thing in ['ca']:
                if thing in certificate_authority['resources']:
                    if 'limits' in certificate_authority['resources'][thing]:
                        del certificate_authority['resources'][thing]['limits']

            # Update the certificate authority configuration.
            new_certificate_authority = copy_dict(certificate_authority)
            merge_dicts(new_certificate_authority,
                        expected_certificate_authority)

            # You can't change the registry after creation, but the remote names and secrets are redacted.
            # In order to diff properly, we need to redact the incoming secrets. We need to restore the
            # unredacted versions before sending them to the API though!
            expected_ca_identities = new_certificate_authority.get(
                'config_override',
                dict()).get('ca',
                            dict()).get('registry',
                                        dict()).get('identities', list())
            expected_tlsca_identities = new_certificate_authority.get(
                'config_override',
                dict()).get('tlsca',
                            dict()).get('registry',
                                        dict()).get('identities', list())
            original_expected_ca_identities = copy.deepcopy(
                expected_ca_identities)
            original_expected_tlsca_identities = copy.deepcopy(
                expected_tlsca_identities)
            for certificate_authority_to_update in [
                    certificate_authority, new_certificate_authority
            ]:
                for ca in ['ca', 'tlsca']:
                    identities = certificate_authority_to_update.get(
                        'config_override', dict()).get(ca, dict()).get(
                            'registry', dict()).get('identities', list())
                    for identity in identities:
                        if 'name' in identity:
                            identity['name'] = '[redacted]'
                        if 'pass' in identity:
                            identity['pass'] = '******'

            # Check to see if any banned changes have been made.
            # HACK: zone is documented as a permitted change, but it has no effect.
            permitted_changes = [
                'resources', 'config_override', 'replicas', 'version'
            ]
            diff = diff_dicts(certificate_authority, new_certificate_authority)
            for change in diff:
                if change not in permitted_changes:
                    raise Exception(
                        f'{change} cannot be changed from {certificate_authority[change]} to {new_certificate_authority[change]} for existing certificate authority'
                    )

            # HACK: we can't send in certain config override changes, even if they haven't
            # changed, so we need to check those and then remove them.
            banned_changes = ['db']
            for change in banned_changes:
                for ca in ['ca', 'tlsca']:
                    expected_config_override = new_certificate_authority.get(
                        'config_override',
                        dict()).get(ca, dict()).get(change, None)
                    actual_config_override = certificate_authority.get(
                        'config_override',
                        dict()).get(ca, dict()).get(change, None)
                    if expected_config_override and not actual_config_override:
                        # HACK: ignore this case; it can be missing but still present.
                        pass
                    elif not expected_config_override and actual_config_override:
                        # Cannot unset.
                        raise Exception(
                            f'config_override.{ca}.{change} cannot be changed from {actual_config_override} to {expected_config_override} for existing certificate authority'
                        )
                    elif not equal_dicts(expected_config_override,
                                         actual_config_override):
                        # Cannot modify.
                        raise Exception(
                            f'config_override.{ca}.{change} cannot be changed from {actual_config_override} to {expected_config_override} for existing certificate authority'
                        )
                    new_certificate_authority.get('config_override',
                                                  dict()).get(ca, dict()).pop(
                                                      change, None)
                    certificate_authority.get('config_override', dict()).get(
                        ca, dict()).pop(change, None)

            # HACK: if the version has not changed, do not send it in. The current
            # version may not be supported by the current version of IBP.
            if certificate_authority['version'] == new_certificate_authority[
                    'version']:
                del certificate_authority['version']
                del new_certificate_authority['version']

            # If the certificate authority has changed, apply the changes.
            certificate_authority_changed = not equal_dicts(
                certificate_authority, new_certificate_authority)
            if certificate_authority_changed:
                new_certificate_authority.get(
                    'config_override', dict()).get('ca', dict()).get(
                        'registry',
                        dict())['identities'] = original_expected_ca_identities
                new_certificate_authority.get('config_override', dict()).get(
                    'tlsca', dict()).get('registry', dict(
                    ))['identities'] = original_expected_tlsca_identities
                certificate_authority = console.update_ca(
                    new_certificate_authority['id'], new_certificate_authority)
                changed = True

        # Wait for the certificate authority to start.
        certificate_authority = CertificateAuthority.from_json(
            console.extract_ca_info(certificate_authority))
        timeout = module.params['wait_timeout']
        certificate_authority.wait_for(timeout)

        # Return the certificate authority.
        module.exit_json(changed=changed,
                         certificate_authority=certificate_authority.to_json())

    # Notify Ansible of the exception.
    except Exception as e:
        module.fail_json(msg=to_native(e))
Пример #18
0
def main():

    # Create the module.
    argument_spec = dict(
        state=dict(type='str',
                   default='present',
                   choices=['present', 'absent']),
        api_endpoint=dict(type='str', required=True),
        api_authtype=dict(type='str',
                          required=True,
                          choices=['ibmcloud', 'basic']),
        api_key=dict(type='str', required=True, no_log=True),
        api_secret=dict(type='str', no_log=True),
        api_timeout=dict(type='int', default=60),
        api_token_endpoint=dict(
            type='str', default='https://iam.cloud.ibm.com/identity/token'),
        operation=dict(type='str', choices=['fetch']),
        ordering_service=dict(type='raw'),
        ordering_service_nodes=dict(type='list', elements='raw'),
        tls_handshake_time_shift=dict(
            type='str',
            fallback=(env_fallback, ['IBP_TLS_HANDSHAKE_TIME_SHIFT'])),
        identity=dict(type='raw'),
        msp_id=dict(type='str'),
        hsm=dict(type='dict',
                 options=dict(pkcs11library=dict(type='str', required=True),
                              label=dict(type='str',
                                         required=True,
                                         no_log=True),
                              pin=dict(type='str', required=True,
                                       no_log=True))),
        name=dict(type='str'),
        path=dict(type='str', required=True),
        target=dict(type='str'))
    required_if = [
        ('api_authtype', 'basic', ['api_secret']),
        ('state', 'present', ['identity', 'msp_id', 'name', 'target']),
    ]
    # Ansible doesn't allow us to say "require one of X and Y only if condition A is true",
    # so we need to handle this ourselves by seeing what was passed in.
    actual_params = _load_params()
    if actual_params.get('state', None) == 'present':
        required_one_of = [['ordering_service', 'ordering_service_nodes']]
    else:
        required_one_of = []
    module = BlockchainModule(argument_spec=argument_spec,
                              supports_check_mode=True,
                              required_if=required_if,
                              required_one_of=required_one_of)

    # Validate HSM requirements if HSM is specified.
    if module.params['hsm']:
        module.check_for_missing_hsm_libs()

    # Ensure all exceptions are caught.
    try:

        # Log in to the IBP console.
        console = get_console(module)

        # Handle the state is absent case first.
        state = module.params['state']
        path = module.params['path']
        path_exists = os.path.isfile(path)
        if state == 'absent' and path_exists:
            os.remove(path)
            return module.exit_json(changed=True)
        elif state == 'absent':
            return module.exit_json(changed=False)

        # Get the ordering service.
        ordering_service_specified = module.params[
            'ordering_service'] is not None
        if ordering_service_specified:
            ordering_service = get_ordering_service_by_module(console, module)
        else:
            ordering_service_nodes = get_ordering_service_nodes_by_module(
                console, module)
            ordering_service = OrderingService(ordering_service_nodes)
        tls_handshake_time_shift = module.params['tls_handshake_time_shift']

        # Get the identity.
        identity = get_identity_by_module(module)
        msp_id = module.params['msp_id']
        hsm = module.params['hsm']
        identity = resolve_identity(console, module, identity, msp_id)

        # Get the channel and target path.
        name = module.params['name']
        target = module.params['target']

        # Create a temporary file to hold the block.
        block_proto_path = get_temp_file()
        try:

            # Fetch the block.
            with ordering_service.connect(
                    module, identity, msp_id, hsm,
                    tls_handshake_time_shift) as connection:
                connection.fetch(name, target, block_proto_path)

            # Compare and copy if needed.
            if os.path.exists(path):
                changed = not equal_files(path, block_proto_path)
                if changed:
                    shutil.copyfile(block_proto_path, path)
                module.exit_json(changed=changed, path=path)
            else:
                shutil.copyfile(block_proto_path, path)
                module.exit_json(changed=True, path=path)

        # Ensure the temporary file is cleaned up.
        finally:
            os.remove(block_proto_path)

    # Notify Ansible of the exception.
    except Exception as e:
        module.fail_json(msg=to_native(e))
Пример #19
0
    def __init__(self, api_info):

        if not HAS_REQUESTS:
            self.fail(
                'Could not import the python library requests required by this module.'
            )

        self._fortigate_original_config = None
        self._fortigate_current_config = None
        self._update_config = None

        self._params = _load_params()
        self._vdom = self._params.get('vdom', "root")

        self._arg_spec_filename = "FortiosAPIArgSpecs.json"

        self._api_info = api_info
        if api_info.get('from_configs') is True:
            self._api_info = self._params['endpoint_information']

        self._match_ignore_params = self._api_info.get('match_ignore_params',
                                                       [])
        if self._match_ignore_params:
            self._match_ignore_params.append('name')

        if isinstance(self._api_info["endpoint"], list):
            self._endpoint = '/'.join(self._api_info["endpoint"])
        else:
            self._endpoint = self._api_info["endpoint"]

        if self._params['conn_params'].get('disable_warnings', False):
            requests.packages.urllib3.disable_warnings()
        self._verify = self._params['conn_params'].get('verify', True)
        self._secure = self._params['conn_params'].get('secure', True)
        self.proxies = self._params['conn_params'].get('proxies')

        self._ip = self._build_target_ip()

        response = self._login()
        self.cookies = response.cookies
        self.header = self._set_csrf_header()

        if "loginpwd_change" in response.text:
            self.fail(
                "Authentication succeed. But password changed is required.")
        if not self.header:
            self.fail(
                "Authentication with FortiOS device failed. Check your username/password."
            )
        if not response.cookies:
            self.fail(
                "Authentication failed. Authentication attempts likely blocked temporarily."
            )
        if "just_logged_in" in response.cookies:
            self.fail(
                "Authentication with FortiOS device failed. Check your username/password."
            )

        self._minimum_object_params = self._params.get('default_object', [])
        if self._endpoint.startswith('cmdb'):
            self._default_object_configuration = self._get_default_object()
        self._object_identifier = self._api_info.get('object_identifier')
        self._permanent_object_ids = self._get_permanent_object_identifiers()
        self._ignore_object_ids = self._get_ignore_object_identifiers()

        self.http_status_codes = {
            200:
            "Request Successful.",
            400:
            "Request cannot be processed by API.",
            401:
            "Login unsuccessful.",
            403:
            "Account doesn't have access permissions.",
            404:
            "Unable to find specified resource.",
            405:
            "HTTP method not allowed for this resource.",
            413:
            "Request can't be processed because entity is too large.",
            424:
            "Failed dependency - one of: duplicate resource, missing required parameter, "
            "missing required attribute, or invalid attribute value.",
            500:
            "Internal server error.",
            501:
            "Not Implemented. Check that your endpoint is correct."
        }

        self._object_map = []
        self._used_object_ids = []
        self._existing_object_ids = []
        self._object_ids_to_update = []
        self._permanent_object_ids_to_reset = []

        self._list_identifier = self._api_info['list_identifier']
        if self._endpoint.startswith('cmdb'):
            self._argument_spec = {
                self._list_identifier:
                dict(type='list', options=self._get_argument_spec())
            }
        else:  # for Monitor API call (not CMDB API call), not need to verify with schema
            if self._list_identifier:
                self._argument_spec = {
                    self._list_identifier: dict(type='list', options=None)
                }
            else:  # Some Monitor API call don't even have list_identifier
                self._argument_spec = {}
        self._argument_spec.update(fortios_api_argument_spec)

        self._module = AnsibleModule(self._argument_spec,
                                     supports_check_mode=True)
        self._update_config = self._module.params.get(
            self._list_identifier) or []
        self._print_current_config = self._module.params.get(
            'print_current_config')
        self._full_config = self._module.params.get('full_config')
        self._delete_objects = self._module.params.get('delete_objects')
        self._check_mode = self._module.check_mode or self._print_current_config
Пример #20
0
def main():

    # Create the module.
    argument_spec = dict(
        state=dict(type='str',
                   default='present',
                   choices=['present', 'absent']),
        api_endpoint=dict(type='str', required=True),
        api_authtype=dict(type='str',
                          required=True,
                          choices=['ibmcloud', 'basic']),
        api_key=dict(type='str', required=True, no_log=True),
        api_secret=dict(type='str', no_log=True),
        api_timeout=dict(type='int', default=60),
        api_token_endpoint=dict(
            type='str', default='https://iam.cloud.ibm.com/identity/token'),
        name=dict(type='str', required=True),
        ordering_service=dict(type='raw'),
        msp_id=dict(type='str'),
        orderer_type=dict(type='str', default='raft', choices=['raft']),
        system_channel_id=dict(type='str', default='testchainid'),
        config_block=dict(type='str'),
        certificate_authority=dict(type='raw'),
        enrollment_id=dict(type='str'),
        enrollment_secret=dict(type='str', no_log=True),
        admins=dict(type='list',
                    elements='str',
                    aliases=['admin_certificates']),
        config=dict(type='dict'),
        config_override=dict(type='dict', default=dict()),
        resources=dict(
            type='dict',
            default=dict(),
            options=dict(orderer=dict(
                type='dict',
                default=dict(),
                options=dict(requests=dict(
                    type='dict',
                    default=dict(),
                    options=dict(cpu=dict(type='str', default='250m'),
                                 memory=dict(type='str', default='500M'))))),
                         proxy=dict(
                             type='dict',
                             default=dict(),
                             options=dict(requests=dict(
                                 type='dict',
                                 default=dict(),
                                 options=dict(
                                     cpu=dict(type='str', default='100m'),
                                     memory=dict(type='str',
                                                 default='200M'))))))),
        storage=dict(
            type='dict',
            default=dict(),
            options=dict(
                orderer=dict(type='dict',
                             default=dict(),
                             options={
                                 'size': dict(type='str', default='100Gi'),
                                 'class': dict(type='str')
                             }))),
        hsm=dict(type='dict',
                 options=dict(pkcs11endpoint=dict(type='str'),
                              label=dict(type='str',
                                         required=True,
                                         no_log=True),
                              pin=dict(type='str', required=True,
                                       no_log=True))),
        zone=dict(type='str'),
        version=dict(type='str'),
        wait_timeout=dict(type='int', default=60))
    required_if = [('api_authtype', 'basic', ['api_secret']),
                   ('state', 'present', ['msp_id', 'ordering_service'])]
    # Ansible doesn't allow us to say "require one of X and Y only if condition A is true",
    # so we need to handle this ourselves by seeing what was passed in.
    actual_params = _load_params()
    if actual_params.get('state', 'present') == 'present':
        required_one_of = [['certificate_authority', 'config']]
    else:
        required_one_of = []
    required_together = [['certificate_authority', 'enrollment_id'],
                         ['certificate_authority', 'enrollment_secret'],
                         ['certificate_authority', 'admins']]
    mutually_exclusive = [['certificate_authority', 'config']]
    module = BlockchainModule(argument_spec=argument_spec,
                              supports_check_mode=True,
                              required_if=required_if,
                              required_one_of=required_one_of,
                              required_together=required_together,
                              mutually_exclusive=mutually_exclusive)

    # Ensure all exceptions are caught.
    try:

        # Log in to the IBP console.
        console = get_console(module)

        # Determine if the ordering service node exists.
        name = module.params['name']
        ordering_service_node = console.get_component_by_display_name(
            name, deployment_attrs='included')
        ordering_service_node_exists = ordering_service_node is not None
        ordering_service_node_corrupt = ordering_service_node is not None and 'deployment_attrs_missing' in ordering_service_node

        # If this is a free cluster, we cannot accept resource/storage configuration,
        # as these are ignored for free clusters. We must also delete the defaults,
        # otherwise they cause a mismatch with the values that actually get set.
        if console.is_free_cluster():
            actual_params = _load_params()
            if 'resources' in actual_params or 'storage' in actual_params:
                raise Exception(
                    'Cannot specify resources or storage for a free IBM Kubernetes Service cluster'
                )
            if ordering_service_node_exists:
                module.params['resources'] = dict()
                module.params['storage'] = dict()

        # If the ordering service node should not exist, handle that now.
        state = module.params['state']
        if state == 'absent' and ordering_service_node_exists:

            # The ordering service node should not exist, so delete it.
            console.delete_ordering_service_node(ordering_service_node['id'])
            return module.exit_json(changed=True)

        elif state == 'absent':

            # The ordering service node should not exist and doesn't.
            return module.exit_json(changed=False)

        # Get the ordering service that this ordering service node should belong to.
        ordering_service = get_ordering_service_by_module(console, module)
        cluster_id = ordering_service.nodes[0].cluster_id
        cluster_name = ordering_service.nodes[0].cluster_name

        # HACK: strip out the storage class if it is not specified. Can't pass null as the API barfs.
        storage = module.params['storage']
        for storage_type in storage:
            if 'class' not in storage[storage_type]:
                continue
            storage_class = storage[storage_type]['class']
            if storage_class is None:
                del storage[storage_type]['class']

        # Extract the expected ordering service node configuration.
        expected_ordering_service_node = dict(
            display_name=name,
            cluster_id=cluster_id,
            cluster_name=cluster_name,
            msp_id=module.params['msp_id'],
            orderer_type=module.params['orderer_type'],
            system_channel_id=module.params['system_channel_id'],
            config_override=module.params['config_override'],
            resources=module.params['resources'],
            storage=storage)

        # Add the HSM configuration if it is specified.
        hsm = module.params['hsm']
        if hsm is not None:
            pkcs11endpoint = hsm['pkcs11endpoint']
            if pkcs11endpoint:
                expected_ordering_service_node['hsm'] = dict(
                    pkcs11endpoint=pkcs11endpoint)
            hsm_config_override = dict(General=dict(
                BCCSP=dict(Default='PKCS11',
                           PKCS11=dict(Label=hsm['label'], Pin=hsm['pin']))))
            merge_dicts(expected_ordering_service_node['config_override'],
                        hsm_config_override)

        # Add the zone if it is specified.
        zone = module.params['zone']
        if zone is not None:
            expected_ordering_service_node['zone'] = zone

        # Add the version if it is specified.
        version = module.params['version']
        if version is not None:
            resolved_version = console.resolve_ordering_service_node_version(
                version)
            expected_ordering_service_node['version'] = resolved_version

        # If the ordering service node is corrupt, delete it first. This may happen if somebody imported an external
        # ordering service node with the same name, or if somebody deleted the Kubernetes resources directly.
        changed = False
        if ordering_service_node_corrupt:
            module.warn(
                'Ordering service node exists in console but not in Kubernetes, deleting it before continuing'
            )
            console.delete_ext_ordering_service_node(
                ordering_service_node['id'])
            ordering_service_node_exists = ordering_service_node_corrupt = False
            changed = True

        # Either create or update the ordering service.
        changed = False
        if state == 'present' and not ordering_service_node_exists:

            # Delete the resources and storage configuration for a new ordering
            # service being deployed to a free cluster.
            if console.is_free_cluster():
                del expected_ordering_service_node['resources']
                del expected_ordering_service_node['storage']

            # There is no "create an ordering service node" API, so we need to create/append to
            # an existing ordering service. This means we have to convert various parameters to
            # lists to make it look like a request to create a one node ordering service.
            expected_ordering_service_node['config_override'] = [
                expected_ordering_service_node['config_override']
            ]
            if zone is not None:
                expected_ordering_service_node['zone'] = [zone]

            # Get the config.
            expected_ordering_service_node['config'] = [
                get_config(console, module)
            ]

            # Create the ordering service.
            ordering_service = console.create_ordering_service(
                expected_ordering_service_node)
            if isinstance(ordering_service, list):
                ordering_service_node = ordering_service[0]
            else:
                ordering_service_node = ordering_service
            changed = True

        elif state == 'present' and ordering_service_node_exists:

            # HACK: never send the limits back, as they are rejected.
            for thing in ['orderer', 'proxy']:
                if thing in ordering_service_node['resources']:
                    if 'limits' in ordering_service_node['resources'][thing]:
                        del ordering_service_node['resources'][thing]['limits']

            # HACK: never send the init resources back, as they are rejected.
            ordering_service_node['resources'].pop('init', None)

            # Update the ordering service node configuration.
            new_ordering_service_node = copy_dict(ordering_service_node)
            merge_dicts(new_ordering_service_node,
                        expected_ordering_service_node)

            # Check to see if any banned changes have been made.
            # HACK: zone is documented as a permitted change, but it has no effect.
            permitted_changes = ['resources', 'config_override', 'version']
            diff = diff_dicts(ordering_service_node, new_ordering_service_node)
            for change in diff:
                if change not in permitted_changes:
                    raise Exception(
                        f'{change} cannot be changed from {ordering_service_node[change]} to {new_ordering_service_node[change]} for existing ordering service node'
                    )

            # HACK: the BCCSP section of the config overrides cannot be specified,
            # even if it has not changed, so never send it in as part of an update.
            ordering_service_node['config_override'].get('General',
                                                         dict()).pop(
                                                             'BCCSP', None)
            new_ordering_service_node['config_override'].get(
                'General', dict()).pop('BCCSP', None)

            # HACK: if the version has not changed, do not send it in. The current
            # version may not be supported by the current version of IBP.
            if ordering_service_node['version'] == new_ordering_service_node[
                    'version']:
                del ordering_service_node['version']
                del new_ordering_service_node['version']

            # If the ordering service node has changed, apply the changes.
            ordering_service_node_changed = not equal_dicts(
                ordering_service_node, new_ordering_service_node)
            if ordering_service_node_changed:
                ordering_service_node = console.update_ordering_service_node(
                    new_ordering_service_node['id'], new_ordering_service_node)
                changed = True

            # Now need to compare the list of admin certs. The admin certs may be passed in via
            # three different parameters (admins, config.enrollment.component.admincerts, and
            # config.msp.component.admincerts) so we need to find them.
            # HACK: if the admin certs did not get returned, we're running on IBP v2.1.3
            # and it does not support this feature.
            expected_admins = module.params['admins']
            if not expected_admins:
                config = module.params['config']
                if config:
                    for config_type in ['enrollment', 'msp']:
                        expected_admins = config.get(config_type, dict()).get(
                            'component', dict()).get('admincerts', None)
                        if expected_admins:
                            break
            if expected_admins:
                expected_admins_set = set(
                    map(normalize_whitespace, expected_admins))
                actual_admins = ordering_service_node.get('admin_certs', None)
                if actual_admins is not None:
                    actual_admins_set = set(
                        map(normalize_whitespace, actual_admins))
                    append_admin_certs = list(
                        expected_admins_set.difference(actual_admins_set))
                    remove_admin_certs = list(
                        actual_admins_set.difference(expected_admins_set))
                    if append_admin_certs or remove_admin_certs:
                        console.edit_admin_certs(ordering_service_node['id'],
                                                 append_admin_certs,
                                                 remove_admin_certs)
                        changed = True

            # If the ordering service node has not been added to the system channel, we may need to
            # send the ordering service node a config block.
            if not ordering_service_node['consenter_proposal_fin']:

                # Check to see if a config block has been specified.
                config_block_file = module.params['config_block']
                if config_block_file:

                    # Read the config block and base64 encode it.
                    with open(config_block_file, 'rb') as file:
                        config_block = file.read()
                    config_block = base64.b64encode(config_block).decode(
                        'utf-8')

                    # Submit the config block to the ordering service.
                    console.submit_config_block(ordering_service_node['id'],
                                                config_block)

                    # Edit the ordering service node.
                    ordering_service_node = console.edit_ordering_service_node(
                        ordering_service_node['id'],
                        dict(consenter_proposal_fin=True))
                    changed = True

        # Wait for the ordering service node to start, but only if it has been added to the system channel.
        ordering_service_node = OrderingServiceNode.from_json(
            console.extract_ordering_service_node_info(ordering_service_node))
        if ordering_service_node.consenter_proposal_fin:
            timeout = module.params['wait_timeout']
            ordering_service_node.wait_for(timeout)

        # Return the ordering service node.
        module.exit_json(changed=changed,
                         ordering_service_node=ordering_service_node.to_json())

    # Notify Ansible of the exception.
    except Exception as e:
        module.fail_json(msg=to_native(e))
Пример #21
0
def main():

    # Create the module.
    argument_spec = dict(
        state=dict(type='str',
                   default='present',
                   choices=['present', 'absent']),
        api_endpoint=dict(type='str', required=True),
        api_authtype=dict(type='str',
                          required=True,
                          choices=['ibmcloud', 'basic']),
        api_key=dict(type='str', required=True),
        api_secret=dict(type='str'),
        api_timeout=dict(type='int', default=60),
        api_token_endpoint=dict(
            type='str', default='https://iam.cloud.ibm.com/identity/token'),
        name=dict(type='str', required=True),
        msp_id=dict(type='str'),
        orderer_type=dict(type='str', default='raft', choices=['raft']),
        system_channel_id=dict(type='str', default='testchainid'),
        certificate_authority=dict(type='raw'),
        enrollment_id=dict(type='str'),
        enrollment_secret=dict(type='str'),
        admin_certificates=dict(type='list', elements='str'),
        nodes=dict(type='int'),
        config=dict(type='list', elements='dict'),
        config_override=dict(type='list'),
        resources=dict(
            type='dict',
            default=dict(),
            options=dict(orderer=dict(
                type='dict',
                default=dict(),
                options=dict(requests=dict(
                    type='dict',
                    default=dict(),
                    options=dict(cpu=dict(type='str', default='250m'),
                                 memory=dict(type='str', default='500M'))))),
                         proxy=dict(
                             type='dict',
                             default=dict(),
                             options=dict(requests=dict(
                                 type='dict',
                                 default=dict(),
                                 options=dict(
                                     cpu=dict(type='str', default='100m'),
                                     memory=dict(type='str',
                                                 default='200M'))))))),
        storage=dict(
            type='dict',
            default=dict(),
            options=dict(
                orderer=dict(type='dict',
                             default=dict(),
                             options={
                                 'size': dict(type='str', default='100Gi'),
                                 'class': dict(type='str', default='default')
                             }))),
        hsm=dict(type='dict',
                 options=dict(pkcs11endpoint=dict(type='str', required=True),
                              label=dict(type='str', required=True),
                              pin=dict(type='str', required=True))),
        wait_timeout=dict(type='int', default=60))
    required_if = [('api_authtype', 'basic', ['api_secret']),
                   ('state', 'present', ['msp_id', 'nodes'])]
    required_one_of = [['certificate_authority', 'config']]
    required_together = [['certificate_authority', 'enrollment_id'],
                         ['certificate_authority', 'enrollment_secret'],
                         ['certificate_authority', 'admin_certificates']]
    mutually_exclusive = [['certificate_authority', 'config']]
    module = AnsibleModule(argument_spec=argument_spec,
                           supports_check_mode=True,
                           required_if=required_if,
                           required_one_of=required_one_of,
                           required_together=required_together,
                           mutually_exclusive=mutually_exclusive)

    # Ensure all exceptions are caught.
    try:

        # Log in to the IBP console.
        console = get_console(module)

        # Determine if the ordering service exists.
        name = module.params['name']
        ordering_service = console.get_components_by_cluster_name(
            name, deployment_attrs='included')
        ordering_service_exists = len(ordering_service) > 0

        # If this is a free cluster, we cannot accept resource/storage configuration,
        # as these are ignored for free clusters. We must also delete the defaults,
        # otherwise they cause a mismatch with the values that actually get set.
        if console.is_free_cluster():
            actual_params = _load_params()
            if 'resources' in actual_params or 'storage' in actual_params:
                raise Exception(
                    f'Cannot specify resources or storage for a free IBM Kubernetes Service cluster'
                )
            if ordering_service_exists:
                module.params['resources'] = dict()
                module.params['storage'] = dict()

        # If the ordering service should not exist, handle that now.
        state = module.params['state']
        if state == 'absent' and ordering_service_exists:

            # The ordering service should not exist, so delete it.
            console.delete_ordering_service(ordering_service[0]['cluster_id'])
            return module.exit_json(changed=True)

        elif state == 'absent':

            # The ordering service should not exist and doesn't.
            return module.exit_json(changed=False)

        # Compute the HSM configuration if it is specified.
        hsm = module.params['hsm']
        if hsm is not None:
            pkcs11endpoint = hsm['pkcs11endpoint']
            hsm_config_override = dict(General=dict(
                BCCSP=dict(Default='PKCS11',
                           PKCS11=dict(Label=hsm['label'], Pin=hsm['pin']))))

        # Either create or update the ordering service.
        changed = False
        if state == 'present' and not ordering_service_exists:

            # Get the config.
            config = get_config(console, module)

            # Get the config overrides.
            nodes = module.params['nodes']
            config_override_list = module.params['config_override']
            if config_override_list is not None:
                if len(config_override_list) != nodes:
                    raise Exception(
                        f'Number of nodes is {nodes}, but only {len(config_override_list)} config override objects provided'
                    )
            else:
                config_override_list = list()
                i = 0
                while i < nodes:
                    config_override_list.append(dict())
                    i = i + 1

            # Extract the expected ordering service configuration.
            expected_ordering_service = dict(
                display_name=name,
                cluster_name=name,
                msp_id=module.params['msp_id'],
                orderer_type=module.params['orderer_type'],
                system_channel_id=module.params['system_channel_id'],
                config=config,
                config_override=config_override_list,
                resources=module.params['resources'],
                storage=module.params['storage'])

            # Add the HSM configuration if it is specified.
            if hsm is not None:
                expected_ordering_service['hsm'] = dict(
                    pkcs11endpoint=pkcs11endpoint)
                for config_override in config_override_list:
                    merge_dicts(config_override, hsm_config_override)

            # Create the ordering service.
            ordering_service = console.create_ordering_service(
                expected_ordering_service)
            changed = True

        elif state == 'present' and ordering_service_exists:

            # Check to see if the number of ordering service nodes has changed.
            nodes = module.params['nodes']
            if nodes != len(ordering_service):
                raise Exception(
                    f'nodes cannot be changed from {len(ordering_service)} to {nodes} for existing ordering service'
                )
            config_override_list = module.params['config_override']
            if config_override_list is not None:
                if len(config_override_list) != nodes:
                    raise Exception(
                        f'Number of nodes is {nodes}, but only {len(config_override_list)} config override objects provided'
                    )

            # Go through each ordering service node.
            i = 0
            while i < len(ordering_service):

                # Get the ordering service node.
                ordering_service_node = ordering_service[i]

                # HACK: never send the limits back, as they are rejected.
                for thing in ['orderer', 'proxy']:
                    if thing in ordering_service_node['resources']:
                        if 'limits' in ordering_service_node['resources'][
                                thing]:
                            del ordering_service_node['resources'][thing][
                                'limits']

                # Get the config overrides.
                if config_override_list is not None:
                    config_override = config_override[i]
                else:
                    config_override = dict()

                # Extract the expected ordering service node configuration.
                expected_ordering_service_node = dict(
                    msp_id=module.params['msp_id'],
                    orderer_type=module.params['orderer_type'],
                    system_channel_id=module.params['system_channel_id'],
                    config_override=config_override,
                    resources=module.params['resources'],
                    storage=module.params['storage'])

                # Add the HSM configuration if it is specified.
                if hsm is not None:
                    expected_ordering_service_node['hsm'] = dict(
                        pkcs11endpoint=pkcs11endpoint)
                    merge_dicts(config_override, hsm_config_override)

                # Update the ordering service node configuration.
                new_ordering_service_node = copy_dict(ordering_service_node)
                merge_dicts(new_ordering_service_node,
                            expected_ordering_service_node)

                # Check to see if any banned changes have been made.
                banned_changes = [
                    'msp_id', 'orderer_type', 'system_channel_id', 'storage'
                ]
                diff = diff_dicts(ordering_service_node,
                                  new_ordering_service_node)
                for banned_change in banned_changes:
                    if banned_change in diff:
                        raise Exception(
                            f'{banned_change} cannot be changed from {ordering_service_node[banned_change]} to {new_ordering_service_node[banned_change]} for existing ordering service node'
                        )

                # If the ordering service node has changed, apply the changes.
                ordering_service_node_changed = not equal_dicts(
                    ordering_service_node, new_ordering_service_node)
                if ordering_service_node_changed:
                    ordering_service[i] = console.update_ordering_service_node(
                        new_ordering_service_node['id'],
                        new_ordering_service_node)
                    changed = True

                # Move to the next ordering service node.
                i = i + 1

        # Wait for the ordering service to start.
        ordering_service = OrderingService.from_json(
            console.extract_ordering_service_info(ordering_service))
        timeout = module.params['wait_timeout']
        ordering_service.wait_for(timeout)

        # Return the ordering service.
        module.exit_json(changed=changed,
                         ordering_service=ordering_service.to_json())

    # Notify Ansible of the exception.
    except Exception as e:
        module.fail_json(msg=to_native(e))
Пример #22
0
def main():

    # Create the module.
    argument_spec = dict(
        api_endpoint=dict(type='str'),
        api_authtype=dict(type='str', choices=['ibmcloud', 'basic']),
        api_key=dict(type='str', no_log=True),
        api_secret=dict(type='str', no_log=True),
        api_timeout=dict(type='int', default=60),
        api_token_endpoint=dict(
            type='str', default='https://iam.cloud.ibm.com/identity/token'),
        operation=dict(type='str',
                       required=True,
                       choices=[
                           'create', 'fetch', 'compute_update', 'sign_update',
                           'apply_update'
                       ]),
        ordering_service=dict(type='raw'),
        ordering_service_nodes=dict(type='list', elements='raw'),
        tls_handshake_time_shift=dict(
            type='str',
            fallback=(env_fallback, ['IBP_TLS_HANDSHAKE_TIME_SHIFT'])),
        identity=dict(type='raw'),
        msp_id=dict(type='str'),
        hsm=dict(type='dict',
                 options=dict(pkcs11library=dict(type='str', required=True),
                              label=dict(type='str',
                                         required=True,
                                         no_log=True),
                              pin=dict(type='str', required=True,
                                       no_log=True))),
        name=dict(type='str'),
        path=dict(type='str'),
        original=dict(type='str'),
        updated=dict(type='str'),
        organizations=dict(type='list', elements='raw'),
        policies=dict(type='dict'),
        capabilities=dict(type='dict',
                          default=dict(),
                          options=dict(application=dict(type='str',
                                                        default='V1_4_2'),
                                       channel=dict(type='str'),
                                       orderer=dict(type='str'))),
        parameters=dict(type='dict',
                        default=dict(),
                        options=dict(batch_size=dict(
                            type='dict',
                            options=dict(
                                max_message_count=dict(type='int'),
                                absolute_max_bytes=dict(type='int'),
                                preferred_max_bytes=dict(type='int'))),
                                     batch_timeout=dict(type='str'))))
    required_if = [('api_authtype', 'basic', ['api_secret']),
                   ('operation', 'create', [
                       'api_endpoint', 'api_authtype', 'api_key',
                       'organizations', 'policies', 'name', 'path'
                   ]),
                   ('operation', 'fetch', [
                       'api_endpoint', 'api_authtype', 'api_key', 'identity',
                       'msp_id', 'name', 'path'
                   ]),
                   ('operation', 'compute_update',
                    ['name', 'path', 'original', 'updated']),
                   ('operation', 'sign_update',
                    ['identity', 'msp_id', 'name', 'path']),
                   ('operation', 'apply_update', [
                       'api_endpoint', 'api_authtype', 'api_key', 'identity',
                       'msp_id', 'name', 'path'
                   ])]
    # Ansible doesn't allow us to say "require one of X and Y only if condition A is true",
    # so we need to handle this ourselves by seeing what was passed in.
    actual_params = _load_params()
    if actual_params.get('operation', None) in ['fetch', 'apply_update']:
        required_one_of = [['ordering_service', 'ordering_service_nodes']]
    else:
        required_one_of = []
    module = BlockchainModule(argument_spec=argument_spec,
                              supports_check_mode=True,
                              required_if=required_if,
                              required_one_of=required_one_of)

    # Validate HSM requirements if HSM is specified.
    if module.params['hsm']:
        module.check_for_missing_hsm_libs()

    # Ensure all exceptions are caught.
    try:
        operation = module.params['operation']
        if operation == 'create':
            create(module)
        elif operation == 'fetch':
            fetch(module)
        elif operation == 'compute_update':
            compute_update(module)
        elif operation == 'sign_update':
            sign_update(module)
        elif operation == 'apply_update':
            apply_update(module)
        else:
            raise Exception(f'Invalid operation {operation}')

    # Notify Ansible of the exception.
    except Exception as e:
        module.fail_json(msg=to_native(e))
Пример #23
0
def main():

    # Create the module.
    argument_spec = dict(
        state=dict(type='str',
                   default='present',
                   choices=['present', 'absent']),
        api_endpoint=dict(type='str', required=True),
        api_authtype=dict(type='str',
                          required=True,
                          choices=['ibmcloud', 'basic']),
        api_key=dict(type='str', required=True),
        api_secret=dict(type='str'),
        api_timeout=dict(type='int', default=60),
        api_token_endpoint=dict(
            type='str', default='https://iam.cloud.ibm.com/identity/token'),
        name=dict(type='str', required=True),
        msp_id=dict(type='str'),
        state_db=dict(type='str',
                      default='couchdb',
                      choices=['couchdb', 'leveldb']),
        certificate_authority=dict(type='raw'),
        enrollment_id=dict(type='str'),
        enrollment_secret=dict(type='str'),
        admin_certificates=dict(type='list', elements='str'),
        config=dict(type='dict'),
        config_override=dict(type='dict', default=dict()),
        resources=dict(
            type='dict',
            default=dict(),
            options=dict(
                peer=dict(
                    type='dict',
                    default=dict(),
                    options=dict(requests=dict(
                        type='dict',
                        default=dict(),
                        options=dict(cpu=dict(type='str', default='200m'),
                                     memory=dict(type='str', default='1G'))))),
                proxy=dict(
                    type='dict',
                    default=dict(),
                    options=dict(requests=dict(
                        type='dict',
                        default=dict(),
                        options=dict(cpu=dict(type='str', default='100m'),
                                     memory=dict(type='str',
                                                 default='200M'))))),
                couchdb=dict(
                    type='dict',
                    default=dict(),
                    options=dict(requests=dict(
                        type='dict',
                        default=dict(),
                        options=dict(cpu=dict(type='str', default='200m'),
                                     memory=dict(type='str',
                                                 default='400M'))))),
                dind=dict(type='dict',
                          default=dict(),
                          options=dict(requests=dict(
                              type='dict',
                              default=dict(),
                              options=dict(cpu=dict(type='str', default='1'),
                                           memory=dict(type='str',
                                                       default='1G'))))))),
        storage=dict(type='dict',
                     default=dict(),
                     options=dict(peer=dict(type='dict',
                                            default=dict(),
                                            options={
                                                'size':
                                                dict(type='str',
                                                     default='100Gi'),
                                                'class':
                                                dict(type='str',
                                                     default='default')
                                            }),
                                  statedb=dict(type='dict',
                                               default=dict(),
                                               options={
                                                   'size':
                                                   dict(type='str',
                                                        default='100Gi'),
                                                   'class':
                                                   dict(type='str',
                                                        default='default')
                                               }))),
        hsm=dict(type='dict',
                 options=dict(pkcs11endpoint=dict(type='str', required=True),
                              label=dict(type='str', required=True),
                              pin=dict(type='str', required=True))),
        wait_timeout=dict(type='int', default=60))
    required_if = [('api_authtype', 'basic', ['api_secret']),
                   ('state', 'present', ['name', 'msp_id'])]
    required_one_of = [['certificate_authority', 'config']]
    required_together = [['certificate_authority', 'enrollment_id'],
                         ['certificate_authority', 'enrollment_secret'],
                         ['certificate_authority', 'admin_certificates']]
    mutually_exclusive = [['certificate_authority', 'config']]
    module = AnsibleModule(argument_spec=argument_spec,
                           supports_check_mode=True,
                           required_if=required_if,
                           required_one_of=required_one_of,
                           required_together=required_together,
                           mutually_exclusive=mutually_exclusive)

    # Ensure all exceptions are caught.
    try:

        # Log in to the IBP console.
        console = get_console(module)

        # Determine if the peer exists.
        name = module.params['name']
        peer = console.get_component_by_display_name(
            name, deployment_attrs='included')
        peer_exists = peer is not None

        # If this is a free cluster, we cannot accept resource/storage configuration,
        # as these are ignored for free clusters. We must also delete the defaults,
        # otherwise they cause a mismatch with the values that actually get set.
        if console.is_free_cluster():
            actual_params = _load_params()
            if 'resources' in actual_params or 'storage' in actual_params:
                raise Exception(
                    f'Cannot specify resources or storage for a free IBM Kubernetes Service cluster'
                )
            if peer_exists:
                module.params['resources'] = dict()
                module.params['storage'] = dict()

        # If the peer should not exist, handle that now.
        state = module.params['state']
        if state == 'absent' and peer_exists:

            # The peer should not exist, so delete it.
            console.delete_peer(peer['id'])
            return module.exit_json(changed=True)

        elif state == 'absent':

            # The peer should not exist and doesn't.
            return module.exit_json(changed=False)

        # Extract the expected peer configuration.
        expected_peer = dict(display_name=name,
                             msp_id=module.params['msp_id'],
                             state_db=module.params['state_db'],
                             config_override=module.params['config_override'],
                             resources=module.params['resources'],
                             storage=module.params['storage'])

        # Add the HSM configuration if it is specified.
        hsm = module.params['hsm']
        if hsm is not None:
            pkcs11endpoint = hsm['pkcs11endpoint']
            expected_peer['hsm'] = dict(pkcs11endpoint=pkcs11endpoint)
            hsm_config_override = dict(peer=dict(
                BCCSP=dict(Default='PKCS11',
                           PKCS11=dict(Label=hsm['label'], Pin=hsm['pin']))))
            merge_dicts(expected_peer['config_override'], hsm_config_override)

        # Either create or update the peer.
        changed = False
        if state == 'present' and not peer_exists:

            # Get the config.
            expected_peer['config'] = get_config(console, module)

            # Create the peer.
            peer = console.create_peer(expected_peer)
            changed = True

        elif state == 'present' and peer_exists:

            # HACK: never send the limits back, as they are rejected.
            for thing in ['peer', 'proxy', 'couchdb', 'dind']:
                if thing in peer['resources']:
                    if 'limits' in peer['resources'][thing]:
                        del peer['resources'][thing]['limits']

            # Update the peer configuration.
            new_peer = copy_dict(peer)
            merge_dicts(new_peer, expected_peer)

            # Check to see if any banned changes have been made.
            banned_changes = ['msp_id', 'state_db', 'storage']
            diff = diff_dicts(peer, new_peer)
            for banned_change in banned_changes:
                if banned_change in diff:
                    raise Exception(
                        f'{banned_change} cannot be changed from {peer[banned_change]} to {new_peer[banned_change]} for existing peer'
                    )

            # If the peer has changed, apply the changes.
            peer_changed = not equal_dicts(peer, new_peer)
            if peer_changed:
                peer = console.update_peer(new_peer['id'], new_peer)
                changed = True

        # Wait for the peer to start.
        peer = Peer.from_json(console.extract_peer_info(peer))
        timeout = module.params['wait_timeout']
        peer.wait_for(timeout)

        # Return the peer.
        module.exit_json(changed=changed, peer=peer.to_json())

    # Notify Ansible of the exception.
    except Exception as e:
        module.fail_json(msg=to_native(e))
Пример #24
0
def main():

    # Create the module.
    argument_spec = dict(
        state=dict(type='str', default='present', choices=['present', 'absent']),
        api_endpoint=dict(type='str', required=True),
        api_authtype=dict(type='str', required=True, choices=['ibmcloud', 'basic']),
        api_key=dict(type='str', required=True, no_log=True),
        api_secret=dict(type='str', no_log=True),
        api_timeout=dict(type='int', default=60),
        api_token_endpoint=dict(type='str', default='https://iam.cloud.ibm.com/identity/token'),
        name=dict(type='str', required=True),
        msp_id=dict(type='str'),
        orderer_type=dict(type='str', default='raft', choices=['raft']),
        system_channel_id=dict(type='str', default='testchainid'),
        certificate_authority=dict(type='raw'),
        enrollment_id=dict(type='str'),
        enrollment_secret=dict(type='str', no_log=True),
        admins=dict(type='list', elements='str', aliases=['admin_certificates']),
        nodes=dict(type='int'),
        config=dict(type='list', elements='dict'),
        config_override=dict(type='list'),
        resources=dict(type='dict', default=dict(), options=dict(
            orderer=dict(type='dict', default=dict(), options=dict(
                requests=dict(type='dict', default=dict(), options=dict(
                    cpu=dict(type='str', default='250m'),
                    memory=dict(type='str', default='500M')
                ))
            )),
            proxy=dict(type='dict', default=dict(), options=dict(
                requests=dict(type='dict', default=dict(), options=dict(
                    cpu=dict(type='str', default='100m'),
                    memory=dict(type='str', default='200M')
                ))
            ))
        )),
        storage=dict(type='dict', default=dict(), options=dict(
            orderer=dict(type='dict', default=dict(), options={
                'size': dict(type='str', default='100Gi'),
                'class': dict(type='str', default='default')
            })
        )),
        hsm=dict(type='dict', options=dict(
            pkcs11endpoint=dict(type='str', required=True),
            label=dict(type='str', required=True, no_log=True),
            pin=dict(type='str', required=True, no_log=True)
        )),
        zones=dict(type='list', elements='str', aliases=['zone']),
        version=dict(type='str'),
        wait_timeout=dict(type='int', default=60)
    )
    required_if = [
        ('api_authtype', 'basic', ['api_secret']),
        ('state', 'present', ['msp_id', 'nodes'])
    ]
    # Ansible doesn't allow us to say "require one of X and Y only if condition A is true",
    # so we need to handle this ourselves by seeing what was passed in.
    actual_params = _load_params()
    if actual_params.get('state', 'present') == 'present':
        required_one_of = [
            ['certificate_authority', 'config']
        ]
    else:
        required_one_of = []
    required_together = [
        ['certificate_authority', 'enrollment_id'],
        ['certificate_authority', 'enrollment_secret'],
        ['certificate_authority', 'admins']
    ]
    mutually_exclusive = [
        ['certificate_authority', 'config']
    ]
    module = BlockchainModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        required_if=required_if,
        required_one_of=required_one_of,
        required_together=required_together,
        mutually_exclusive=mutually_exclusive)

    # Ensure all exceptions are caught.
    try:

        # Log in to the IBP console.
        console = get_console(module)

        # Determine if the ordering service exists.
        name = module.params['name']
        ordering_service = console.get_components_by_cluster_name(name, deployment_attrs='included')
        ordering_service_exists = len(ordering_service) > 0
        ordering_service_corrupt_nodes = 0
        for ordering_service_node in ordering_service:
            if 'deployment_attrs_missing' in ordering_service_node:
                ordering_service_corrupt_nodes += 1
        ordering_service_corrupt = ordering_service_corrupt_nodes > 0

        # If this is a free cluster, we cannot accept resource/storage configuration,
        # as these are ignored for free clusters. We must also delete the defaults,
        # otherwise they cause a mismatch with the values that actually get set.
        if console.is_free_cluster():
            actual_params = _load_params()
            if 'resources' in actual_params or 'storage' in actual_params:
                raise Exception('Cannot specify resources or storage for a free IBM Kubernetes Service cluster')
            if ordering_service_exists:
                module.params['resources'] = dict()
                module.params['storage'] = dict()

        # If the ordering service should not exist, handle that now.
        state = module.params['state']
        if state == 'absent' and ordering_service_exists:

            # The ordering service should not exist, so delete it.
            console.delete_ordering_service(ordering_service[0]['cluster_id'])
            return module.exit_json(changed=True)

        elif state == 'absent':

            # The ordering service should not exist and doesn't.
            return module.exit_json(changed=False)

        # Compute the HSM configuration if it is specified.
        hsm = module.params['hsm']
        if hsm is not None:
            pkcs11endpoint = hsm['pkcs11endpoint']
            hsm_config_override = dict(
                General=dict(
                    BCCSP=dict(
                        Default='PKCS11',
                        PKCS11=dict(
                            Label=hsm['label'],
                            Pin=hsm['pin']
                        )
                    )
                )
            )

        # If the ordering service is corrupt, delete it first. This may happen if somebody imported an external ordering
        # service with the same name, or if somebody deleted the Kubernetes resources directly.
        changed = False
        if ordering_service_corrupt:
            # We can't handle an ordering service where some of the nodes are in Kubernetes
            # and some aren't. We don't want to delete the entire ordering service if we don't
            # know it's all corrupt as that will lose data, and since some of the nodes exist
            # then the ordering service should be recoverable.
            if ordering_service_corrupt_nodes < len(ordering_service):
                raise Exception('Some ordering service nodes exist in console but not in Kubernetes, refusing to continue')
            module.warn('Ordering service exists in console but not in Kubernetes, deleting it before continuing')
            console.delete_ext_ordering_service(ordering_service[0]['cluster_id'])
            ordering_service_exists = ordering_service_corrupt = False
            changed = True

        # Either create or update the ordering service.
        changed = False
        if state == 'present' and not ordering_service_exists:

            # Get the config.
            config = get_config(console, module)

            # Get the config overrides.
            nodes = module.params['nodes']
            config_override_list = module.params['config_override']
            if config_override_list is not None:
                if len(config_override_list) != nodes:
                    raise Exception(f'Number of nodes is {nodes}, but only {len(config_override_list)} config override objects provided')
            else:
                config_override_list = list()
                i = 0
                while i < nodes:
                    config_override_list.append(dict())
                    i = i + 1

            # Extract the expected ordering service configuration.
            expected_ordering_service = dict(
                display_name=name,
                cluster_name=name,
                msp_id=module.params['msp_id'],
                orderer_type=module.params['orderer_type'],
                system_channel_id=module.params['system_channel_id'],
                config=config,
                config_override=config_override_list,
                resources=module.params['resources'],
                storage=module.params['storage']
            )

            # Delete the resources and storage configuration for a new ordering
            # service being deployed to a free cluster.
            if console.is_free_cluster():
                del expected_ordering_service['resources']
                del expected_ordering_service['storage']

            # Add the HSM configuration if it is specified.
            if hsm is not None:
                expected_ordering_service['hsm'] = dict(pkcs11endpoint=pkcs11endpoint)
                for config_override in config_override_list:
                    merge_dicts(config_override, hsm_config_override)

            # Add the zones if they are specified.
            zones = module.params['zones']
            if zones is not None:
                if len(zones) != nodes:
                    raise Exception(f'Number of nodes is {nodes}, but only {len(zones)} zones provided')
                expected_ordering_service['zone'] = zones

            # Add the version if it is specified.
            version = module.params['version']
            if version is not None:
                expected_ordering_service['version'] = version

            # Create the ordering service.
            ordering_service = console.create_ordering_service(expected_ordering_service)
            changed = True

        elif state == 'present' and ordering_service_exists:

            # Check to see if the number of ordering service nodes has changed.
            nodes = module.params['nodes']
            if nodes != len(ordering_service):
                raise Exception(f'Number of nodes cannot be changed from {len(ordering_service)} to {nodes} for existing ordering service')
            config_override_list = module.params['config_override']
            if config_override_list is not None:
                if len(config_override_list) != nodes:
                    raise Exception(f'Number of nodes is {nodes}, but only {len(config_override_list)} config override objects provided')

            # Go through each ordering service node.
            i = 0
            while i < len(ordering_service):

                # Get the ordering service node.
                ordering_service_node = ordering_service[i]

                # HACK: never send the limits back, as they are rejected.
                for thing in ['orderer', 'proxy']:
                    if thing in ordering_service_node['resources']:
                        if 'limits' in ordering_service_node['resources'][thing]:
                            del ordering_service_node['resources'][thing]['limits']

                # HACK: never send the init resources back, as they are rejected.
                ordering_service_node['resources'].pop('init', None)

                # Get the config overrides.
                if config_override_list is not None:
                    config_override = config_override[i]
                else:
                    config_override = dict()

                # Extract the expected ordering service node configuration.
                expected_ordering_service_node = dict(
                    msp_id=module.params['msp_id'],
                    orderer_type=module.params['orderer_type'],
                    system_channel_id=module.params['system_channel_id'],
                    config_override=config_override,
                    resources=module.params['resources'],
                    storage=module.params['storage']
                )

                # Add the HSM configuration if it is specified.
                if hsm is not None:
                    expected_ordering_service_node['hsm'] = dict(pkcs11endpoint=pkcs11endpoint)
                    merge_dicts(config_override, hsm_config_override)

                # Add the zone if it is specified.
                zones = module.params['zones']
                if zones is not None:
                    if len(zones) != nodes:
                        raise Exception(f'Number of nodes is {nodes}, but only {len(zones)} zones provided')
                    expected_ordering_service_node['zone'] = zones[i]

                # Add the version if it is specified.
                version = module.params['version']
                if version is not None:
                    expected_ordering_service_node['version'] = version

                # Update the ordering service node configuration.
                new_ordering_service_node = copy_dict(ordering_service_node)
                merge_dicts(new_ordering_service_node, expected_ordering_service_node)

                # Check to see if any banned changes have been made.
                # HACK: zone is documented as a permitted change, but it has no effect.
                permitted_changes = ['resources', 'config_override', 'version']
                diff = diff_dicts(ordering_service_node, new_ordering_service_node)
                for change in diff:
                    if change not in permitted_changes:
                        raise Exception(f'{change} cannot be changed from {ordering_service_node[change]} to {new_ordering_service_node[change]} for existing ordering service node')

                # HACK: if the version has not changed, do not send it in. The current
                # version may not be supported by the current version of IBP.
                if ordering_service_node['version'] == new_ordering_service_node['version']:
                    del ordering_service_node['version']
                    del new_ordering_service_node['version']

                # If the ordering service node has changed, apply the changes.
                ordering_service_node_changed = not equal_dicts(ordering_service_node, new_ordering_service_node)
                if ordering_service_node_changed:
                    ordering_service[i] = console.update_ordering_service_node(new_ordering_service_node['id'], new_ordering_service_node)
                    changed = True

                # Now need to compare the list of admin certs. The admin certs may be passed in via
                # three different parameters (admins, config.enrollment.component.admincerts, and
                # config.msp.component.admincerts) so we need to find them.
                # HACK: if the admin certs did not get returned, we're running on IBP v2.1.3
                # and it does not support this feature.
                expected_admins = module.params['admins']
                if not expected_admins:
                    config = module.params['config']
                    if config:
                        node_config = config[i]
                        for config_type in ['enrollment', 'msp']:
                            expected_admins = node_config.get(config_type, dict()).get('component', dict()).get('admincerts', None)
                            if expected_admins:
                                break
                if expected_admins:
                    expected_admins_set = set(map(normalize_whitespace, expected_admins))
                    actual_admins = ordering_service_node.get('admin_certs', None)
                    if actual_admins is not None:
                        actual_admins_set = set(map(normalize_whitespace, actual_admins))
                        append_admin_certs = list(expected_admins_set.difference(actual_admins_set))
                        remove_admin_certs = list(actual_admins_set.difference(expected_admins_set))
                        if append_admin_certs or remove_admin_certs:
                            console.edit_admin_certs(ordering_service_node['id'], append_admin_certs, remove_admin_certs)
                            changed = True

                # Move to the next ordering service node.
                i = i + 1

        # Wait for the ordering service to start.
        ordering_service = OrderingService.from_json(console.extract_ordering_service_info(ordering_service))
        timeout = module.params['wait_timeout']
        ordering_service.wait_for(timeout)

        # Return the ordering service.
        module.exit_json(changed=changed, ordering_service=ordering_service.to_json())

    # Notify Ansible of the exception.
    except Exception as e:
        module.fail_json(msg=to_native(e))
def main():

    # Create the module.
    argument_spec = dict(
        state=dict(type='str',
                   default='present',
                   choices=['present', 'absent']),
        api_endpoint=dict(type='str', required=True),
        api_authtype=dict(type='str',
                          required=True,
                          choices=['ibmcloud', 'basic']),
        api_key=dict(type='str', required=True, no_log=True),
        api_secret=dict(type='str', no_log=True),
        api_timeout=dict(type='int', default=60),
        api_token_endpoint=dict(
            type='str', default='https://iam.cloud.ibm.com/identity/token'),
        peer=dict(type='raw', required=True),
        identity=dict(type='raw', required=True),
        msp_id=dict(type='str', required=True),
        hsm=dict(type='dict',
                 options=dict(pkcs11library=dict(type='str', required=True),
                              label=dict(type='str',
                                         required=True,
                                         no_log=True),
                              pin=dict(type='str', required=True,
                                       no_log=True))),
        name=dict(type='str'),
        version=dict(type='str'),
        path=dict(type='str'),
        package_id=dict(type='str'))
    required_if = [('api_authtype', 'basic', ['api_secret']),
                   ('state', 'present', ['path'])]
    # Ansible doesn't allow us to say "require one of X and Y only if condition A is true",
    # so we need to handle this ourselves by seeing what was passed in.
    actual_params = _load_params()
    if actual_params.get('state', 'present') == 'absent':
        required_one_of = [['name', 'package_id']]
    else:
        required_one_of = []
    required_together = [['name', 'version']]
    mutually_exclusive = [['name', 'path'], ['version', 'path'],
                          ['package_id', 'path'], ['package_id', 'name']]
    module = BlockchainModule(argument_spec=argument_spec,
                              supports_check_mode=True,
                              required_if=required_if,
                              required_one_of=required_one_of,
                              required_together=required_together,
                              mutually_exclusive=mutually_exclusive)

    # Validate HSM requirements if HSM is specified.
    if module.params['hsm']:
        module.check_for_missing_hsm_libs()

    # Ensure all exceptions are caught.
    try:

        # Log in to the IBP console.
        console = get_console(module)

        # Get the peer, identity, and MSP ID.
        peer = get_peer_by_module(console, module)
        identity = get_identity_by_module(module)
        msp_id = module.params['msp_id']
        hsm = module.params['hsm']
        identity = resolve_identity(console, module, identity, msp_id)

        # Determine which lifecycle we are using.
        path = module.params['path']
        package_id = module.params['package_id']
        if path is not None:
            new_lifecycle = tarfile.is_tarfile(path)
        else:
            new_lifecycle = package_id is not None

        # Switch to new lifecycle code if requird.
        if new_lifecycle:
            return do_new_lifecycle(module, console, peer, identity, msp_id,
                                    hsm)
        else:
            return do_old_lifecycle(module, console, peer, identity, msp_id,
                                    hsm)

    # Notify Ansible of the exception.
    except Exception as e:
        module.fail_json(msg=to_native(e))
def main():

    # Create the module.
    argument_spec = dict(
        state=dict(type='str',
                   default='present',
                   choices=['present', 'absent']),
        api_endpoint=dict(type='str', required=True),
        api_authtype=dict(type='str',
                          required=True,
                          choices=['ibmcloud', 'basic']),
        api_key=dict(type='str', required=True),
        api_secret=dict(type='str'),
        api_timeout=dict(type='int', default=60),
        api_token_endpoint=dict(
            type='str', default='https://iam.cloud.ibm.com/identity/token'),
        name=dict(type='str', required=True),
        config_override=dict(type='dict', default=dict()),
        resources=dict(
            type='dict',
            default=dict(),
            options=dict(ca=dict(
                type='dict',
                default=dict(),
                options=dict(requests=dict(
                    type='dict',
                    default=dict(),
                    options=dict(cpu=dict(type='str', default='100m'),
                                 memory=dict(type='str', default='200M'))))))),
        storage=dict(
            type='dict',
            default=dict(),
            options=dict(
                ca=dict(type='dict',
                        default=dict(),
                        options={
                            'size': dict(type='str', default='20Gi'),
                            'class': dict(type='str', default='default')
                        }))),
        hsm=dict(type='dict',
                 options=dict(pkcs11endpoint=dict(type='str', required=True),
                              label=dict(type='str', required=True),
                              pin=dict(type='str', required=True))),
        wait_timeout=dict(type='int', default=60))
    required_if = [('api_authtype', 'basic', ['api_secret'])]
    module = AnsibleModule(argument_spec=argument_spec,
                           supports_check_mode=True,
                           required_if=required_if)

    # Ensure all exceptions are caught.
    try:

        # Log in to the IBP console.
        console = get_console(module)

        # Determine if the certificate authority exists.
        name = module.params['name']
        certificate_authority = console.get_component_by_display_name(
            name, deployment_attrs='included')
        certificate_authority_exists = certificate_authority is not None

        # If this is a free cluster, we cannot accept resource/storage configuration,
        # as these are ignored for free clusters. We must also delete the defaults,
        # otherwise they cause a mismatch with the values that actually get set.
        if console.is_free_cluster():
            actual_params = _load_params()
            if 'resources' in actual_params or 'storage' in actual_params:
                raise Exception(
                    f'Cannot specify resources or storage for a free IBM Kubernetes Service cluster'
                )
            if certificate_authority_exists:
                module.params['resources'] = dict()
                module.params['storage'] = dict()

        # If the certificate authority should not exist, handle that now.
        state = module.params['state']
        if state == 'absent' and certificate_authority_exists:

            # The certificate authority should not exist, so delete it.
            console.delete_ca(certificate_authority['id'])
            return module.exit_json(changed=True)

        elif state == 'absent':

            # The certificate authority should not exist and doesn't.
            return module.exit_json(changed=False)

        # Extract the expected certificate authority configuration.
        expected_certificate_authority = dict(
            display_name=name,
            config_override=module.params['config_override'],
            resources=module.params['resources'],
            storage=module.params['storage'])

        # Add the HSM configuration if it is specified.
        hsm = module.params['hsm']
        if hsm is not None:
            pkcs11endpoint = hsm['pkcs11endpoint']
            expected_certificate_authority['hsm'] = dict(
                pkcs11endpoint=pkcs11endpoint)
            bccsp = dict(
                BCCSP=dict(Default='PKCS11',
                           PKCS11=dict(Label=hsm['label'], Pin=hsm['pin'])))
            ca = expected_certificate_authority['config_override'].setdefault(
                'ca', dict())
            tlsca = expected_certificate_authority['config_override'].get(
                'tlsca', None)
            merge_dicts(ca, bccsp)
            if tlsca is not None:
                merge_dicts(tlsca, bccsp)

        # Either create or update the peer.
        changed = False
        if state == 'present' and not certificate_authority_exists:

            # Create the certificate authority.
            certificate_authority = console.create_ca(
                expected_certificate_authority)
            changed = True

        elif state == 'present' and certificate_authority_exists:

            # Update the certificate authority configuration.
            new_certificate_authority = copy_dict(certificate_authority)
            merge_dicts(new_certificate_authority,
                        expected_certificate_authority)

            # You can't change the registry after creation, but the remote names and secrets are redacted.
            # In order to diff properly, we need to redact the incoming secrets.
            identities = new_certificate_authority.get(
                'config_override',
                dict()).get('ca',
                            dict()).get('registry',
                                        dict()).get('identities', list())
            for identity in identities:
                if 'name' in identity:
                    identity['name'] = '[redacted]'
                if 'pass' in identity:
                    identity['pass'] = '******'
            identities = new_certificate_authority.get(
                'config_override',
                dict()).get('tlsca',
                            dict()).get('registry',
                                        dict()).get('identities', list())
            for identity in identities:
                if 'name' in identity:
                    identity['name'] = '[redacted]'
                if 'pass' in identity:
                    identity['pass'] = '******'

            # Check to see if any banned changes have been made.
            banned_changes = ['storage']
            diff = diff_dicts(certificate_authority, new_certificate_authority)
            for banned_change in banned_changes:
                if banned_change in diff:
                    raise Exception(
                        f'{banned_change} cannot be changed from {certificate_authority[banned_change]} to {new_certificate_authority[banned_change]} for existing certificate authority'
                    )

            # If the certificate authority has changed, apply the changes.
            certificate_authority_changed = not equal_dicts(
                certificate_authority, new_certificate_authority)
            if certificate_authority_changed:
                certificate_authority = console.update_ca(
                    new_certificate_authority['id'], new_certificate_authority)
                changed = True

        # Wait for the certificate authority to start.
        certificate_authority = CertificateAuthority.from_json(
            console.extract_ca_info(certificate_authority))
        timeout = module.params['wait_timeout']
        certificate_authority.wait_for(timeout)

        # Return the certificate authority.
        module.exit_json(changed=changed,
                         certificate_authority=certificate_authority.to_json())

    # Notify Ansible of the exception.
    except Exception as e:
        module.fail_json(msg=to_native(e))
 def __init__(self, *args, **kwargs):
     params = _load_params() or {}
     if params.get('secured'):
         kwargs['argument_spec']['value'].update({'no_log': True})
     super(BitBucketPipelineVariable, self).__init__(*args, **kwargs)