def test_api_version(self):
     with HTTMock(api_mock):
         api_default = MiqApi('http://example.com/api', ('admin', 'admin'),
                              verify_ssl=False)
         api_version = api_default.api_version('2.4.0')
         assert api_default._entry_point == 'http://example.com/api'
         assert api_version._entry_point == 'http://example.com/api/v2.4.0'
         assert api_default._auth == api_version._auth
         assert api_default.logger is api_version.logger
         assert api_default._verify_ssl == api_version._verify_ssl
         assert api_default._ca_bundle_path == api_version._ca_bundle_path
예제 #2
0
 def __init__(self,
              manageiq_aws_access_key,
              manageiq_aws_secret_key,
              manageiq_home_directory,
              manageiq_endpoint='https://127.0.0.1:8443/',
              manageiq_username='******',
              manageiq_password='******',
              manageiq_region='us-east-2',
              manageiq_provider_name='API_call',
              manageiq_ports=None,
              manageiq_docker_image='manageiq/manageiq:gaprindashvili-3'):
     if manageiq_ports is None:
         manageiq_ports = {'443/tcp': 8443}
     self.manageiq_home_directory = manageiq_home_directory
     self.manageiq_aws_access_key = manageiq_aws_access_key
     self.manageiq_aws_secret_key = manageiq_aws_secret_key
     self.manageiq_endpoint = manageiq_endpoint
     self.manageiq_username = manageiq_username
     self.manageiq_password = manageiq_password
     self.manageiq_region = manageiq_region
     self.manageiq_provider_name = manageiq_provider_name
     self.manageiq_ports = manageiq_ports
     self.manageiq_docker_image = manageiq_docker_image
     try:
         self.client = MiqApi(self.manageiq_endpoint + 'api',
                              dict(user=self.manageiq_username,
                                   password=self.manageiq_password),
                              verify_ssl=False)
     except ConnectionError:
         self.client = None
     self.provider = None
 def __init__(self, module, url, user, password, miq_verify_ssl, ca_bundle_path):
     self.module   = module
     self.api_url  = url + '/api'
     self.user     = user
     self.password = password
     self.client   = MiqApi(self.api_url, (self.user, self.password), verify_ssl=miq_verify_ssl, ca_bundle_path=ca_bundle_path)
     self.changed  = False
예제 #4
0
 def _connect(self):
     """
     Create new manageIQClient pointer and assign to self._client
     """
     try:
         self._client = ManageIQClient(self._url,
                                       dict(token=self._token),
                                       verify_ssl=self._verify_ssl)
     except APIException as e:
         log.abort('Error creating library pointer - {0}'.format(e.message))
     except Exception as e:
         log.abort('{0}'.format(e.message))
예제 #5
0
    def __init__(self, module):
        # handle import errors
        check_client(module)

        params = validate_connection_params(module)

        url = params['url']
        username = params['username']
        password = params['password']
        token = params['token']
        verify_ssl = params['verify_ssl']
        ca_bundle_path = params['ca_bundle_path']

        self._module = module
        self._api_url = url + '/api'
        self._auth = dict(user=username, password=password, token=token)
        try:
            self._client = ManageIQClient(self._api_url, self._auth, verify_ssl=verify_ssl, ca_bundle_path=ca_bundle_path)
        except Exception as e:
            self.module.fail_json(msg="failed to open connection (%s): %s" % (url, str(e)))
예제 #6
0
    def __init__(self, module):
        # handle import errors
        check_client(module)

        params = validate_connection_params(module)

        url = params['url']
        username = params['username']
        password = params['password']
        token = params['token']
        verify_ssl = params['verify_ssl']
        ca_bundle_path = params['ca_bundle_path']

        self._module = module
        self._api_url = url + '/api'
        self._auth = dict(user=username, password=password, token=token)
        self._client = ManageIQClient(self._api_url,
                                      self._auth,
                                      verify_ssl=verify_ssl,
                                      ca_bundle_path=ca_bundle_path)
예제 #7
0
    def __init__(self, module):
        # handle import errors
        check_client(module)

        params = module.params['manageiq_connection']

        # check for required arguments
        for arg in ['url', 'username', 'password']:
            if params[arg] in (None, ''):
                module.fail_json(
                    msg="missing required argument: manageiq_connection[{}]".
                    format(arg))

        url = params['url']
        username = params['username']
        password = params['password']
        verify_ssl = params['verify_ssl']
        ca_bundle_path = params['ca_bundle_path']

        self._module = module
        self._api_url = url + '/api'
        self._client = ManageIQClient(self._api_url, (username, password),
                                      verify_ssl=verify_ssl,
                                      ca_bundle_path=ca_bundle_path)
예제 #8
0
class ManageIQProvider(object):
    """ ManageIQ object to execute various operations in manageiq

    url            - manageiq environment url
    user           - the username in manageiq
    password       - the user password in manageiq
    miq_verify_ssl - whether SSL certificates should be verified for HTTPS requests
    ca_bundle_path - the path to a CA_BUNDLE file or directory with certificates
    """

    OPENSHIFT_DEFAULT_PORT = '8443'

    PROVIDER_TYPES = {
        'openshift-origin':
        'ManageIQ::Providers::Openshift::ContainerManager',
        'openshift-enterprise':
        'ManageIQ::Providers::OpenshiftEnterprise::ContainerManager',
        'amazon':
        'ManageIQ::Providers::Amazon::CloudManager',
        'hawkular-datawarehouse':
        "ManageIQ::Providers::Hawkular::DatawarehouseManager",
    }

    WAIT_TIME = 5
    ITERATIONS = 10

    def __init__(self, module, url, user, password, miq_verify_ssl,
                 ca_bundle_path):
        self.module = module
        self.api_url = url + '/api'
        self.user = user
        self.password = password
        self.client = MiqApi(self.api_url, (self.user, self.password),
                             verify_ssl=miq_verify_ssl,
                             ca_bundle_path=ca_bundle_path)
        self.changed = False
        self.providers_url = self.api_url + '/providers'

    def auths_validation_details(self, provider_id):
        try:
            result = self.client.get(
                '{providers_url}/{id}/?attributes=authentications'.format(
                    providers_url=self.providers_url, id=provider_id))
            auths = result.get('authentications', [])
            return {auth['authtype']: auth for auth in auths}
        except Exception as e:
            self.module.fail_json(
                msg="Failed to get provider data. Error: {!r}".format(e))

    def verify_authenticaion_validation(self, provider_id,
                                        old_validation_details,
                                        authtypes_to_verify):
        """ Verifies that the provider's authentication validation passed.
        provider_id            - the provider's id manageiq
        old_validation_details - a tuple of (last_valid_on, last_invalid_on), representing the last time
                                 that the authentication validation occured (success or failure).
        authtypes_to_verify    - a list of autentication types that require validation

        Returns a (result, details) tuple:
            result: 'Valid' if authentication validation passed for all endpoints, 'Invalid' if failed for any endpoint,
                    'Timed out' if any validation didn't complete in the assigned time
            details: Authentication validation details, 'Validation didn't complete' in case it timed out
        """
        def validated(old, new):
            """ Returns True if the validation timestamp, valid or invalid, is different
            from the old validation timestamp, False otherwise
            """
            return ((old.get('last_valid_on'), old.get('last_invalid_on')) !=
                    (new.get('last_valid_on'), new.get('last_invalid_on')))

        for i in range(ManageIQProvider.ITERATIONS):
            new_validation_details = self.auths_validation_details(provider_id)

            validations_done = True
            all_done_valid = "Valid"  # Out of the (re)validated ones.
            details = {}
            for t in authtypes_to_verify:
                old = old_validation_details.get(t, {})
                new = new_validation_details.get(t, {})
                if not validated(old, new):
                    details[t] = "Validation didn't complete"
                    validations_done = False
                else:
                    details[t] = (new.get('status'), new.get('status_details'))
                    if new.get('status') != 'Valid':
                        all_done_valid = "Invalid"

            if validations_done:
                return all_done_valid, details
            time.sleep(ManageIQProvider.WAIT_TIME)

        return "Timed out", details

    def get_provider_config(self, provider_id):
        """ get the endpoint content of existing provider from manageiq API"""
        try:
            result = self.client.get(
                '{providers_url}/{id}/?attributes=endpoints'.format(
                    providers_url=self.providers_url, id=provider_id))
            return result
        except Exception as e:
            self.module.fail_json(
                msg="Failed to get provider data. Error: {!r}".format(e))

    def required_updates(self, provider_id, endpoints, zone_id,
                         provider_region, existing_config):
        """ Checks whether an update is required for the provider

        Returns:
            Empty Hash (None) - If the hostname, port, zone and region passed equals
                                the provider's current values
            Hash of Changes   - Changes that need to be made if any endpoint, zone
                                or region are different than the current values of the
                                provider. The hash will have three entries:
                                    Updated, Removed, Added
                                that will contain all the changed endpoints
                                and their values.
        """
        def host_port_ssl(endpoint):
            return {
                'hostname': endpoint.get('hostname'),
                'port': endpoint.get('port'),
                'verify_ssl': endpoint.get('verify_ssl'),
                'certificate_authority': endpoint.get('certificate_authority'),
                'security_protocol': endpoint.get('security_protocol')
            }

        desired_by_role = {
            e['endpoint']['role']: host_port_ssl(e['endpoint'])
            for e in endpoints
        }
        existing_by_role = {
            e['role']: host_port_ssl(e)
            for e in existing_config['endpoints']
        }
        existing_provider_region = existing_config.get(
            'provider_region') or None
        if existing_by_role == desired_by_role and existing_config[
                'zone_id'] == zone_id and existing_provider_region == provider_region:
            return {}
        updated = {
            role: {
                k: v
                for k, v in ep.items() if k not in existing_by_role[role]
                or v != existing_by_role[role][k]
            }
            for role, ep in desired_by_role.items()
            if role in existing_by_role and ep != existing_by_role[role]
        }
        added = {
            role: ep
            for role, ep in desired_by_role.items()
            if role not in existing_by_role
        }
        removed = {
            role: ep
            for role, ep in existing_by_role.items()
            if role not in desired_by_role
        }
        if existing_config['zone_id'] != zone_id:
            updated['zone_id'] = zone_id
        if existing_provider_region != provider_region:
            updated['provider_region'] = provider_region
        return {"Updated": updated, "Added": added, "Removed": removed}

    def refresh_provider(self, provider_id):
        """ Performs a refresh of provider's inventory
        """
        try:
            self.client.post('{api_url}/providers/{id}'.format(
                api_url=self.api_url, id=provider_id),
                             action='refresh')
            self.changed = True
        except Exception as e:
            self.module.fail_json(
                msg="Failed to refresh provider. Error: {!r}".format(e))

    def update_provider(self, provider_id, provider_name, endpoints, zone_id,
                        provider_region):
        """ Updates the existing provider with new parameters
        """
        try:
            self.client.post('{api_url}/providers/{id}'.format(
                api_url=self.api_url, id=provider_id),
                             action='edit',
                             zone={'id': zone_id},
                             connection_configurations=endpoints,
                             provider_region=provider_region)
            self.changed = True
        except Exception as e:
            self.module.fail_json(
                msg="Failed to update provider. Error: {!r}".format(e))

    def add_new_provider(self, provider_name, provider_type, endpoints,
                         zone_id, provider_region):
        """ Adds a provider to manageiq

        Returns:
            the added provider id
        """
        try:
            result = self.client.post(
                self.providers_url,
                name=provider_name,
                type=ManageIQProvider.PROVIDER_TYPES[provider_type],
                zone={'id': zone_id},
                connection_configurations=endpoints,
                provider_region=provider_region)
            provider_id = result['results'][0]['id']
            self.changed = True
        except Exception as e:
            self.module.fail_json(
                msg="Failed to add provider. Error: {!r}".format(e))
        return provider_id

    def find_zone_by_name(self, zone_name):
        """ Searches the zone name in manageiq existing zones

        Returns:
            the zone id if it exists in manageiq, None otherwise
        """
        zones = self.client.collections.zones
        return next((z.id for z in zones if z.name == zone_name), None)

    def find_provider_by_name(self, provider_name):
        """ Searches the provider name in manageiq existing providers

        Returns:
            the provider id if it exists in manageiq, None otherwise
        """
        providers = self.client.collections.providers
        return next((p.id for p in providers if p.name == provider_name), None)

    def generate_auth_key_config(self, role, authtype, hostname, port, token,
                                 provider_verify_ssl, provider_ca_path):
        """ Returns an openshift provider endpoint dictionary.
        """
        config = {
            'endpoint': {
                'role': role,
                'hostname': hostname,
                'port': int(port),
                'verify_ssl': provider_verify_ssl
            },
            'authentication': {
                'authtype': authtype,
                'auth_key': token
            }
        }

        if provider_ca_path:
            with open(provider_ca_path, 'r') as provider_ca_file:
                provider_ca_content = provider_ca_file.read()
                config['endpoint'][
                    'certificate_authority'] = provider_ca_content
        else:
            config['endpoint']['certificate_authority'] = None

        # deduce security_protocol from provider_verify_ssl and provider_ca_path
        if provider_verify_ssl:
            if provider_ca_path:
                config['endpoint'][
                    'security_protocol'] = 'ssl-with-validation-custom-ca'
            else:
                config['endpoint']['security_protocol'] = 'ssl-with-validation'
        else:
            config['endpoint']['security_protocol'] = 'ssl-without-validation'

        return config

    def generate_amazon_config(self, role, authtype, userid, password):
        """ Returns an amazon provider endpoint dictionary.
        """
        return {
            'endpoint': {
                'role': role
            },
            'authentication': {
                'authtype': authtype,
                'userid': userid,
                'password': password
            }
        }

    def delete_provider(self, provider_name):
        """ Deletes the provider

        Returns:
            the delete task id if a task was generated, whether or not
            a change took place and a short message describing the operation
            executed.
        """
        provider_id = self.find_provider_by_name(provider_name)
        if provider_id:
            try:
                url = '{providers_url}/{id}'.format(
                    providers_url=self.providers_url, id=provider_id)
                result = self.client.post(url, action='delete')
                if result['success']:
                    self.changed = True
                    return dict(task_id=result['task_id'],
                                changed=self.changed,
                                msg=result['message'])
                else:
                    return dict(
                        task_id=None,
                        changed=self.changed,
                        api_error=result,
                        msg="Failed to delete {provider_name} provider".format(
                            provider_name=provider_name))
            except Exception as e:
                self.module.fail_json(
                    msg=
                    "Failed to delete {provider_name} provider. Error: {error!r}"
                    .format(provider_name=provider_name, error=e))
        else:
            return dict(task_id=None,
                        changed=self.changed,
                        msg="Provider {provider_name} doesn't exist".format(
                            provider_name=provider_name))

    def filter_unsupported_fields_from_config(self, configs,
                                              existing_endpoints, fields):
        """
        Only update fields that already exist in the endpoint with empty values.
        :param configs: New configuration. method mutates this param inplace
        :param existing_endpoints: current provider endpoints
        :param fields: a list of fields that we want to check if already exist in provider, and if not remove empty occurences from endpoints
        """
        for field in fields:
            if not any(field in e for e in existing_endpoints):
                for c in configs:
                    endpoint = c['endpoint']
                    if field in endpoint and endpoint[field] is None:
                        del endpoint[field]

    def add_or_update_provider(self,
                               provider_name,
                               provider_type,
                               endpoints,
                               zone,
                               provider_region,
                               validate_provider_auth=True,
                               initiate_refresh=True):
        """ Adds a provider to manageiq or update its attributes in case
        a provider with the same name already exists

        Returns:
            the added or updated provider id, whether or not a change took
            place and a short message describing the operation executed,
            including the authentication validation status
        """
        zone_id = self.find_zone_by_name(zone or 'default')
        # check if provider with the same name already exists
        provider_id = self.find_provider_by_name(provider_name)
        if provider_id:  # provider exists
            existing_config = self.get_provider_config(provider_id)

            # ManageIQ Euwe / CFME 5.7 API and older versions don't support certificate authority field in endpoint.
            # If it wasn't returned from existing provider configuration this means it is either unsupported or null,
            # in both cases we can remove null/empty certificate_authority from endpoints we want to update.
            self.filter_unsupported_fields_from_config(
                endpoints, existing_config['endpoints'],
                {'certificate_authority'})

            updates = self.required_updates(provider_id, endpoints, zone_id,
                                            provider_region, existing_config)

            if not updates:
                return dict(changed=self.changed,
                            msg="Provider %s already exists" % provider_name)

            old_validation_details = self.auths_validation_details(provider_id)
            operation = "update"
            self.update_provider(provider_id, provider_name, endpoints,
                                 zone_id, provider_region)
            roles_with_changes = set(updates["Added"]) | set(
                updates["Updated"])
        else:  # provider doesn't exists, adding it to manageiq

            # ManageIQ Euwe / CFME 5.7 API and older versions don't support certificate authority field in endpoint.
            # filter empty fields if none on creation - No existing endpoints for new provider
            self.filter_unsupported_fields_from_config(
                endpoints, [{}], {'certificate_authority'})
            updates = None
            old_validation_details = {}
            operation = "addition"
            provider_id = self.add_new_provider(provider_name, provider_type,
                                                endpoints, zone_id,
                                                provider_region)
            roles_with_changes = [e['endpoint']['role'] for e in endpoints]

        if validate_provider_auth:
            authtypes_to_verify = []
            for e in endpoints:
                if e['endpoint']['role'] in roles_with_changes:
                    # todo: Temporary hack. Remove this line when manageiq supports prometheus validation
                    if e['authentication']['authtype'] != 'prometheus':
                        authtypes_to_verify.append(
                            e['authentication']['authtype'])
            result, details = self.verify_authenticaion_validation(
                provider_id, old_validation_details, authtypes_to_verify)
        else:
            result = "Skipped Validation"
            details = result

        if result == "Invalid":
            self.module.fail_json(
                msg=
                "Failed to Validate provider authentication after {operation}. details: {details}"
                .format(operation=operation, details=details))
        elif result == "Valid" or result == "Skipped Validation":
            if initiate_refresh:
                self.refresh_provider(provider_id)
                message = "Successful {operation} of {provider} provider. Authentication: {validation}. Refreshing provider inventory".format(
                    operation=operation,
                    provider=provider_name,
                    validation=details)
            else:
                message = "Successful {operation} of {provider} provider. Authentication: {validation}.".format(
                    operation=operation,
                    provider=provider_name,
                    validation=details)
        elif result == "Timed out":
            message = "Provider {provider} validation after {operation} timed out. Authentication: {validation}".format(
                operation=operation,
                provider=provider_name,
                validation=details)
        return dict(provider_id=provider_id,
                    changed=self.changed,
                    msg=message,
                    updates=updates)
class ManageIQTagAssignment(object):
    """ ManageIQ object to execute tag assignments in manageiq

    url            - manageiq environment url
    user           - the username in manageiq
    password       - the user password in manageiq
    miq_verify_ssl - whether SSL certificates should be verified for HTTPS requests
    ca_bundle_path - the path to a CA_BUNDLE file or directory with certificates
    """

    manageiq_entities = {
        'provider': 'providers',
        'host': 'hosts',
        'vm': 'vms',
        'category': 'categories',
        'cluster': 'clusters',
        'data store': 'data_stores',
        'group': 'groups',
        'resource pool': 'resource_pools',
        'service': 'services',
        'service template': 'service_templates',
        'template': 'templates',
        'tenant': 'tenants',
        'user': '******',
        'blueprint': 'blueprints'
    }
    actions = {'present': 'assign', 'absent': 'unassign'}

    def __init__(self, module, url, user, password, miq_verify_ssl,
                 ca_bundle_path):
        self.module = module
        self.api_url = url + '/api'
        self.user = user
        self.password = password
        self.client = MiqApi(self.api_url, (self.user, self.password),
                             verify_ssl=miq_verify_ssl,
                             ca_bundle_path=ca_bundle_path)
        self.changed = False

    def find_entity_by_name(self, entity_type, entity_name):
        """ Searches the entity name in ManageIQ.

        Returns:
            the entity id if it exists in manageiq, None otherwise.
        """
        entities_list = getattr(self.client.collections, entity_type)
        return next((e.id for e in entities_list if e.name == entity_name),
                    None)

    def query_resource_tags(self, resource_type, resource_id):
        """ Returns a set of the full tag names assigned to the resource
        """
        try:
            url = '{api_url}/{resource_type}/{resource_id}/tags?expand=resources'.format(
                api_url=self.api_url,
                resource_type=resource_type,
                resource_id=resource_id)
            response = self.client.get(url)
        except Exception as e:
            self.module.fail_json(
                msg="Failed to query {resource_type} tags: {error}".format(
                    resource_type=resource_type, error=e))
        tags = response.get('resources', [])
        tags_set = set([tag['name'] for tag in tags])
        return tags_set

    def execute_action(self, resource_type, resource_id, tags, action):
        """Executes the action for the resource tag
        """
        url = '{api_url}/{resource_type}/{resource_id}/tags'.format(
            api_url=self.api_url,
            resource_type=resource_type,
            resource_id=resource_id)
        try:
            response = self.client.post(url, action=action, resources=tags)
        except Exception as e:
            self.module.fail_json(msg="Failed to {action} tag: {error}".format(
                action=action, error=e))
        for result in response['results']:
            if result['success']:
                self.changed = True
            else:
                self.module.fail_json(msg="Failed to {action}: {fail_message}".
                                      format(action=action,
                                             entity=entity,
                                             fail_message=result['message']))

    def full_tag_name(self, tag):
        """ Returns the full tag name in manageiq
        """
        full_tag_name = '/managed/{category_name}/{tag_name}'.format(
            category_name=tag['category'], tag_name=tag['name'])
        return full_tag_name

    def assign_or_unassign_tag(self, tags, resource, resource_name, state):
        """ Assign or unassign the tag on a manageiq resource.

        Returns:
            Whether or not a change took place and a message describing the
            operation executed.
        """
        resource_type = self.manageiq_entities[resource]
        resource_id = self.find_entity_by_name(resource_type, resource_name)
        if not resource_id:  # resource doesn't exist
            self.module.fail_json(
                msg=
                "Failed to {action} tag: {resource_name} {resource} does not exist in manageiq"
                .format(action=ManageIQTagAssignment.actions[state],
                        resource_name=resource_name,
                        resource=resource))

        tags_to_execute = []
        assigned_tags = self.query_resource_tags(resource_type, resource_id)
        for tag in tags:
            assigned = self.full_tag_name(tag) in assigned_tags

            if assigned and state == 'absent':
                tags_to_execute.append(tag)
            elif (not assigned) and state == 'present':
                tags_to_execute.append(tag)

        if not tags_to_execute:
            return dict(changed=self.changed,
                        msg="Tags already {action}ed, nothing to do".format(
                            action=ManageIQTagAssignment.actions[state]))
        else:
            self.execute_action(resource_type, resource_id, tags_to_execute,
                                ManageIQTagAssignment.actions[state])
            return dict(changed=self.changed,
                        msg="Successfully {action}ed tags".format(
                            action=ManageIQTagAssignment.actions[state]))
예제 #10
0
class ManageIQUser(object):
    """ ManageIQ object to execute user management operations in manageiq

    url            - manageiq environment url
    user           - the username in manageiq
    password       - the user password in manageiq
    miq_verify_ssl - whether SSL certificates should be verified for HTTPS requests
    ca_bundle_path - the path to a CA_BUNDLE file or directory with certificates
    """
    def __init__(self, module, url, user, password, miq_verify_ssl,
                 ca_bundle_path):
        self.module = module
        self.api_url = url + '/api'
        self.user = user
        self.password = password
        self.client = MiqApi(self.api_url, (self.user, self.password),
                             verify_ssl=miq_verify_ssl,
                             ca_bundle_path=ca_bundle_path)
        self.changed = False

    def find_group_by_name(self, group_name):
        """ Searches the group name in ManageIQ.

        Returns:
            the group id if it exists in manageiq, None otherwise.
        """
        groups = self.client.collections.groups
        return next(
            (group.id for group in groups if group.description == group_name),
            None)

    def find_user_by_userid(self, userid):
        """ Searches the userid in ManageIQ.

        Returns:
            the user's id if it exists in manageiq, None otherwise.
        """
        users = self.client.collections.users
        return next((user.id for user in users if user.userid == userid), None)

    def delete_user(self, userid):
        """Deletes the user from manageiq.

        Returns:
            a short message describing the operation executed.
        """
        user_id = self.find_user_by_userid(userid)
        if not user_id:  # user doesn't exist
            return dict(changed=self.changed,
                        msg="User {userid} does not exist in manageiq".format(
                            userid=userid))
        try:
            url = '{api_url}/users/{user_id}'.format(api_url=self.api_url,
                                                     user_id=user_id)
            result = self.client.post(url, action='delete')
            self.changed = True
            return dict(changed=self.changed, msg=result['message'])
        except Exception as e:
            self.module.fail_json(
                msg="Failed to delete user {userid}: {error}".format(
                    userid=userid, error=e))

    def user_update_required(self, user_id, userid, username, group_id, email):
        """ Returns true if the username, group id or email passed for the user
            differ from the user's existing ones, False otherwise.
        """
        try:
            url = "{api_url}/users/{user_id}".format(api_url=self.api_url,
                                                     user_id=user_id)
            result = self.client.get(url)
            return result['name'] != username or result[
                'current_group_id'] != group_id or result.get('email') != email
        except Exception as e:
            self.module.fail_json(
                msg="Failed to get user {userid} details. Error: {error}".
                format(userid=userid, error=e))

    def update_user_if_required(self, user_id, userid, username, group_id,
                                password, email):
        """Updates the user in manageiq.

        Returns:
            the created user id, name, created_on timestamp,
            updated_on timestamp, userid and current_group_id
        """
        if not self.user_update_required(user_id, userid, username, group_id,
                                         email):
            return dict(
                changed=self.changed,
                msg="User {userid} already exist, no need for updates".format(
                    userid=userid))
        try:
            url = '{api_url}/users/{user_id}'.format(api_url=self.api_url,
                                                     user_id=user_id)
            resource = {
                'userid': userid,
                'name': username,
                'password': password,
                'group': {
                    'id': group_id
                },
                'email': email
            }
            result = self.client.post(url, action='edit', resource=resource)
            self.changed = True
            return dict(
                changed=self.changed,
                msg="Successfully updated the user {userid}: {user_details}".
                format(userid=userid, user_details=result))
        except Exception as e:
            self.module.fail_json(
                msg="Failed to update user {userid}: {error}".format(
                    userid=userid, error=e))

    def create_user(self, userid, username, group_id, password, email):
        """Creates the user in manageiq.

        Returns:
            the created user id, name, created_on timestamp,
            updated_on timestamp, userid and current_group_id
        """
        try:
            url = '{api_url}/users'.format(api_url=self.api_url)
            resource = {
                'userid': userid,
                'name': username,
                'password': password,
                'group': {
                    'id': group_id
                },
                'email': email
            }
            result = self.client.post(url, action='create', resource=resource)
            self.changed = True
            return dict(
                changed=self.changed,
                msg="Successfully created the user {userid}: {user_details}".
                format(userid=userid, user_details=result['results']))
        except Exception as e:
            self.module.fail_json(
                msg="Failed to create user {userid}: {error}".format(
                    userid=userid, error=e))

    def create_or_update_user(self, userid, username, password, group, email):
        """ Create or update a user in manageiq.

        Returns:
            Whether or not a change took place and a message describing the
            operation executed.
        """
        group_id = self.find_group_by_name(group)
        if not group_id:  # group doesn't exist
            self.module.fail_json(
                msg=
                "Failed to create user {userid}: group {group_name} does not exist in manageiq"
                .format(userid=userid, group_name=group))

        user_id = self.find_user_by_userid(userid)
        if user_id:  # user already exist
            return self.update_user_if_required(user_id, userid, username,
                                                group_id, password, email)
        else:
            return self.create_user(userid, username, group_id, password,
                                    email)
예제 #11
0
class Evaluation:
    def __init__(self,
                 manageiq_aws_access_key,
                 manageiq_aws_secret_key,
                 manageiq_home_directory,
                 manageiq_endpoint='https://127.0.0.1:8443/',
                 manageiq_username='******',
                 manageiq_password='******',
                 manageiq_region='us-east-2',
                 manageiq_provider_name='API_call',
                 manageiq_ports=None,
                 manageiq_docker_image='manageiq/manageiq:gaprindashvili-3'):
        if manageiq_ports is None:
            manageiq_ports = {'443/tcp': 8443}
        self.manageiq_home_directory = manageiq_home_directory
        self.manageiq_aws_access_key = manageiq_aws_access_key
        self.manageiq_aws_secret_key = manageiq_aws_secret_key
        self.manageiq_endpoint = manageiq_endpoint
        self.manageiq_username = manageiq_username
        self.manageiq_password = manageiq_password
        self.manageiq_region = manageiq_region
        self.manageiq_provider_name = manageiq_provider_name
        self.manageiq_ports = manageiq_ports
        self.manageiq_docker_image = manageiq_docker_image
        try:
            self.client = MiqApi(self.manageiq_endpoint + 'api',
                                 dict(user=self.manageiq_username,
                                      password=self.manageiq_password),
                                 verify_ssl=False)
        except ConnectionError:
            self.client = None
        self.provider = None

    @Decorators.tagging('*:System:Download')
    @Decorators.timing(output=True)
    def download_sources(self):
        system.docker_pull(self.manageiq_docker_image)
        size, volume = system.docker_get_images_sizes('manageiq')
        return {'size': size, 'volume': volume}

    @Decorators.tagging('*:System:Start')
    @Decorators.timing()
    def start_docker_container(self):
        system.docker_run(image=self.manageiq_docker_image,
                          port=self.manageiq_ports)
        system.wait_service(self.manageiq_endpoint, ssl_enable=False)
        self.__init__(self.manageiq_aws_access_key,
                      self.manageiq_aws_secret_key,
                      self.manageiq_home_directory)

    @Decorators.tagging('*:System:Stop')
    @Decorators.timing()
    def stop_docker_container(self):
        system.docker_container_stop_and_delete('manageiq')

    @Decorators.tagging('*:System:Remove')
    @Decorators.timing()
    def delete_sources(self):
        system.docker_delete_images()
        system.delete_dir(self.manageiq_home_directory)

    @Decorators.tagging('AWS:Provider:Create')
    @Decorators.docker_consumption(list_of_containers)
    @Decorators.timing()
    def create_aws_provider(self,
                            aws_access_key=None,
                            aws_secret_key=None,
                            provider_name=None,
                            provider_region=None):
        if not aws_access_key:
            aws_access_key = self.manageiq_aws_access_key
        if not aws_secret_key:
            aws_secret_key = self.manageiq_aws_secret_key
        if not provider_name:
            provider_name = self.manageiq_provider_name
        if not provider_region:
            provider_region = self.manageiq_region
        response = self.client.post(
            self.manageiq_endpoint + 'api/providers',
            type='ManageIQ::Providers::Amazon::CloudManager',
            name=provider_name,
            provider_region=provider_region,
            credentials={
                'userid': aws_access_key,
                'password': aws_secret_key
            })
        self.provider = response['results'][0]['id']

    @Decorators.tagging('*:Provider:Sync')
    @Decorators.docker_consumption(list_of_containers)
    @Decorators.timing()
    def sync_aws_provider(self):
        while True:
            if self.client.collections.flavors.all:
                return

    @Decorators.tagging('*:Provider:List')
    @Decorators.docker_consumption(list_of_containers)
    @Decorators.timing()
    def list_of_providers(self):
        while True:
            if self.client.collections.providers.all:
                return

    @Decorators.tagging('*:Provider:Delete')
    @Decorators.docker_consumption(list_of_containers)
    @Decorators.timing()
    def delete_provider(self, provider=None):
        if not provider:
            provider = self.provider
        self.client.delete(self.manageiq_endpoint + 'api/providers/' +
                           str(provider))
        while True:
            if not self.client.collections.flavors.all:
                return
예제 #12
0
class ManageIQCustomAttributes(object):
    """ ManageIQ object to execute custom attibutes related operations
    in manageiq

    url            - manageiq environment url
    user           - the username in manageiq
    password       - the user password in manageiq
    miq_verify_ssl - whether SSL certificates should be verified for HTTPS requests
    ca_bundle_path - the path to a CA_BUNDLE file or directory with certificates
    """

    supported_entities = {'vm': 'vms', 'provider': 'providers'}

    def __init__(self, module, url, user, password, miq_verify_ssl,
                 ca_bundle_path):
        self.module = module
        self.api_url = url + '/api'
        self.user = user
        self.password = password
        self.client = MiqApi(self.api_url, (self.user, self.password),
                             verify_ssl=miq_verify_ssl,
                             ca_bundle_path=ca_bundle_path)
        self.changed = False

    def find_entity_by_name(self, entity_type, entity_name):
        """ Searches the entity name in ManageIQ.

            Returns:
                the entity id if it exists in manageiq, None otherwise.
        """
        entities_list = getattr(
            self.client.collections,
            ManageIQCustomAttributes.supported_entities[entity_type])
        return next((e.id for e in entities_list if e.name == entity_name),
                    None)

    def get_entity_custom_attributes(self, entity_type, entity_id):
        """ Returns the entity's custom attributes
        """
        try:
            url = '{api_url}/{entity_type}/{id}?expand=custom_attributes'.format(
                api_url=self.api_url,
                entity_type=ManageIQCustomAttributes.
                supported_entities[entity_type],
                id=entity_id)
            result = self.client.get(url)
            return result.get('custom_attributes', [])
        except Exception as e:
            self.module.fail_json(
                msg=
                "Failed to get {entity_type} custom attributes. Error: {error}"
                .format(entity_type=entity_type, error=e))

    def add_custom_attributes(self, entity_type, entity_id, custom_attributes):
        """ Returns the added custom attributes """
        try:
            url = '{api_url}/{entity_type}/{id}/custom_attributes'.format(
                api_url=self.api_url,
                entity_type=ManageIQCustomAttributes.
                supported_entities[entity_type],
                id=entity_id)
            result = self.client.post(url,
                                      action='add',
                                      resources=custom_attributes)
            self.changed = True
            return result['results']
        except Exception as e:
            self.module.fail_json(
                msg="Failed to add the custom attributes. Error: {}".format(e))

    def update_custom_attribute(self, entity_type, entity_id, ca, ca_href):
        """ Returns the updated custom attributes """
        try:
            url = '{api_url}/{entity_type}/{id}/custom_attributes'.format(
                api_url=self.api_url,
                entity_type=ManageIQCustomAttributes.
                supported_entities[entity_type],
                id=entity_id)
            ca_object = {
                'name': ca['name'],
                'href': ca_href,
                'value': ca['value']
            }
            result = self.client.post(url,
                                      action='edit',
                                      resources=[ca_object])
            self.changed = True
            return result['results']
        except Exception as e:
            self.module.fail_json(
                msg=
                "Failed to update the custom attribute {ca_name}. Error: {error}"
                .format(ca_name=ca['name'], error=e))

    @staticmethod
    def compare_custom_attributes(ca1, ca2):
        return (ca1['name'], ca1['section']) == (ca2['name'], ca2['section'])

    def add_or_update_custom_attributes(self, entity_type, entity_name,
                                        custom_attributes):
        """ Adds custom attributes to an entity in manageiq or updates the
        attributes in case already exists

        Returns:
            the added or updated custom attributes, whether or not a change
            took place and a short message describing the operation executed
        """
        added, updated = [], []
        message = ""
        # check if entity with the type and name passed exists in manageiq
        entity_id = self.find_entity_by_name(entity_type, entity_name)
        if not entity_id:  # entity doesn't exist
            self.module.fail_json(
                msg=
                "Failed to set the custom attributes. {entity_type} {entity_name} does not exist"
                .format(entity_type=entity_type, entity_name=entity_name))

        entity_cas = self.get_entity_custom_attributes(entity_type, entity_id)
        for new_ca in custom_attributes:
            existing_ca = next((ca for ca in entity_cas
                                if self.compare_custom_attributes(ca, new_ca)),
                               None)
            if existing_ca:
                if new_ca['value'] != existing_ca['value']:
                    updated.extend(
                        self.update_custom_attribute(entity_type, entity_id,
                                                     new_ca,
                                                     existing_ca['href']))
            else:
                added.extend(
                    self.add_custom_attributes(entity_type, entity_id,
                                               [new_ca]))

        if added or updated:
            message = "Successfully set the custom attributes to {entity_name} {entity_type}"
        else:
            message = "The custom attributes already exist on {entity_name} {entity_type}"

        return dict(changed=self.changed,
                    msg=message.format(entity_name=entity_name,
                                       entity_type=entity_type),
                    updates={
                        "Added": added,
                        "Updated": updated
                    })

    def delete_custom_attribute(self, ca, ca_href, entity_type, entity_id):
        """ Returns the deleted custom attribute
        """
        try:
            url = '{api_url}/{entity_type}/{id}/custom_attributes'.format(
                api_url=self.api_url,
                entity_type=ManageIQCustomAttributes.
                supported_entities[entity_type],
                id=entity_id)
            ca_object = {'name': ca['name'], 'href': ca_href}
            result = self.client.post(url,
                                      action='delete',
                                      resources=[ca_object])
            self.changed = True
            return result['results']
        except Exception as e:
            self.module.fail_json(
                msg="Failed to delete the custom attribute {ca}. Error: {error}"
                .format(ca=ca, error=e))

    def delete_custom_attributes(self, entity_type, entity_name,
                                 custom_attributes):
        """ Deletes the custom attributes from the entity, if exist

        Returns:
            whether or not a change took and a short message including the
            deleted custom attributes
        """
        deleted = []
        entity_id = self.find_entity_by_name(entity_type, entity_name)
        if not entity_id:  # entity doesn't exist
            self.module.fail_json(
                msg=
                "Failed to delete the custom attributes. {entity_type} {entity_name} does not exist"
                .format(entity_type=entity_type, entity_name=entity_name))

        entity_cas = self.get_entity_custom_attributes(entity_type, entity_id)
        for new_ca in custom_attributes:
            ca_href = next((ca['href'] for ca in entity_cas
                            if self.compare_custom_attributes(ca, new_ca)),
                           None)
            if ca_href:
                deleted.extend(
                    self.delete_custom_attribute(new_ca, ca_href, entity_type,
                                                 entity_id))

        return dict(
            msg=
            "Successfully deleted the following custom attributes from {entity_name} {entity_type}: {deleted}"
            .format(entity_name=entity_name,
                    entity_type=entity_type,
                    deleted=deleted),
            changed=self.changed)
from time import sleep

# the next line assumes you're running this on the CFME appliance itself.
# If not, you need to add the parameter ssl_verify=False to the client
# MiqApi() initialization because CFME appliances use self-signed certs
# by default.
url = os.environ.get('MIQURL') or 'http://localhost:3000/api'
username = os.environ.get('MIQUSERNAME') or 'admin'
password = os.environ.get('MIQPASSWORD') or 'smartvm'
token = os.environ.get('MIQTOKEN')

# CF API Authentication (client initialization)
client = None
if token:
    print("\nAuthenticating with the API token")
    client = MiqApi(url, dict(token=token), verify_ssl=False)
else:
    print("\nAuthenticating with the user credentials")
    client = MiqApi(url, dict(user=username, password=password), verify_ssl=False)

payload = { 
    'action_method': 'POST',
    'uri_parts': {
        'namespace': 'AutomationManagement/AnsibleTower/Operations/StateMachines',
        'class': 'Job',
        # 'instance': 'default',
        'instance': 'autobranch', # This instance is a custom Automate instance (i.e. won't work with out-of-the-box CloudForms)
        'message': 'create'
    },
    'parameters': {
        'job_template_name': 'i-was-here',
예제 #14
0
class ManageIQAlert(object):
    """ ManageIQ object to execute alert definitions management operations in manageiq

    url            - manageiq environment url
    user           - the username in manageiq
    password       - the user password in manageiq
    miq_verify_ssl - whether SSL certificates should be verified for HTTPS requests
    ca_bundle_path - the path to a CA_BUNDLE file or directory with certificates
    """

    supported_entities = {
        'container_node': 'ContainerNode',
        'vm': 'Vm',
        'miq_server': 'MiqServer',
        'host': 'Host',
        'storage': 'Storage',
        'cluster': 'EmsCluster',
        'ems': 'ExtManagementSystem',
        'miq_server': 'MiqServer',
        'middleware_server': 'MiddlewareServer'
    }

    def __init__(self, module, url, user, password, miq_verify_ssl,
                 ca_bundle_path):
        self.module = module
        self.api_url = url + '/api'
        self.user = user
        self.password = password
        self.client = MiqApi(self.api_url, (self.user, self.password),
                             verify_ssl=miq_verify_ssl,
                             ca_bundle_path=ca_bundle_path)
        self.changed = False

    def find_alert_by_description(self, description):
        """ Searches the alert description in ManageIQ.

        Returns:
            the alert id if it exists in manageiq, None otherwise.
        """
        try:
            response = self.client.get(
                '{api_url}/alert_definitions?expand=resources'.format(
                    api_url=self.api_url))
        except Exception as e:
            self.module.fail_json(msg="Failed to query alerts: {error}".format(
                error=e))
        alerts = response.get('resources', [])
        return next(
            (alert['id']
             for alert in alerts if alert['description'] == description), None)

    def delete_alert(self, description):
        """Deletes the alert from manageiq.

        Returns:
            a short message describing the operation executed.
        """
        alert_id = self.find_alert_by_description(description)
        if not alert_id:  # alert doesn't exist
            return dict(
                changed=self.changed,
                msg="Alert {description} does not exist in manageiq".format(
                    description=description))
        try:
            url = '{api_url}/alert_definitions/{alert_id}'.format(
                api_url=self.api_url, alert_id=alert_id)
            result = self.client.post(url, action='delete')
        except Exception as e:
            self.module.fail_json(
                msg="Failed to delete alert {description}: {error}".format(
                    description=description, error=e))
        self.changed = True
        return dict(changed=self.changed, msg=result['message'])

    def alert_update_required(self, alert_id, description, expression,
                              expression_type, miq_entity, options, enabled):
        """ Returns true if the expression, miq_entity, options, or enabled passed for
            the alert differ from the alert's existing ones, False otherwise.
        """
        url = "{api_url}/alert_definitions/{alert_id}".format(
            api_url=self.api_url, alert_id=alert_id)
        try:
            result = self.client.get(url)
        except Exception as e:
            self.module.fail_json(
                msg="Failed to get alert {description} details. Error: {error}"
                .format(description=description, error=e))

        # remove None values from expression and options dicts, if needed
        # TODO (dkorn): use the expression_type from the response, once supported
        if expression_type == 'miq_expression':
            current_expression = {
                k: v
                for k, v in result['expression']['exp'].items()
                if v is not None
            }
        else:
            current_expression = result['expression']
        current_options = {
            k: v
            for k, v in result['options'].items() if v is not None
        }

        attributes_tuples = [(current_expression, expression),
                             (result['db'], miq_entity),
                             (current_options, options),
                             (result['enabled'], enabled)]
        for (current, desired) in attributes_tuples:
            if desired is not None and current != desired:
                return True
        return False

    def update_alert_if_required(self, alert_id, description, expression,
                                 expression_type, miq_entity, options,
                                 enabled):
        """Updates the alert in manageiq.

        Returns:
            Whether or not a change took place and a message describing the
            operation executed.
        """
        if not self.alert_update_required(alert_id, description, expression,
                                          expression_type, miq_entity, options,
                                          enabled):
            return dict(
                changed=self.changed,
                msg="Alert {description} already exist, no need for updates".
                format(description=description))

        url = '{api_url}/alert_definitions/{alert_id}'.format(
            api_url=self.api_url, alert_id=alert_id)
        resource = {
            'description': description,
            'expression': expression,
            'expression_type': expression_type,
            'db': miq_entity,
            'options': options,
            'enabled': enabled
        }
        try:
            result = self.client.post(url, action='edit', resource=resource)
        except Exception as e:
            self.module.fail_json(
                msg="Failed to update alert {description}: {error}".format(
                    description=description, error=e))
        self.changed = True
        return dict(
            changed=self.changed,
            msg="Successfully updated alert {description}: {alert_details}".
            format(description=description, alert_details=result))

    def create_alert(self, description, expression, expression_type,
                     miq_entity, options, enabled):
        """Creates the alert in manageiq.

        Returns:
            Whether or not a change took place and a message describing the
            operation executed.
        """
        url = '{api_url}/alert_definitions/'.format(api_url=self.api_url)
        resource = {
            'description': description,
            'expression': expression,
            'expression_type': expression_type,
            'db': miq_entity,
            'options': options,
            'enabled': enabled
        }
        try:
            result = self.client.post(url, action='create', resource=resource)
            self.changed = True
            return dict(
                changed=self.changed,
                msg="Successfully created alert {description}: {alert_details}"
                .format(description=description,
                        alert_details=result['results']))
        except Exception as e:
            self.module.fail_json(
                msg="Failed to create alert {description}: {error}".format(
                    description=description, error=e))

    def create_or_update_alert(self, description, expression, expression_type,
                               entity, options, enabled):
        """ Create or update an alert in manageiq.

        Returns:
            Whether or not a change took place and a message describing the
            operation executed.
        """
        miq_entity = ManageIQAlert.supported_entities[entity]
        alert_id = self.find_alert_by_description(description)
        if alert_id:  # alert already exist
            return self.update_alert_if_required(alert_id, description,
                                                 expression, expression_type,
                                                 miq_entity, options, enabled)
        else:
            return self.create_alert(description, expression, expression_type,
                                     miq_entity, options, enabled)
예제 #15
0
def api():
    with HTTMock(api_mock):
        api = ManageIQClient('https://example/api', ('admin', 'admin'),
                             verify_ssl=False)
    return api
예제 #16
0
class ManageIQ(object):
    """ ManageIQ object to execute policy assignments in manageiq

    url      - manageiq environment url
    user     - the username in manageiq
    password - the user password in manageiq
    verify_ssl     - whether SSL certificates should be verified for HTTPS requests
    ca_bundle_path - the path to a CA_BUNDLE file or directory with certificates
    """

    manageiq_entities = {
        'policy': 'policies', 'policy profile': 'policy_profiles',
        'provider': 'providers', 'host': 'hosts', 'vm': 'vms',
        'container node': 'container_nodes', 'pod': 'container_groups',
        'replicator': 'container_replicators',
        'container image': 'container_images'
    }
    policy_actions = {
        'present': 'assign', 'absent': 'unassign'
    }

    def __init__(self, module, url, user, password, verify_ssl, ca_bundle_path):
        self.module        = module
        self.api_url       = url + '/api'
        self.user          = user
        self.password      = password
        self.client        = MiqApi(self.api_url, (self.user, self.password), verify_ssl=verify_ssl, ca_bundle_path=ca_bundle_path)
        self.changed       = False

    def find_entity_by_name(self, entity_type, entity_name):
        """ Searches the entity name in ManageIQ.

        Returns:
            the entity id if it exists in manageiq, None otherwise.
        """
        entities_list = getattr(self.client.collections, entity_type)
        return next((e.id for e in entities_list if e.name == entity_name), None)

    def query_resource_policies_or_profiles(self, entity_type, resource_type, resource_id):
        """ Returns the policies or policy profiles assigned to the resource.
        """
        try:
            url = '{api_url}/{resource_type}/{resource_id}/{entity_type}?expand=resources'.format(api_url=self.api_url, resource_type=resource_type, resource_id=resource_id, entity_type=entity_type)
            result = self.client.get(url)
            return result.get('resources', [])
        except Exception as e:
            self.module.fail_json(msg="Failed to query resource {entity_type}: {error}".format(entity_type=entity_type, error=e))

    def entity_assigned(self, entity_type, entity_id, resource_type, resource_id):
        """Return True if the action is needed on the resource, False otherwise.
        """
        assigned_entities = self.query_resource_policies_or_profiles(entity_type, resource_type, resource_id)
        return any(ae['id'] == entity_id for ae in assigned_entities)

    def execute_action(self, entity_type, entity_id, resource_type, resource_id, action):
        """Executes the action for the relevant entity on the resource.

        Returns:
            Whether or not a change took place and a message describing the
            operation executed.
        """
        try:
            href = '{api_url}/{entity_type}/{entity_id}'.format(api_url=self.api_url, entity_type=entity_type, entity_id=entity_id)
            url = '{api_url}/{resource_type}/{resource_id}/{entity_type}'.format(api_url=self.api_url, resource_type=resource_type, resource_id=resource_id, entity_type=entity_type)
            result = self.client.post(url, action=action, resource={'href': href})
            if result['results'][0]['success']:
                self.changed = True
                return dict(
                    changed=self.changed,
                    msg=result['results'][0]['message']
                )
            else:
                self.module.fail_json(msg="Failed to {action}: {fail_message}".format(action=action, entity=entity, fail_message=result['results'][0]['message']))
        except Exception as e:
            self.module.fail_json(msg="Failed to {action}: {error}".format(action=action, entity=entity, error=e))

    def assign_or_unassign_entity(self, entity, entity_name, resource, resource_name, state):
        """ Assign or unassign the entity on the manageiq resource.

        Returns:
            Whether or not a change took place and a message describing the
            operation executed.
        """
        entity_type = self.manageiq_entities[entity]
        resource_type = self.manageiq_entities[resource]
        entity_id = self.find_entity_by_name(entity_type, entity_name)
        if not entity_id:  # entity doesn't exist
            self.module.fail_json(
                msg="Failed to {action} {entity}: {entity_name} does not exist in manageiq".format(action=ManageIQ.policy_actions[state], entity=entity, entity_name=entity_name))

        resource_id = self.find_entity_by_name(resource_type, resource_name)
        if not resource_id:  # resource doesn't exist
            self.module.fail_json(
                msg="Failed to {action} {entity}: {resource_name} {resource} does not exist in manageiq".format(action=ManageIQ.policy_actions[state], entity=entity, resource_name=resource_name, resource=resource))

        assigned = self.entity_assigned(entity_type, entity_id, resource_type, resource_id)
        if assigned and state == 'absent':
            return self.execute_action(entity_type, entity_id, resource_type, resource_id, 'unassign')
        if (not assigned) and state == 'present':
            return self.execute_action(entity_type, entity_id, resource_type, resource_id, 'assign')

        #  Default case is that there's nothing to change
        return dict(
            changed=self.changed,
            msg="{entity_name} {entity} already {action}ed".format(entity_name=entity_name, entity=entity, action=ManageIQ.policy_actions[state]))