示例#1
0
class ResourceClient(object):
    """
    This class implements common functions for HpOneView API rest
    """
    def __init__(self, con, uri):
        self._connection = con
        self._uri = uri
        self._task_monitor = TaskMonitor(con)

    def get_all(self,
                start=0,
                count=-1,
                filter='',
                query='',
                sort='',
                view='',
                fields='',
                uri=None):
        """
        Gets all items according with the given arguments.

        More than one request can be send to get the items, regardless the query parameter 'count', since the actual
        number of items in the response may differ from the requested count. Some types of resource has a limited
        number of items returned on each call. For those resources, additional calls are made to the API to retrieve
        any other items matching the given filter. The actual number of items can also diverge from the requested call
        if the requested number of items would take too long.

        The use of optional parameters for OneView 2.0 is described at:
        http://h17007.www1.hpe.com/docs/enterprise/servers/oneview2.0/cic-api/en/api-docs/current/index.html

        Note:
            Single quote - "'" - inside a query parameter is not supported by OneView API.

        Args:
            start:
                The first item to return, using 0-based indexing.
                If not specified, the default is 0 - start with the first available item.
            count:
                The number of resources to return. A count of -1 requests all the items (default).
            filter:
                A general filter/query string to narrow the list of items returned. The default is no filter - all
                resources are returned.
            query:
                A single query parameter can do what would take multiple parameters or multiple GET requests using
                filter. Use query for more complex queries. NOTE: This parameter is experimental for OneView 2.0.
            sort:
                The sort order of the returned data set. By default, the sort order is based on create time, with the
                oldest entry first.
            view:
                Returns a specific subset of the attributes of the resource or collection by specifying the name of a
                predefined view. The default view is expand (show all attributes of the resource, and all elements of
                collections or resources).
            fields:
                Nome of the fields.
            uri:
                A specific URI (optional)

        Returns:
            list: A list of items matching the specified filter.
        """
        if filter:
            filter = "&filter=" + quote(filter)

        if query:
            query = "&query=" + quote(query)

        if sort:
            sort = "&sort=" + quote(sort)

        if view:
            view = "&view=" + quote(view)

        if fields:
            fields = "&fields=" + quote(fields)

        path = uri if uri else self._uri
        self.__validate_resource_uri(path)

        symbol = '?' if '?' not in path else '&'

        uri = "{0}{1}start={2}&count={3}{4}{5}{6}{7}{8}".format(
            path, symbol, start, count, filter, query, sort, view, fields)

        logger.debug('Getting all resources with uri: {0}'.format(uri))

        result = self.__do_requests_to_getall(uri, count)

        return result

    def delete_all(self, filter, force=False, timeout=-1):
        """
        Deletes all resources from the appliance that match the provided filter.

        Args:
            filter:
                A general filter/query string to narrow the list of items deleted.
            force:
                If set to true the operation completes despite any problems with network connectivity or errors
                on the resource itself. The default is false.
            timeout:
                Timeout in seconds. Wait task completion by default. The timeout does not abort the operation
                in OneView, just stops waiting for its completion.

        Returns:
            bool: Indicating if the resources were successfully deleted.
        """
        uri = "{}?filter={}&force={}".format(self._uri, quote(filter), force)
        logger.debug("Delete all resources (uri = %s)" % uri)

        task, body = self._connection.delete(uri)

        if not task:
            # 204 NO CONTENT
            # Successful return from a synchronous delete operation.
            return True

        return self._task_monitor.wait_for_task(task, timeout=timeout)

    def delete(self, resource, force=False, timeout=-1, custom_headers=None):

        if not resource:
            logger.exception(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)
            raise ValueError(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)

        if isinstance(resource, dict):
            if 'uri' in resource and resource['uri']:
                uri = resource['uri']
            else:
                logger.exception(RESOURCE_CLIENT_UNKNOWN_OBJECT_TYPE)
                raise HPOneViewUnknownType(RESOURCE_CLIENT_UNKNOWN_OBJECT_TYPE)
        else:
            uri = self._uri + "/" + resource

        if force:
            uri += '?force=True'

        logger.debug("Delete resource (uri = %s, resource = %s)" %
                     (self._uri, str(resource)))

        task, body = self._connection.delete(uri,
                                             custom_headers=custom_headers)

        if not task:
            # 204 NO CONTENT
            # Successful return from a synchronous delete operation.
            return True

        task = self._task_monitor.wait_for_task(task, timeout=timeout)

        return task

    def get_schema(self):
        logger.debug('Get schema (uri = %s, resource = %s)' %
                     (self._uri, self._uri))
        return self._connection.get(self._uri + '/schema')

    def get(self, id_or_uri):
        """
        Args:
            id_or_uri: Could be either the resource id or the resource uri.

        Returns:
             The requested resource.
        """
        uri = self.build_uri(id_or_uri)
        logger.debug('Get resource (uri = %s, ID = %s)' %
                     (uri, str(id_or_uri)))
        return self._connection.get(uri)

    def get_collection(self, id_or_uri, filter=''):
        """
        Retrieves a collection of resources.

        Use this function when the 'start' and 'count' parameters are not allowed in the GET call.
        Otherwise, use get_all instead.

        Optional filtering criteria may be specified.

        Args:
            id_or_uri: Could be either the resource id or the resource uri.
            filter: General filter/query string.

        Returns:
             Collection of the requested resource.
        """
        if filter:
            filter = "?filter=" + quote(filter)

        uri = "{uri}{filter}".format(uri=self.build_uri(id_or_uri),
                                     filter=filter)
        logger.debug('Get resource collection (uri = %s)' % uri)
        response = self._connection.get(uri)
        return self.__get_members(response)

    def update_with_zero_body(self, uri, timeout=-1, custom_headers=None):
        """
        Makes a PUT request to update a resource, when no request body is required.

        Args:
            uri:
                Could be either the resource id or the resource uri.
            timeout:
                Timeout in seconds. Wait task completion by default. The timeout does not abort the operation
                in OneView, just stops waiting for its completion.
            custom_headers:
                Allows set specific HTTP headers.

        Returns:
            Updated resource.
        """
        logger.debug('Update with zero length body (uri = %s)' % uri)

        return self.__do_put(uri, None, timeout, custom_headers)

    def update(self,
               resource,
               uri=None,
               force=False,
               timeout=-1,
               custom_headers=None):
        """
        Makes a PUT request to update a resource, when a request body is required.

        Args:
            uri:
                Could be either the resource id or the resource uri.
            force:
                If set to true the operation completes despite any problems with network connectivity or errors
                on the resource itself. The default is false.
            timeout:
                Timeout in seconds. Wait task completion by default. The timeout does not abort the operation
                in OneView, just stops waiting for its completion.
            custom_headers:
                Allows set specific HTTP headers.

        Returns:
            Updated resource.
        """
        if not resource:
            logger.exception(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)
            raise ValueError(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)

        logger.debug('Update async (uri = %s, resource = %s)' %
                     (self._uri, str(resource)))

        if not uri:
            uri = resource['uri']

        if force:
            uri += '?force=True'

        return self.__do_put(uri, resource, timeout, custom_headers)

    def create_with_zero_body(self, uri=None, timeout=-1, custom_headers=None):
        """
        Makes a POST request to create a resource, when no request body is required.

        Args:
            uri:
                Could be either the resource id or the resource uri.
            timeout:
                Timeout in seconds. Wait task completion by default. The timeout does not abort the operation
                in OneView, just stops waiting for its completion.
            custom_headers:
                Allows set specific HTTP headers.

        Returns:
            Created resource.
        """
        if not uri:
            uri = self._uri

        logger.debug('Create with zero body (uri = %s)' % uri)

        return self.__do_post(uri, {}, timeout, custom_headers)

    def create(self, resource, uri=None, timeout=-1, custom_headers=None):
        """
        Makes a POST request to create a resource, when a request body is required.

        Args:
            uri:
                Could be either the resource id or the resource uri.
            timeout:
                Timeout in seconds. Wait task completion by default. The timeout does not abort the operation
                in OneView, just stops waiting for its completion.
            custom_headers:
                Allows set specific HTTP headers.

        Returns:
            Created resource.
        """
        if not resource:
            logger.exception(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)
            raise ValueError(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)

        if not uri:
            uri = self._uri

        logger.debug('Create (uri = %s, resource = %s)' % (uri, str(resource)))

        return self.__do_post(uri, resource, timeout, custom_headers)

    def patch(self,
              id_or_uri,
              operation,
              path,
              value,
              timeout=-1,
              custom_headers=None):
        """
        Uses the PATCH to update a resource.

        Only one operation can be performed in each PATCH call.

        Args:
            id_or_uri: Could be either the resource id or the resource uri
            operation: Patch operation
            path: Path
            value: Value
            timeout: Timeout in seconds. Wait task completion by default. The timeout does not abort the operation
                in OneView, just stops waiting for its completion.

        Returns:
            Updated resource.
        """
        uri = self.build_uri(id_or_uri)

        logger.debug(
            'Patch resource (uri = %s, op = %s, path = %s, value = %s)' %
            (uri, operation, path, value))

        patch_request = [{'op': operation, 'path': path, 'value': value}]
        task, entity = self._connection.patch(uri,
                                              patch_request,
                                              custom_headers=custom_headers)

        if not task:
            return entity

        return self._task_monitor.wait_for_task(task, timeout)

    def get_by(self, field, value, uri=None):
        """
        This function uses get_all passing a filter.

        The search is case insensitive.

        Args:
            field: Field name to filter.
            value: Value to filter.
            uri: Resource uri.

        Returns:
            dict
        """
        if not field:
            logger.exception(RESOURCE_CLIENT_INVALID_FIELD)
            raise ValueError(RESOURCE_CLIENT_INVALID_FIELD)

        if not uri:
            uri = self._uri
        self.__validate_resource_uri(uri)

        logger.debug('Get by (uri = %s, field = %s, value = %s)' %
                     (uri, field, str(value)))

        filter = "\"'{0}'='{1}'\"".format(field, value)
        return self.get_all(filter=filter, uri=uri)

    def get_by_name(self, name):
        """
        Retrieve a resource by his name.

        Args:
            name: Resource name.

        Returns:
            dict
        """
        result = self.get_by('name', name)
        if not result:
            return None
        else:
            return result[0]

    def get_utilization(self,
                        id_or_uri,
                        fields=None,
                        filter=None,
                        refresh=False,
                        view=None):
        """
        Retrieves historical utilization data for the specified resource, metrics, and time span.

        Args:
            id_or_uri:
                Resource identification
            fields:
                Name of the supported metric(s) to be retrieved in the format METRIC[,METRIC]...
                If unspecified, all metrics supported are returned.

            filter:
                Filters should be in the format FILTER_NAME=VALUE[,FILTER_NAME=VALUE]...
                E.g.: 'startDate=2016-05-30T11:20:44.541Z,endDate=2016-05-30T19:20:44.541Z'

                startDate
                    Start date of requested starting time range in ISO 8601 format. If omitted, the startDate is
                    determined by the endDate minus 24 hours.
                endDate
                    End date of requested starting time range in ISO 8601 format. When omitted the endDate includes
                    the latest data sample available.

                If an excessive number of samples would otherwise be returned, the results will be segmented. The
                caller is responsible for comparing the returned sliceStartTime with the requested startTime in the
                response. If the sliceStartTime is greater than the oldestSampleTime and the requested start time,
                the caller is responsible for repeating the request with endTime set to sliceStartTime to obtain the
                next segment. This process is repeated until the full data set is retrieved.

                If the resource has no data, the UtilizationData is still returned, but will contain no samples and
                sliceStartTime/sliceEndTime will be equal. oldestSampleTime/newestSampleTime will still be set
                appropriately (null if no data is available). If the filter just does not happen to overlap the data
                that a resource does have, then the metric history service will return null sample values for any
                missing samples.

            refresh:
                Specifies that if necessary an additional request will be queued to obtain the most recent
                utilization data from the iLO. The response will not include any refreshed data. To track the
                availability of the newly collected data, monitor the TaskResource identified by the refreshTaskUri
                property in the response. If null, no refresh was queued.

            view:
                Specifies the resolution interval length of the samples to be retrieved. This is reflected in the
                resolution in the returned response. Utilization data is automatically purged to stay within storage
                space constraints. Supported views are listed below.

                native
                    Resolution of the samples returned will be one sample for each 5-minute time period. This is the
                    default view and matches the resolution of the data returned by the iLO. Samples at this resolution
                    are retained up to one year.
                hour
                    Resolution of the samples returned will be one sample for each 60-minute time period. Samples are
                    calculated by averaging the available 5-minute data samples that occurred within the hour, except
                    for PeakPower which is calculated by reporting the peak observed 5-minute sample value data during
                    the hour. Samples at this resolution are retained up to three years.
                day
                    Resolution of the samples returned will be one sample for each 24-hour time period. One day is a
                    24-hour period that starts at midnight GMT regardless of the time zone in which the appliance or
                    client is located. Samples are calculated by averaging the available 5-minute data samples that
                    occurred during the day, except for PeakPower which is calculated by reporting the peak observed
                    5-minute sample value data during the day. Samples at this resolution are retained up to three
                    years.

        Returns:
            dict
        """

        if not id_or_uri:
            raise ValueError(RESOURCE_CLIENT_INVALID_ID)

        query = ''

        if filter:
            query += self.__make_query_filter(filter)

        if fields:
            query += "&fields=" + quote(fields)

        if refresh:
            query += "&refresh=true"

        if view:
            query += "&view=" + quote(view)

        if query:
            query = "?" + query[1:]

        uri = "{0}/utilization{1}".format(self.build_uri(id_or_uri), query)

        return self._connection.get(uri)

    def create_report(self, uri, timeout=-1):
        """
        Creates a report and returns the output.

        Args:
            uri: URI
            timeout:
                Timeout in seconds. Wait task completion by default. The timeout does not abort the operation
                in OneView, just stops waiting for its completion.

        Returns:
            list:
        """
        logger.debug('Creating Report (uri = %s)'.format(uri))
        task, _ = self._connection.post(uri, {})

        if not task:
            raise HPOneViewException(RESOURCE_CLIENT_TASK_EXPECTED)

        task = self._task_monitor.get_completed_task(task, timeout)

        return task['taskOutput']

    def build_uri(self, id_or_uri):
        if not id_or_uri:
            logger.exception(RESOURCE_CLIENT_INVALID_ID)
            raise ValueError(RESOURCE_CLIENT_INVALID_ID)

        if "/" in id_or_uri:
            self.__validate_resource_uri(id_or_uri)
            return id_or_uri
        else:
            return self._uri + "/" + id_or_uri

    def __validate_resource_uri(self, path):
        if self._uri not in path:
            logger.exception('Get by uri : unrecognized uri: (%s)' % path)
            raise HPOneViewUnknownType(UNRECOGNIZED_URI)

    def __make_query_filter(self, filter):
        filters = filter.split(",")
        formated_filter = "&filter=".join(quote(f) for f in filters)
        return "&filter=" + formated_filter

    def __get_members(self, mlist):
        if mlist and 'members' in mlist:
            return mlist['members']
        else:
            return []

    def __do_post(self, uri, resource, timeout, custom_headers):
        task, entity = self._connection.post(uri,
                                             resource,
                                             custom_headers=custom_headers)

        if not task:
            return entity

        return self._task_monitor.wait_for_task(task, timeout)

    def __do_put(self, uri, resource, timeout, custom_headers):
        task, body = self._connection.put(uri,
                                          resource,
                                          custom_headers=custom_headers)

        if not task:
            return body

        return self._task_monitor.wait_for_task(task, timeout)

    def __do_requests_to_getall(self, uri, count):
        items = []
        request_needed = True

        while request_needed:
            logger.debug(
                'Making HTTP request to get all resources. Uri: {0}'.format(
                    uri))
            response = self._connection.get(uri)
            members = self.__get_members(response)
            uri = response.get('nextPageUri')
            items += members
            logger.debug(
                "Response getAll: nextPageUri = {0}, members list length: {1}".
                format(uri, str(len(members))))
            request_needed = uri and not len(members) == 0 and (
                len(items) < count or count == -1)

        logger.debug('Total # of members found = {0}'.format(str(len(items))))
        return items
class ResourceClient(object):
    """
    This class implements common functions for HpOneView API rest
    """

    def __init__(self, con, uri):
        self._connection = con
        self._uri = uri
        self._task_monitor = TaskMonitor(con)

    def get_all(self, start=0, count=-1, filter='', query='', sort='', view='', fields='', uri=None):
        """
        Gets all items according with the given arguments.

        More than one request can be send to get the items, regardless the query parameter 'count', because the actual
        number of items in the response might differ from the requested count. Some types of resource have a limited
        number of items returned on each call. For those resources, additional calls are made to the API to retrieve
        any other items matching the given filter. The actual number of items can also differ from the requested call
        if the requested number of items would take too long.

        The use of optional parameters for OneView 2.0 is described at:
        http://h17007.www1.hpe.com/docs/enterprise/servers/oneview2.0/cic-api/en/api-docs/current/index.html

        Note:
            Single quote - "'" - inside a query parameter is not supported by OneView API.

        Args:
            start:
                The first item to return, using 0-based indexing.
                If not specified, the default is 0 - start with the first available item.
            count:
                The number of resources to return. A count of -1 requests all items (default).
            filter (list or str):
                A general filter/query string to narrow the list of items returned. The default is no
                filter; all resources are returned.
            query:
                A single query parameter can do what would take multiple parameters or multiple GET requests using
                filter. Use query for more complex queries. NOTE: This parameter is experimental for OneView 2.0.
            sort:
                The sort order of the returned data set. By default, the sort order is based on create time with the
                oldest entry first.
            view:
                Returns a specific subset of the attributes of the resource or collection by specifying the name of a
                predefined view. The default view is expand (show all attributes of the resource and all elements of
                the collections or resources).
            fields:
                Name of the fields.
            uri:
                A specific URI (optional)

        Returns:
            list: A list of items matching the specified filter.
        """
        if filter:
            filter = self.__make_query_filter(filter)

        if query:
            query = "&query=" + quote(query)

        if sort:
            sort = "&sort=" + quote(sort)

        if view:
            view = "&view=" + quote(view)

        if fields:
            fields = "&fields=" + quote(fields)

        path = uri if uri else self._uri
        self.__validate_resource_uri(path)

        symbol = '?' if '?' not in path else '&'

        uri = "{0}{1}start={2}&count={3}{4}{5}{6}{7}{8}".format(path, symbol, start, count, filter, query, sort,
                                                                view, fields)

        logger.debug('Getting all resources with uri: {0}'.format(uri))

        result = self.__do_requests_to_getall(uri, count)

        return result

    def delete_all(self, filter, force=False, timeout=-1):
        """
        Deletes all resources from the appliance that match the provided filter.

        Args:
            filter:
                A general filter/query string to narrow the list of items deleted.
            force:
                If set to true, the operation completes despite any problems with network connectivity or errors
                on the resource itself. The default is false.
            timeout:
                Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation
                in OneView; it just stops waiting for its completion.

        Returns:
            bool: Indicates if the resources were successfully deleted.
        """
        uri = "{}?filter={}&force={}".format(self._uri, quote(filter), force)
        logger.debug("Delete all resources (uri = %s)" % uri)

        task, body = self._connection.delete(uri)

        if not task:
            # 204 NO CONTENT
            # Successful return from a synchronous delete operation.
            return True

        return self._task_monitor.wait_for_task(task, timeout=timeout)

    def delete(self, resource, force=False, timeout=-1, custom_headers=None):

        if not resource:
            logger.exception(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)
            raise ValueError(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)

        if isinstance(resource, dict):
            if 'uri' in resource and resource['uri']:
                uri = resource['uri']
            else:
                logger.exception(RESOURCE_CLIENT_UNKNOWN_OBJECT_TYPE)
                raise HPOneViewUnknownType(RESOURCE_CLIENT_UNKNOWN_OBJECT_TYPE)
        else:
            uri = self.build_uri(resource)

        if force:
            uri += '?force=True'

        logger.debug("Delete resource (uri = %s, resource = %s)" %
                     (self._uri, str(resource)))

        task, body = self._connection.delete(uri, custom_headers=custom_headers)

        if not task:
            # 204 NO CONTENT
            # Successful return from a synchronous delete operation.
            return True

        task = self._task_monitor.wait_for_task(task, timeout=timeout)

        return task

    def get_schema(self):
        logger.debug('Get schema (uri = %s, resource = %s)' %
                     (self._uri, self._uri))
        return self._connection.get(self._uri + '/schema')

    def get(self, id_or_uri):
        """
        Args:
            id_or_uri: Can be either the resource ID or the resource URI.

        Returns:
             The requested resource.
        """
        uri = self.build_uri(id_or_uri)
        logger.debug('Get resource (uri = %s, ID = %s)' %
                     (uri, str(id_or_uri)))
        return self._connection.get(uri)

    def get_collection(self, id_or_uri, filter=''):
        """
        Retrieves a collection of resources.

        Use this function when the 'start' and 'count' parameters are not allowed in the GET call.
        Otherwise, use get_all instead.

        Optional filtering criteria may be specified.

        Args:
            id_or_uri: Can be either the resource ID or the resource URI.
            filter (list or str): General filter/query string.

        Returns:
             Collection of the requested resource.
        """
        if filter:
            filter = self.__make_query_filter(filter)
            filter = "?" + filter[1:]

        uri = "{uri}{filter}".format(uri=self.build_uri(id_or_uri), filter=filter)
        logger.debug('Get resource collection (uri = %s)' % uri)
        response = self._connection.get(uri)
        return self.__get_members(response)

    def update_with_zero_body(self, uri, timeout=-1, custom_headers=None):
        """
        Makes a PUT request to update a resource when no request body is required.

        Args:
            uri:
                Can be either the resource ID or the resource URI.
            timeout:
                Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation
                in OneView; it just stops waiting for its completion.
            custom_headers:
                Allows set specific HTTP headers.

        Returns:
            Updated resource.
        """
        logger.debug('Update with zero length body (uri = %s)' % uri)

        return self.__do_put(uri, None, timeout, custom_headers)

    def update(self, resource, uri=None, force=False, timeout=-1, custom_headers=None, default_values={}):
        """
        Makes a PUT request to update a resource when a request body is required.

        Args:
            resource:
                OneView resource dictionary.
            uri:
                Can be either the resource ID or the resource URI.
            force:
                If set to true, the operation completes despite any problems with network connectivity or errors
                on the resource itself. The default is false.
            timeout:
                Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation
                in OneView; it just stops waiting for its completion.
            custom_headers:
                Allows set specific HTTP headers.
            default_values:
                Dictionary with default values grouped by OneView API version. This dictionary will be be merged with
                the resource dictionary only if the dictionary does not contain the keys.
                This argument is optional and the default value is an empty dictionary.
                Ex.:
                    default_values = {
                        '200': {"type": "logical-switch-group"},
                        '300': {"type": "logical-switch-groupV300"}
                    }

        Returns:
            Updated resource.
        """
        if not resource:
            logger.exception(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)
            raise ValueError(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)

        logger.debug('Update async (uri = %s, resource = %s)' %
                     (self._uri, str(resource)))

        if not uri:
            uri = resource['uri']

        if force:
            uri += '?force=True'

        resource = self.merge_default_values(resource, default_values)

        return self.__do_put(uri, resource, timeout, custom_headers)

    def create_with_zero_body(self, uri=None, timeout=-1, custom_headers=None):
        """
        Makes a POST request to create a resource when no request body is required.

        Args:
            uri:
                Can be either the resource ID or the resource URI.
            timeout:
                Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation
                in OneView; it just stops waiting for its completion.
            custom_headers:
                Allows set specific HTTP headers.

        Returns:
            Created resource.
        """
        if not uri:
            uri = self._uri

        logger.debug('Create with zero body (uri = %s)' % uri)

        return self.__do_post(uri, {}, timeout, custom_headers)

    def create(self, resource, uri=None, timeout=-1, custom_headers=None, default_values={}):
        """
        Makes a POST request to create a resource when a request body is required.

        Args:
            resource:
                OneView resource dictionary.
            uri:
                Can be either the resource ID or the resource URI.
            timeout:
                Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation
                in OneView; it just stops waiting for its completion.
            custom_headers:
                Allows set specific HTTP headers.
            default_values:
                Dictionary with default values grouped by OneView API version. This dictionary will be be merged with
                the resource dictionary only if the dictionary does not contain the keys.
                This argument is optional and the default value is an empty dictionary.
                Ex.:
                    default_values = {
                        '200': {"type": "logical-switch-group"},
                        '300': {"type": "logical-switch-groupV300"}
                    }

        Returns:
            Created resource.
        """
        if not resource:
            logger.exception(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)
            raise ValueError(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED)

        if not uri:
            uri = self._uri

        logger.debug('Create (uri = %s, resource = %s)' %
                     (uri, str(resource)))

        resource = self.merge_default_values(resource, default_values)

        return self.__do_post(uri, resource, timeout, custom_headers)

    def upload(self, file_path, uri=None, timeout=-1):
        """
        Makes a multipart request.

        Args:
            file_path:
                File to upload.
            uri:
                A specific URI (optional).
            timeout:
                Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation
                in OneView; it just stops waiting for its completion.

        Returns:
            dict: Response body.
        """
        if not uri:
            uri = self._uri

        upload_file_name = os.path.basename(file_path)
        task, entity = self._connection.post_multipart_with_response_handling(uri, file_path, upload_file_name)

        if not task:
            return entity

        return self._task_monitor.wait_for_task(task, timeout)

    def patch(self, id_or_uri, operation, path, value, timeout=-1, custom_headers=None):
        """
        Uses the PATCH to update a resource.

        Only one operation can be performed in each PATCH call.

        Args:
            id_or_uri: Can be either the resource ID or the resource URI.
            operation: Patch operation
            path: Path
            value: Value
            timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation
                in OneView; it just stops waiting for its completion.

        Returns:
            Updated resource.
        """
        patch_request_body = [{'op': operation, 'path': path, 'value': value}]

        return self.patch_request(id_or_uri=id_or_uri,
                                  body=patch_request_body,
                                  timeout=timeout,
                                  custom_headers=custom_headers)

    def patch_request(self, id_or_uri, body, timeout=-1, custom_headers=None):
        """
        Uses the PATCH to update a resource.

        Only one operation can be performed in each PATCH call.

        Args:
            id_or_uri: Can be either the resource ID or the resource URI.
            body: Patch request body
            timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation
                in OneView; it just stops waiting for its completion.

        Returns:
            Updated resource.
        """
        uri = self.build_uri(id_or_uri)

        logger.debug('Patch resource (uri = %s, data = %s)' % (uri, body))

        custom_headers_copy = custom_headers.copy() if custom_headers else {}
        if self._connection._apiVersion >= 300 and 'Content-Type' not in custom_headers_copy:
            custom_headers_copy['Content-Type'] = 'application/json-patch+json'

        task, entity = self._connection.patch(uri, body, custom_headers=custom_headers_copy)

        if not task:
            return entity

        return self._task_monitor.wait_for_task(task, timeout)

    def get_by(self, field, value, uri=None):
        """
        This function uses get_all passing a filter.

        The search is case-insensitive.

        Args:
            field: Field name to filter.
            value: Value to filter.
            uri: Resource uri.

        Returns:
            dict
        """
        if not field:
            logger.exception(RESOURCE_CLIENT_INVALID_FIELD)
            raise ValueError(RESOURCE_CLIENT_INVALID_FIELD)

        if not uri:
            uri = self._uri
        self.__validate_resource_uri(uri)

        logger.debug('Get by (uri = %s, field = %s, value = %s)' %
                     (uri, field, str(value)))

        filter = "\"{0}='{1}'\"".format(field, value)
        results = self.get_all(filter=filter, uri=uri)

        # Workaround when the OneView filter does not work, it will filter again
        if "." not in field:
            # This filter only work for the first level
            results = [item for item in results if str(item.get(field, '')).lower() == value.lower()]

        return results

    def get_by_name(self, name):
        """
        Retrieve a resource by its name.

        Args:
            name: Resource name.

        Returns:
            dict
        """
        result = self.get_by('name', name)
        if not result:
            return None
        else:
            return result[0]

    def get_utilization(self, id_or_uri, fields=None, filter=None, refresh=False, view=None):
        """
        Retrieves historical utilization data for the specified resource, metrics, and time span.

        Args:
            id_or_uri:
                Resource identification
            fields:
                Name of the supported metric(s) to be retrieved in the format METRIC[,METRIC]...
                If unspecified, all metrics supported are returned.

            filter (list or str):
                Filters should be in the format FILTER_NAME=VALUE[,FILTER_NAME=VALUE]...
                E.g.: 'startDate=2016-05-30T11:20:44.541Z,endDate=2016-05-30T19:20:44.541Z'

                startDate
                    Start date of requested starting time range in ISO 8601 format. If omitted, the startDate is
                    determined by the endDate minus 24 hours.
                endDate
                    End date of requested starting time range in ISO 8601 format. When omitted, the endDate includes
                    the latest data sample available.

                If an excessive number of samples would otherwise be returned, the results will be segmented. The
                caller is responsible for comparing the returned sliceStartTime with the requested startTime in the
                response. If the sliceStartTime is greater than the oldestSampleTime and the requested start time,
                the caller is responsible for repeating the request with endTime set to sliceStartTime to obtain the
                next segment. This process is repeated until the full data set is retrieved.

                If the resource has no data, the UtilizationData is still returned but will contain no samples and
                sliceStartTime/sliceEndTime will be equal. oldestSampleTime/newestSampleTime will still be set
                appropriately (null if no data is available). If the filter does not happen to overlap the data
                that a resource has, then the metric history service will return null sample values for any
                missing samples.

            refresh:
                Specifies that if necessary, an additional request will be queued to obtain the most recent
                utilization data from the iLO. The response will not include any refreshed data. To track the
                availability of the newly collected data, monitor the TaskResource identified by the refreshTaskUri
                property in the response. If null, no refresh was queued.

            view:
                Specifies the resolution interval length of the samples to be retrieved. This is reflected in the
                resolution in the returned response. Utilization data is automatically purged to stay within storage
                space constraints. Supported views are listed below:

                native
                    Resolution of the samples returned will be one sample for each 5-minute time period. This is the
                    default view and matches the resolution of the data returned by the iLO. Samples at this resolution
                    are retained up to one year.
                hour
                    Resolution of the samples returned will be one sample for each 60-minute time period. Samples are
                    calculated by averaging the available 5-minute data samples that occurred within the hour, except
                    for PeakPower which is calculated by reporting the peak observed 5-minute sample value data during
                    the hour. Samples at this resolution are retained up to three years.
                day
                    Resolution of the samples returned will be one sample for each 24-hour time period. One day is a
                    24-hour period that starts at midnight GMT regardless of the time zone in which the appliance or
                    client is located. Samples are calculated by averaging the available 5-minute data samples that
                    occurred during the day, except for PeakPower which is calculated by reporting the peak observed
                    5-minute sample value data during the day. Samples at this resolution are retained up to three
                    years.

        Returns:
            dict
        """

        if not id_or_uri:
            raise ValueError(RESOURCE_CLIENT_INVALID_ID)

        query = ''

        if filter:
            query += self.__make_query_filter(filter)

        if fields:
            query += "&fields=" + quote(fields)

        if refresh:
            query += "&refresh=true"

        if view:
            query += "&view=" + quote(view)

        if query:
            query = "?" + query[1:]

        uri = "{0}/utilization{1}".format(self.build_uri(id_or_uri), query)

        return self._connection.get(uri)

    def create_report(self, uri, timeout=-1):
        """
        Creates a report and returns the output.

        Args:
            uri: URI
            timeout:
                Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation
                in OneView; it just stops waiting for its completion.

        Returns:
            list:
        """
        logger.debug('Creating Report (uri = %s)'.format(uri))
        task, _ = self._connection.post(uri, {})

        if not task:
            raise HPOneViewException(RESOURCE_CLIENT_TASK_EXPECTED)

        task = self._task_monitor.get_completed_task(task, timeout)

        return task['taskOutput']

    def build_uri(self, id_or_uri):
        if not id_or_uri:
            logger.exception(RESOURCE_CLIENT_INVALID_ID)
            raise ValueError(RESOURCE_CLIENT_INVALID_ID)

        if "/" in id_or_uri:
            self.__validate_resource_uri(id_or_uri)
            return id_or_uri
        else:
            return self._uri + "/" + id_or_uri

    def build_subresource_uri(self, resource_id_or_uri=None, subresource_id_or_uri=None, subresource_path=''):
        if subresource_id_or_uri and "/" in subresource_id_or_uri:
            return subresource_id_or_uri
        else:
            if not resource_id_or_uri:
                raise HPOneViewValueError(RESOURCE_ID_OR_URI_REQUIRED)

            resource_uri = self.build_uri(resource_id_or_uri)

            uri = "{}/{}/{}".format(resource_uri, subresource_path, str(subresource_id_or_uri or ''))
            uri = uri.replace("//", "/")

            if uri.endswith("/"):
                uri = uri[:-1]

            return uri

    def download(self, uri, file_path):
        """
        Downloads the contents of the requested URI to a stream.

        Args:
            uri: URI
            file_path: File path destination

        Returns:
            bool: Indicates if the file was successfully downloaded.
        """
        with open(file_path, 'wb') as file:
            return self._connection.download_to_stream(file, uri)

    def __validate_resource_uri(self, path):
        if self._uri not in path:
            logger.exception('Get by uri : unrecognized uri: (%s)' % path)
            raise HPOneViewUnknownType(UNRECOGNIZED_URI)

    def __make_query_filter(self, filters):
        if isinstance(filters, list):
            formated_filter = "&filter=".join(quote(f) for f in filters)
        else:
            formated_filter = quote(filters)

        return "&filter=" + formated_filter

    def __get_members(self, mlist):
        if mlist and 'members' in mlist:
            return mlist['members']
        else:
            return []

    def __do_post(self, uri, resource, timeout, custom_headers):
        task, entity = self._connection.post(uri, resource, custom_headers=custom_headers)

        if not task:
            return entity

        return self._task_monitor.wait_for_task(task, timeout)

    def __do_put(self, uri, resource, timeout, custom_headers):
        task, body = self._connection.put(uri, resource, custom_headers=custom_headers)

        if not task:
            return body

        return self._task_monitor.wait_for_task(task, timeout)

    def __do_requests_to_getall(self, uri, requested_count):
        items = []

        while uri:
            logger.debug('Making HTTP request to get all resources. Uri: {0}'.format(uri))
            response = self._connection.get(uri)
            members = self.__get_members(response)
            items += members

            logger.debug("Response getAll: nextPageUri = {0}, members list length: {1}".format(uri, str(len(members))))
            uri = self.__get_next_page(response, items, requested_count)

        logger.debug('Total # of members found = {0}'.format(str(len(items))))
        return items

    def __get_next_page(self, response, items, requested_count):
        next_page_is_empty = response.get('nextPageUri') is None
        has_different_next_page = not response.get('uri') == response.get('nextPageUri')
        has_next_page = not next_page_is_empty and has_different_next_page

        if len(items) >= requested_count and requested_count != -1:
            return None

        return response.get('nextPageUri') if has_next_page else None

    def merge_default_values(self, resource, default_values):
        if not default_values:
            return resource

        merged_resource = None

        if not isinstance(resource, list):
            api_version = str(self._connection._apiVersion)
            data = default_values.get(api_version, {}).copy()
            merged_resource = merge_resources(data, resource)

        return merged_resource or resource
class TaskMonitorTest(unittest.TestCase):
    URI = "/rest/testuri"

    def setUp(self):
        super(TaskMonitorTest, self).setUp()
        self.host = '127.0.0.1'
        self.connection = connection(self.host)
        self.task_monitor = TaskMonitor(self.connection)

    @mock.patch.object(connection, 'get')
    def test_get_associated_resource_with_task(self, mock_get):

        task = {
            "category": "tasks",
            "type": "TaskResourceV2",
            "associatedResource": {
                "resourceUri": "/rest/associatedresourceuri"
            }}

        mock_get.return_value = {"resource": "resource1"}

        ret_task, entity = self.task_monitor.get_associated_resource(task.copy())

        self.assertEqual(entity, {"resource": "resource1"})
        self.assertEqual(ret_task, task)
        mock_get.assert_called_once_with("/rest/associatedresourceuri")

    @mock.patch.object(connection, 'get')
    def test_get_associated_resource_with_backup(self, mock_get):
        backup = {
            "category": "backups",
            "type": "BACKUP",
            "taskUri": "/rest/taskuri",
        }

        task = {
            "category": "TaskResourceV2",
            "type": "tasks",
            "uri": "/rest/justuri",
        }

        def inner_get(uri):
            if uri == "/rest/taskuri":
                return task.copy()
            else:
                return {"resource": "resource1"}

        mock_get.side_effect = inner_get
        mock_get.return_value = task.copy()

        ret_task, entity = self.task_monitor.get_associated_resource(backup.copy())

        self.assertEqual(entity, {"resource": "resource1"})
        self.assertEqual(ret_task, task)

    def test_get_associated_resource_support_dump(self):

        task = {
            "category": "tasks",
            "type": "TaskResourceV2",
            "associatedResource": {
                "resourceUri": "/rest/appliance/support-dumps/hennig59.eco-MyDump16-E-2016_07_07-17_53_42.867287.sdmp"
            }}

        ret_task, entity = self.task_monitor.get_associated_resource(task.copy())

        self.assertEqual(
            entity,
            "/rest/appliance/support-dumps/hennig59.eco-MyDump16-E-2016_07_07-17_53_42.867287.sdmp")
        self.assertEqual(ret_task, task)

    def test_get_associated_resource_with_task_empty(self):
        try:
            self.task_monitor.get_associated_resource({})
        except HPOneViewUnknownType as e:
            self.assertEqual(MSG_INVALID_TASK, e.msg)
        else:
            self.fail()

    def test_get_associated_resource_with_invalid_task(self):
        try:
            self.task_monitor.get_associated_resource({"category": "networking"})
        except HPOneViewUnknownType as e:
            self.assertEqual(MSG_UNKNOWN_OBJECT_TYPE, e.msg)
        else:
            self.fail()

    def test_get_associated_resource_with_invalid_type(self):
        try:
            self.task_monitor.get_associated_resource({"category": "tasks",
                                                       "type": "TaskResource"})
        except HPOneViewInvalidResource as e:
            self.assertEqual(MSG_TASK_TYPE_UNRECONIZED % "TaskResource", e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running(self, mock_get):

        mock_get.return_value = {"uri": "uri",
                                 "taskState": "Pending"}

        self.assertTrue(self.task_monitor.is_task_running({"uri": "uri"}))

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running_false(self, mock_get):
        mock_get.return_value = {"uri": "uri",
                                 "taskState": "Warning"}

        self.assertFalse(self.task_monitor.is_task_running({"uri": "uri"}))

    @mock.patch.object(TaskMonitor, 'is_task_running')
    def test_wait_for_task_timeout(self, mock_is_running):

        mock_is_running.return_value = True
        timeout = 2

        try:
            self.task_monitor.wait_for_task({"uri": "uri"}, timeout)
        except HPOneViewTimeout as e:
            self.assertEqual(MSG_TIMEOUT % timeout, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch('time.sleep')
    def test_wait_for_task_increasing_sleep(self, mock_sleep, mock_is_running):

        mock_is_running.return_value = True
        timeout = 0.1

        # should call sleep increasing 1 until 10
        calls = [call(1), call(2), call(3), call(4), call(5), call(6), call(7),
                 call(8), call(9), call(10), call(10), call(10)]

        try:
            self.task_monitor.wait_for_task({"uri": "uri"}, timeout)
        except HPOneViewTimeout as e:
            mock_sleep.assert_has_calls(calls)
            self.assertEqual(MSG_TIMEOUT % timeout, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_message(self, mock_get, mock_is_running):

        task = {"uri": "uri",
                "taskState": "Error",
                "taskErrors": [{"message": "Error Message"}]}

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual("Error Message", e.msg)
        else:
            self.fail()

    def test_wait_for_task_empty(self):
        try:
            self.task_monitor.wait_for_task({})
        except HPOneViewUnknownType as e:
            self.assertEqual(MSG_INVALID_TASK, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_empty(self, mock_get, mock_is_running):

        task = {"uri": "uri",
                "taskState": "Error",
                "taskStatus": "Failed",
                }

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual("Failed", e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_unknown(self, mock_get, mock_is_running):

        task = {"uri": "uri",
                "taskState": "Error",
                }

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual(MSG_UNKNOWN_EXCEPTION, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'get_associated_resource')
    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task(self, mock_get, mock_is_running, mock_assoc_res):
        task = {"uri": "uri",
                "type": "TaskResourceV2",
                "name": "update",
                "taskState": "Completed",
                }

        mock_is_running.return_value = False
        mock_get.return_value = task
        mock_assoc_res.return_value = task.copy(), {"resource": "resource1"}

        ret_entity = self.task_monitor.wait_for_task(task.copy())

        self.assertEqual(ret_entity, {"resource": "resource1"})

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_unexpected_result(self, mock_get, mock_is_running):
        task = {"uri": "uri",
                "type": "Undefined",
                "name": "Undefined",
                "taskState": "Completed",
                }

        mock_is_running.return_value = False
        mock_get.return_value = task

        ret_entity = self.task_monitor.wait_for_task(task.copy())

        self.assertEqual(ret_entity, task.copy())

    @mock.patch.object(TaskMonitor, 'get_associated_resource')
    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_delete(self, mock_get, mock_is_running, mock_assoc_res):
        task = {"uri": "uri",
                "type": "TaskResourceV2",
                "name": "Delete",
                "taskState": "Completed",
                }

        mock_is_running.return_value = False
        mock_get.return_value = task
        mock_assoc_res.return_value = task.copy(), {"resource": "resource1"}

        ret = self.task_monitor.wait_for_task(task.copy())

        # may return a different type
        self.assertEqual(True, ret)

    @mock.patch.object(connection, 'get')
    def test_get(self, mock_get):
        self.task_monitor.get({"uri": "an uri"})
        mock_get.assert_called_once_with("an uri")

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_get_completed_task(self, mock_get, mock_is_running):
        task = {"uri": "uri",
                "type": "TaskResourceV2",
                "name": "Create unexpected zoning report'",
                "taskState": "Completed",
                "taskOutput": []
                }

        mock_is_running.return_value = False
        mock_get.return_value = task

        response = self.task_monitor.get_completed_task(task.copy())

        self.assertEqual(task, response)
示例#4
0
class TaskMonitorTest(unittest.TestCase):
    URI = "/rest/testuri"

    def setUp(self):
        super(TaskMonitorTest, self).setUp()
        self.host = '127.0.0.1'
        self.connection = connection(self.host)
        self.task_monitor = TaskMonitor(self.connection)

    @mock.patch.object(connection, 'get')
    def test_get_associated_resource_with_task(self, mock_get):

        task = {
            "category": "tasks",
            "type": "TaskResourceV2",
            "associatedResource": {
                "resourceUri": "/rest/associatedresourceuri"
            }
        }

        mock_get.return_value = {"resource": "resource1"}

        ret_task, entity = self.task_monitor.get_associated_resource(
            task.copy())

        self.assertEqual(entity, {"resource": "resource1"})
        self.assertEqual(ret_task, task)
        mock_get.assert_called_once_with("/rest/associatedresourceuri")

    @mock.patch.object(connection, 'get')
    def test_get_associated_resource_with_backup(self, mock_get):
        backup = {
            "category": "backups",
            "type": "BACKUP",
            "taskUri": "/rest/taskuri",
        }

        task = {
            "category": "TaskResourceV2",
            "type": "tasks",
            "uri": "/rest/justuri",
        }

        def inner_get(uri):
            if uri == "/rest/taskuri":
                return task.copy()
            else:
                return {"resource": "resource1"}

        mock_get.side_effect = inner_get
        mock_get.return_value = task.copy()

        ret_task, entity = self.task_monitor.get_associated_resource(
            backup.copy())

        self.assertEqual(entity, {"resource": "resource1"})
        self.assertEqual(ret_task, task)

    def test_get_associated_resource_support_dump(self):

        task = {
            "category": "tasks",
            "type": "TaskResourceV2",
            "associatedResource": {
                "resourceUri":
                "/rest/appliance/support-dumps/hennig59.eco-MyDump16-E-2016_07_07-17_53_42.867287.sdmp"
            }
        }

        ret_task, entity = self.task_monitor.get_associated_resource(
            task.copy())

        self.assertEqual(
            entity,
            "/rest/appliance/support-dumps/hennig59.eco-MyDump16-E-2016_07_07-17_53_42.867287.sdmp"
        )
        self.assertEqual(ret_task, task)

    def test_get_associated_resource_with_task_empty(self):
        try:
            self.task_monitor.get_associated_resource({})
        except HPOneViewUnknownType as e:
            self.assertEqual(MSG_INVALID_TASK, e.msg)
        else:
            self.fail()

    def test_get_associated_resource_with_invalid_task(self):
        try:
            self.task_monitor.get_associated_resource(
                {"category": "networking"})
        except HPOneViewUnknownType as e:
            self.assertEqual(MSG_UNKNOWN_OBJECT_TYPE, e.msg)
        else:
            self.fail()

    def test_get_associated_resource_with_invalid_type(self):
        try:
            self.task_monitor.get_associated_resource({
                "category": "tasks",
                "type": "TaskResource"
            })
        except HPOneViewInvalidResource as e:
            self.assertEqual(MSG_TASK_TYPE_UNRECONIZED % "TaskResource", e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running(self, mock_get):

        mock_get.return_value = {"uri": "uri", "taskState": "Pending"}

        self.assertTrue(self.task_monitor.is_task_running({"uri": "uri"}))

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running_false(self, mock_get):
        mock_get.return_value = {"uri": "uri", "taskState": "Warning"}

        self.assertFalse(self.task_monitor.is_task_running({"uri": "uri"}))

    @mock.patch.object(TaskMonitor, 'is_task_running')
    def test_wait_for_task_timeout(self, mock_is_running):

        mock_is_running.return_value = True
        timeout = 2

        try:
            self.task_monitor.wait_for_task({"uri": "uri"}, timeout)
        except HPOneViewTimeout as e:
            self.assertEqual(MSG_TIMEOUT % timeout, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch('time.sleep')
    def test_wait_for_task_increasing_sleep(self, mock_sleep, mock_is_running):

        mock_is_running.return_value = True
        timeout = 0.1

        # should call sleep increasing 1 until 10
        calls = [
            call(1),
            call(2),
            call(3),
            call(4),
            call(5),
            call(6),
            call(7),
            call(8),
            call(9),
            call(10),
            call(10),
            call(10)
        ]

        try:
            self.task_monitor.wait_for_task({"uri": "uri"}, timeout)
        except HPOneViewTimeout as e:
            mock_sleep.assert_has_calls(calls)
            self.assertEqual(MSG_TIMEOUT % timeout, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_message(self, mock_get, mock_is_running):

        task = {
            "uri": "uri",
            "taskState": "Error",
            "taskErrors": [{
                "message": "Error Message"
            }]
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual("Error Message", e.msg)
            self.assertEqual(None, e.error_code)
        else:
            self.fail("Expected exception not raised")

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_message_and_error_code(
            self, mock_get, mock_is_running):

        task = {
            "uri":
            "uri",
            "taskState":
            "Error",
            "taskErrors": [{
                "message": "Error Message",
                "errorCode": "ProfileAlreadyExistsInServer"
            }]
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual("Error Message", e.msg)
            self.assertEqual("ProfileAlreadyExistsInServer", e.error_code)
        else:
            self.fail("Expected exception not raised")

    def test_wait_for_task_empty(self):
        try:
            self.task_monitor.wait_for_task({})
        except HPOneViewUnknownType as e:
            self.assertEqual(MSG_INVALID_TASK, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_empty(self, mock_get, mock_is_running):

        task = {
            "uri": "uri",
            "taskState": "Error",
            "taskStatus": "Failed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual("Failed", e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_unknown(self, mock_get, mock_is_running):

        task = {
            "uri": "uri",
            "taskState": "Error",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual(MSG_UNKNOWN_EXCEPTION, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'get_associated_resource')
    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task(self, mock_get, mock_is_running, mock_assoc_res):
        task = {
            "uri": "uri",
            "type": "TaskResourceV2",
            "name": "update",
            "taskState": "Completed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task
        mock_assoc_res.return_value = task.copy(), {"resource": "resource1"}

        ret_entity = self.task_monitor.wait_for_task(task.copy())

        self.assertEqual(ret_entity, {"resource": "resource1"})

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_unexpected_result(self, mock_get, mock_is_running):
        task = {
            "uri": "uri",
            "type": "Undefined",
            "name": "Undefined",
            "taskState": "Completed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        ret_entity = self.task_monitor.wait_for_task(task.copy())

        self.assertEqual(ret_entity, task.copy())

    @mock.patch.object(TaskMonitor, 'get_associated_resource')
    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_delete(self, mock_get, mock_is_running,
                                  mock_assoc_res):
        task = {
            "uri": "uri",
            "type": "TaskResourceV2",
            "name": "Delete",
            "taskState": "Completed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task
        mock_assoc_res.return_value = task.copy(), {"resource": "resource1"}

        ret = self.task_monitor.wait_for_task(task.copy())

        # may return a different type
        self.assertEqual(True, ret)

    @mock.patch.object(connection, 'get')
    def test_get(self, mock_get):
        self.task_monitor.get({"uri": "an uri"})
        mock_get.assert_called_once_with("an uri")

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_get_completed_task(self, mock_get, mock_is_running):
        task = {
            "uri": "uri",
            "type": "TaskResourceV2",
            "name": "Create unexpected zoning report'",
            "taskState": "Completed",
            "taskOutput": []
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        response = self.task_monitor.get_completed_task(task.copy())

        self.assertEqual(task, response)
class TaskMonitorTest(unittest.TestCase):
    def setUp(self):
        super(TaskMonitorTest, self).setUp()
        self.host = '127.0.0.1'
        self.connection = connection(self.host)
        self.task_monitor = TaskMonitor(self.connection)

    @mock.patch.object(connection, 'get')
    def test_get_associated_resource_with_task(self, mock_get):

        task = {
            "category": "tasks",
            "type": "TaskResourceV2",
            "associatedResource": {
                "resourceUri": "/rest/associatedresourceuri"
            }
        }

        mock_get.return_value = {"resource": "resource1"}

        ret_task, entity = self.task_monitor.get_associated_resource(
            task.copy())

        self.assertEqual(entity, {"resource": "resource1"})
        self.assertEqual(ret_task, task)
        mock_get.assert_called_once_with("/rest/associatedresourceuri")

    @mock.patch.object(connection, 'get')
    def test_get_associated_resource_with_backup(self, mock_get):
        backup = {
            "category": "backups",
            "type": "BACKUP",
            "taskUri": "/rest/taskuri",
        }

        task = {
            "category": "TaskResourceV2",
            "type": "tasks",
            "uri": "/rest/justuri",
        }

        def inner_get(uri):
            if uri == "/rest/taskuri":
                return task.copy()
            else:
                return {"resource": "resource1"}

        mock_get.side_effect = inner_get
        mock_get.return_value = task.copy()

        ret_task, entity = self.task_monitor.get_associated_resource(
            backup.copy())

        self.assertEqual(entity, {"resource": "resource1"})
        self.assertEqual(ret_task, task)

    def test_get_associated_resource_support_dump(self):

        task = {
            "category": "tasks",
            "type": "TaskResourceV2",
            "associatedResource": {
                "resourceUri":
                "/rest/appliance/support-dumps/hennig59.eco-MyDump16-E-2016_07_07-17_53_42.867287.sdmp"
            }
        }

        ret_task, entity = self.task_monitor.get_associated_resource(
            task.copy())

        self.assertEqual(
            entity,
            "/rest/appliance/support-dumps/hennig59.eco-MyDump16-E-2016_07_07-17_53_42.867287.sdmp"
        )
        self.assertEqual(ret_task, task)

    def test_get_associated_resource_with_task_empty(self):
        try:
            self.task_monitor.get_associated_resource({})
        except HPOneViewUnknownType as e:
            self.assertEqual(MSG_INVALID_TASK, e.msg)
        else:
            self.fail()

    def test_get_associated_resource_with_invalid_task(self):
        try:
            self.task_monitor.get_associated_resource(
                {"category": "networking"})
        except HPOneViewUnknownType as e:
            self.assertEqual(MSG_UNKNOWN_OBJECT_TYPE, e.msg)
        else:
            self.fail()

    def test_get_associated_resource_with_invalid_type(self):
        try:
            self.task_monitor.get_associated_resource({
                "category": "tasks",
                "type": "TaskResource"
            })
        except HPOneViewInvalidResource as e:
            self.assertEqual(MSG_TASK_TYPE_UNRECONIZED % "TaskResource", e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running(self, mock_get):

        mock_get.return_value = {"uri": "uri", "taskState": "Pending"}

        self.assertTrue(self.task_monitor.is_task_running({"uri": "uri"}))

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running_false(self, mock_get):
        mock_get.return_value = {"uri": "uri", "taskState": "Warning"}

        self.assertFalse(self.task_monitor.is_task_running({"uri": "uri"}))

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running_ignore_network_failure(self, mock_get):
        mock_get.side_effect = [{
            "uri": "uri",
            "taskState": "Pending"
        },
                                EnvironmentError(ETIMEDOUT, ERR_MSG)]

        connection_failure_control = dict(
            last_success=self.task_monitor.get_current_seconds())

        # 1. Success get
        self.assertTrue(
            self.task_monitor.is_task_running({"uri": "uri"},
                                              connection_failure_control))
        # 2. Timeout error, expected True anyway
        self.assertTrue(
            self.task_monitor.is_task_running({"uri": "uri"},
                                              connection_failure_control))

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running_network_failure_without_timeout_control(
            self, mock_get):
        mock_get.side_effect = EnvironmentError(ETIMEDOUT, ERR_MSG)

        self.assertRaises(EnvironmentError, self.task_monitor.is_task_running,
                          {"uri": "uri"})

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running_generic_failure_with_timeout_control(
            self, mock_get):
        mock_get.side_effect = Exception(ERR_MSG)

        connection_failure_control = dict(
            last_success=self.task_monitor.get_current_seconds())

        self.assertRaises(Exception, self.task_monitor.is_task_running,
                          {"uri": "uri"}, connection_failure_control)

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running_ignore_network_failure_reset_count(
            self, mock_get):
        mock_get.side_effect = [{
            "uri": "uri",
            "taskState": "Pending"
        },
                                EnvironmentError(ECONNABORTED, ERR_MSG), {
                                    "uri": "uri",
                                    "taskState": "Pending"
                                },
                                EnvironmentError(ETIMEDOUT, ERR_MSG)]

        connection_failure_control = dict(
            last_success=self.task_monitor.get_current_seconds())

        # 1. Success get
        self.assertTrue(
            self.task_monitor.is_task_running({"uri": "uri"},
                                              connection_failure_control))
        # 2. Inside the timeout, must continue
        self.assertTrue(
            self.task_monitor.is_task_running({"uri": "uri"},
                                              connection_failure_control))
        # Force exceed the timeout
        seconds_to_decrement = TaskMonitor.CONNECTION_FAILURE_TIMEOUT + 10
        connection_failure_control['last_success'] -= seconds_to_decrement
        # 3. Success get (reset timeout)
        self.assertTrue(
            self.task_monitor.is_task_running({"uri": "uri"},
                                              connection_failure_control))
        # 4. Inside the timeout again, must continue
        self.assertTrue(
            self.task_monitor.is_task_running({"uri": "uri"},
                                              connection_failure_control))

    @mock.patch.object(TaskMonitor, 'get')
    def test_is_task_running_ignore_network_failure_exceed_timeout(
            self, mock_get):
        mock_get.side_effect = [{
            "uri": "uri",
            "taskState": "Pending"
        },
                                EnvironmentError(ECONNABORTED, ERR_MSG)]

        conn_failure_control = dict(
            last_success=self.task_monitor.get_current_seconds())

        # 1. Success get
        self.assertTrue(
            self.task_monitor.is_task_running({"uri": "uri"},
                                              conn_failure_control))
        # Decrement last success to force exceed the timeout
        seconds_to_decrement = TaskMonitor.CONNECTION_FAILURE_TIMEOUT + 10
        conn_failure_control['last_success'] -= seconds_to_decrement
        # 2. Should fail, timeout exceeded
        self.assertRaises(EnvironmentError, self.task_monitor.is_task_running,
                          {"uri": "uri"}, conn_failure_control)

    @mock.patch.object(TaskMonitor, 'is_task_running')
    def test_wait_for_task_timeout(self, mock_is_running):

        mock_is_running.return_value = True
        timeout = 2

        try:
            self.task_monitor.wait_for_task({"uri": "uri"}, timeout)
        except HPOneViewTimeout as e:
            self.assertEqual(MSG_TIMEOUT % timeout, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch('time.sleep')
    def test_wait_for_task_increasing_sleep(self, mock_sleep, mock_is_running):

        mock_is_running.return_value = True
        timeout = 0.1

        # should call sleep increasing 1 until 10
        calls = [
            call(1),
            call(2),
            call(3),
            call(4),
            call(5),
            call(6),
            call(7),
            call(8),
            call(9),
            call(10),
            call(10),
            call(10)
        ]

        try:
            self.task_monitor.wait_for_task({"uri": "uri"}, timeout)
        except HPOneViewTimeout as e:
            mock_sleep.assert_has_calls(calls)
            self.assertEqual(MSG_TIMEOUT % timeout, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_message(self, mock_get, mock_is_running):

        task = {
            "uri": "uri",
            "taskState": "Error",
            "taskErrors": [{
                "message": "Error Message"
            }]
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual("Error Message", e.msg)
            self.assertEqual(None, e.error_code)
        else:
            self.fail("Expected exception not raised")

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_message_and_error_code(
            self, mock_get, mock_is_running):

        task = {
            "uri":
            "uri",
            "taskState":
            "Error",
            "taskErrors": [{
                "message": "Error Message",
                "errorCode": "ProfileAlreadyExistsInServer"
            }]
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual("Error Message", e.msg)
            self.assertEqual("ProfileAlreadyExistsInServer", e.error_code)
        else:
            self.fail("Expected exception not raised")

    def test_wait_for_task_empty(self):
        try:
            self.task_monitor.wait_for_task({})
        except HPOneViewUnknownType as e:
            self.assertEqual(MSG_INVALID_TASK, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_empty(self, mock_get, mock_is_running):

        task = {
            "uri": "uri",
            "taskState": "Error",
            "taskStatus": "Failed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual("Failed", e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_with_error_unknown(self, mock_get, mock_is_running):

        task = {
            "uri": "uri",
            "taskState": "Error",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        try:
            self.task_monitor.wait_for_task(task.copy())
        except HPOneViewTaskError as e:
            self.assertEqual(MSG_UNKNOWN_EXCEPTION, e.msg)
        else:
            self.fail()

    @mock.patch.object(TaskMonitor, 'get_associated_resource')
    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task(self, mock_get, mock_is_running, mock_assoc_res):
        task = {
            "uri": "uri",
            "type": "TaskResourceV2",
            "name": "update",
            "taskState": "Completed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task
        mock_assoc_res.return_value = task.copy(), {"resource": "resource1"}

        ret_entity = self.task_monitor.wait_for_task(task.copy())

        self.assertEqual(ret_entity, {"resource": "resource1"})

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_unexpected_result(self, mock_get, mock_is_running):
        task = {
            "uri": "uri",
            "type": "Undefined",
            "name": "Undefined",
            "taskState": "Completed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        ret_entity = self.task_monitor.wait_for_task(task.copy())

        self.assertEqual(ret_entity, task.copy())

    @mock.patch.object(TaskMonitor, 'get_associated_resource')
    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_delete(self, mock_get, mock_is_running,
                                  mock_assoc_res):
        task = {
            "uri": "uri",
            "type": "TaskResourceV2",
            "name": "Delete",
            "taskState": "Completed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task
        mock_assoc_res.return_value = task.copy(), {"resource": "resource1"}

        ret = self.task_monitor.wait_for_task(task.copy())

        # may return a different type
        self.assertEqual(True, ret)

    @mock.patch.object(TaskMonitor, 'get_associated_resource')
    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_remove(self, mock_get, mock_is_running,
                                  mock_assoc_res):
        task = {
            "uri": "uri",
            "type": "TaskResourceV2",
            "name": "Remove",
            "taskState": "Removed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task
        mock_assoc_res.return_value = task.copy(), {"resource": "resource1"}

        ret = self.task_monitor.wait_for_task(task.copy())

        self.assertEqual(True, ret)

    @mock.patch.object(TaskMonitor, 'get_associated_resource')
    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_remove_san_manager(self, mock_get, mock_is_running,
                                              mock_assoc_res):
        task = {
            "uri": "uri",
            "type": "TaskResourceV2",
            "name": "Remove SAN manager",
            "taskState": "Completed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task
        mock_assoc_res.return_value = task.copy(), {"resource": "resource1"}

        ret = self.task_monitor.wait_for_task(task.copy())

        self.assertEqual(True, ret)

    @mock.patch.object(TaskMonitor, 'get_associated_resource')
    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_wait_for_task_delete_server_hardware_type(self, mock_get,
                                                       mock_is_running,
                                                       mock_assoc_res):
        task = {
            "uri": "uri",
            "type": "TaskResourceV2",
            "name": "Delete server hardware type",
            "taskState": "Completed",
        }

        mock_is_running.return_value = False
        mock_get.return_value = task
        mock_assoc_res.return_value = task.copy(), {"resource": "resource1"}

        ret = self.task_monitor.wait_for_task(task.copy())

        self.assertEqual(True, ret)

    @mock.patch.object(connection, 'get')
    def test_get(self, mock_get):
        self.task_monitor.get({"uri": "an uri"})
        mock_get.assert_called_once_with("an uri")

    @mock.patch.object(TaskMonitor, 'is_task_running')
    @mock.patch.object(TaskMonitor, 'get')
    def test_get_completed_task(self, mock_get, mock_is_running):
        task = {
            "uri": "uri",
            "type": "TaskResourceV2",
            "name": "Create unexpected zoning report'",
            "taskState": "Completed",
            "taskOutput": []
        }

        mock_is_running.return_value = False
        mock_get.return_value = task

        response = self.task_monitor.get_completed_task(task.copy())

        self.assertEqual(task, response)