예제 #1
0
def display_dict(j: dict, level=0) -> None:
    """
    Walks the cruise-control Response dict and attempts to display
    the given top-level keys to the humans.


    :param j:
    :return:
    """
    warnings.warn(
        "This function is deprecated as of 0.3.0, as cruise-control already "
        "provides human-readable text when supplied the parameter json=false. "
        "Please ensure that your Cruise-Control-Version is 2.0.61 or higher, "
        "and use json=false for retrieving human-readable text. "
        "This function may be removed entirely in future versions.",
        DeprecationWarning,
        stacklevel=2)
    for key in j.keys():
        print_with_indent(f"'{key}':", level)
        # Display the contents of this key with its key-specific function, if possible.
        key_to_display_function = get_key_to_display_function()
        if key in key_to_display_function:
            key_to_display_function[key](j[key], level)
        # Otherwise, just pretty-print this key's contents.
        else:
            print_with_indent(pformat(j[key]), level)
        print_error()
예제 #2
0
    def retrieve_response(self) -> requests.Response:
        """
        Returns a final requests.Response object from cruise-control
        where Response.text is JSON-formatted.
        :return: requests.Response
        """
        # cruise-control's JSON response has a 'progress' key in it so long
        # as the response is not final.
        #
        # Once the response is final, it does not contain the 'progress' key.
        #
        # Accordingly, keep getting the response from this session and checking
        # it for the 'progress' key.
        #
        # Return the response as JSON once we get a valid JSON response that we
        # think is 'final'.

        # Alert the humans to long-running poll
        if self.print_to_stdout_enabled:
            print_error("Starting long-running poll of {}".format(self.url))

        # TODO: Session.get and Session.post can return a plethora of exceptions;
        # they should be handled here.
        response = self.session_http_method(self.url, headers=self.headers)
        while 'progress' in response.json().keys():
            if self.print_to_stdout_enabled:
                display_response(response)
            response = self.session_http_method(self.url, headers=self.headers)

        # return the requests.response object
        return response
예제 #3
0
def display_response(response: Response) -> None:
    """
    Handles setting the display options and extracting the dict from the response,
    then beginning the (possibly recursive) display parsing.

    :param response:
    :return:
    """
    # Set pandas display options
    # Display floats with a ',' 1000s separator, to two decimal places
    set_option('display.float_format', "{:,.2f}".format)
    # Display all rows and columns in the dataframe.  Don't leave any out
    set_option('display.max_rows', int(1E12))
    set_option('display.max_columns', int(1E12))
    # Display the full column width, even if it's very wide.  Don't truncate it.
    set_option('display.max_colwidth', 10000)
    # Don't wrap the table we show
    set_option('display.expand_frame_repr', False)
    # Don't 'sparsify' sorted multi-index dataframes, since
    # sparsifying makes it harder to do bash-type processing of tabular data
    set_option('display.multi_sparse', False)

    j: dict = response.json()

    try:
        display_dict(j)
    except Exception as e:
        print_error(f"Unhandled exception during display; showing raw JSON", e)
        print_error(pformat(j))
예제 #4
0
def display_anomaly_detector_state(AnomalyDetectorState_json: dict,
                                   level=0) -> None:
    """
    Displays the 'AnomalyDetectorState' key in a cruise-control response

    :param AnomalyDetectorState_json: the JSON structure referenced by the
            'AnomalyDetectorState' key.
    :return:
    """
    warnings.warn(
        "This function is deprecated as of 0.3.0, as cruise-control already "
        "provides human-readable text when supplied the parameter json=false. "
        "Please ensure that your Cruise-Control-Version is 2.0.61 or higher, "
        "and use json=false for retrieving human-readable text. "
        "This function may be removed entirely in future versions.",
        DeprecationWarning,
        stacklevel=2)
    for key in AnomalyDetectorState_json:
        if key == "recentGoalViolations":
            print_with_indent(f"\t'{key}:", level)
            for item in AnomalyDetectorState_json[key]:
                if 'optimizationResult' in item:
                    optimization_json = json.loads(item['optimizationResult'])
                    del item['optimizationResult']
                    print_with_indent(pformat(item), level + 1)
                    print_with_indent("'optimizationResult':", level + 2)
                    display_dict(optimization_json, level + 2)

                else:
                    print_with_indent(pformat(item), level + 1)
        else:
            print_error(f"\t'{key}: {AnomalyDetectorState_json[key]}")
예제 #5
0
def display_progress(progress_list: list, level=0) -> None:
    """
    Displays the 'progress' key in a (non-final) cruise-control response

    :param progress_list: the list structure referenced by the 'progress' key.
    :return:
    """
    warnings.warn(
        "This function is deprecated as of 0.3.0, as cruise-control already "
        "provides human-readable text when supplied the parameter json=false. "
        "Please ensure that your Cruise-Control-Version is 2.0.61 or higher, "
        "and use json=false for retrieving human-readable text. "
        "This function may be removed entirely in future versions.",
        DeprecationWarning,
        stacklevel=2)
    for elem in progress_list:
        print_error(f"operation: {elem['operation']}")
        df = DataFrame(elem['operationProgress'])
        try:
            df.set_index('step', inplace=True)
        # The dataframe we get back may not have 'step' as a key
        except KeyError:
            pass
        df_string = df.to_string()
        # Indent the dataframe for better display
        df_string = df_string.replace("\n", "\n    ")
        print_with_indent(df_string, level)

    # Print ellipses so the humans know we are waiting :)
    print_error(".........")
예제 #6
0
def print_with_indent(s: str, level=0) -> None:
    """
    Prints the string with a level of indentation

    :param s:
    :param level:
    :return:
    """
    print_error(textwrap.indent(s, "\t" * level))
예제 #7
0
def display_goal_summary(goalSummary_json: dict, level=0) -> None:
    """
    Displays the 'goalSummary' key in a cruise-control response

    :param goalSummary_json: the JSON structure referenced by the 'goalSummary' key.
    :return:
    """
    # Make and print a dataframe for each of the goals in clusterModelState
    for elem in goalSummary_json:
        print_with_indent(f"{elem['goal']}: {elem['status']}", level)
        df = DataFrame(elem['clusterModelStats']['statistics'])
        print_with_indent(df.to_string(), level)
        print_error()
예제 #8
0
def display_goal_readiness(goalReadiness_list: list, level=0) -> None:
    """
    Displays the 'goalReadiness' key in a cruise-control response.

    Note that this is not a top-level key, but is rather a sub-key of the
    AnalyzerState top-level key.

    :param goalReadiness_list: the list structure referenced by the
            'goalReadiness' key.
    :return:
    """
    for elem in goalReadiness_list:
        print_with_indent(f"{elem['name']}: {elem['status']}", level)
        print_with_indent(pformat(elem['modelCompleteRequirement']))
        print_error()
예제 #9
0
def print_with_indent(s: str, level=0) -> None:
    """
    Prints the string with a level of indentation

    :param s:
    :param level:
    :return:
    """
    warnings.warn(
        "This function is deprecated as of 0.3.0, as cruise-control already "
        "provides human-readable text when supplied the parameter json=false. "
        "Please ensure that your Cruise-Control-Version is 2.0.61 or higher, "
        "and use json=false for retrieving human-readable text. "
        "This function may be removed entirely in future versions.",
        DeprecationWarning,
        stacklevel=2)
    print_error(textwrap.indent(s, "\t" * level))
예제 #10
0
def display_dict(j: dict, level=0) -> None:
    """
    Walks the cruise-control Response dict and attempts to display
    the given top-level keys to the humans.


    :param j:
    :return:
    """
    for key in j.keys():
        print_with_indent(f"'{key}':", level)
        # Display the contents of this key with its key-specific function, if possible.
        key_to_display_function = get_key_to_display_function()
        if key in key_to_display_function:
            key_to_display_function[key](j[key], level)
        # Otherwise, just pretty-print this key's contents.
        else:
            print_with_indent(pformat(j[key]), level)
        print_error()
예제 #11
0
def display_goal_summary(goalSummary_json: dict, level=0) -> None:
    """
    Displays the 'goalSummary' key in a cruise-control response

    :param goalSummary_json: the JSON structure referenced by the 'goalSummary' key.
    :return:
    """
    warnings.warn(
        "This function is deprecated as of 0.3.0, as cruise-control already "
        "provides human-readable text when supplied the parameter json=false. "
        "Please ensure that your Cruise-Control-Version is 2.0.61 or higher, "
        "and use json=false for retrieving human-readable text. "
        "This function may be removed entirely in future versions.",
        DeprecationWarning,
        stacklevel=2)
    # Make and print a dataframe for each of the goals in clusterModelState
    for elem in goalSummary_json:
        print_with_indent(f"{elem['goal']}: {elem['status']}", level)
        df = DataFrame(elem['clusterModelStats']['statistics'])
        print_with_indent(df.to_string(), level)
        print_error()
예제 #12
0
def display_progress(progress_list: list, level=0) -> None:
    """
    Displays the 'progress' key in a (non-final) cruise-control response

    :param progress_list: the list structure referenced by the 'progress' key.
    :return:
    """
    for elem in progress_list:
        print_error(f"operation: {elem['operation']}")
        df = DataFrame(elem['operationProgress'])
        try:
            df.set_index('step', inplace=True)
        # The dataframe we get back may not have 'step' as a key
        except KeyError:
            pass
        df_string = df.to_string()
        # Indent the dataframe for better display
        df_string = df_string.replace("\n", "\n    ")
        print_with_indent(df_string, level)

    # Print ellipses so the humans know we are waiting :)
    print_error(".........")
예제 #13
0
def display_goal_readiness(goalReadiness_list: list, level=0) -> None:
    """
    Displays the 'goalReadiness' key in a cruise-control response.

    Note that this is not a top-level key, but is rather a sub-key of the
    AnalyzerState top-level key.

    :param goalReadiness_list: the list structure referenced by the
            'goalReadiness' key.
    :return:
    """
    warnings.warn(
        "This function is deprecated as of 0.3.0, as cruise-control already "
        "provides human-readable text when supplied the parameter json=false. "
        "Please ensure that your Cruise-Control-Version is 2.0.61 or higher, "
        "and use json=false for retrieving human-readable text. "
        "This function may be removed entirely in future versions.",
        DeprecationWarning,
        stacklevel=2)
    for elem in goalReadiness_list:
        print_with_indent(f"{elem['name']}: {elem['status']}", level)
        print_with_indent(pformat(elem['modelCompleteRequirement']))
        print_error()
예제 #14
0
def display_anomaly_detector_state(AnomalyDetectorState_json: dict,
                                   level=0) -> None:
    """
    Displays the 'AnomalyDetectorState' key in a cruise-control response

    :param AnomalyDetectorState_json: the JSON structure referenced by the
            'AnomalyDetectorState' key.
    :return:
    """
    for key in AnomalyDetectorState_json:
        if key == "recentGoalViolations":
            print_with_indent(f"\t'{key}:", level)
            for item in AnomalyDetectorState_json[key]:
                if 'optimizationResult' in item:
                    optimization_json = json.loads(item['optimizationResult'])
                    del item['optimizationResult']
                    print_with_indent(pformat(item), level + 1)
                    print_with_indent("'optimizationResult':", level + 2)
                    display_dict(optimization_json, level + 2)

                else:
                    print_with_indent(pformat(item), level + 1)
        else:
            print_error(f"\t'{key}: {AnomalyDetectorState_json[key]}")
예제 #15
0
def display_response(response: Response) -> None:
    """
    Handles setting the display options and extracting the dict from the response,
    then beginning the (possibly recursive) display parsing.

    :param response:
    :return:
    """
    warnings.warn(
        "This function is deprecated as of 0.3.0, as cruise-control already "
        "provides human-readable text when supplied the parameter json=false. "
        "Please ensure that your Cruise-Control-Version is 2.0.61 or higher, "
        "and use json=false for retrieving human-readable text. "
        "This function may be removed entirely in future versions.",
        DeprecationWarning,
        stacklevel=2)
    # Set pandas display options
    # Display floats with a ',' 1000s separator, to two decimal places
    set_option('display.float_format', "{:,.2f}".format)
    # Display all rows and columns in the dataframe. Don't leave any out
    set_option('display.max_rows', int(1E12))
    set_option('display.max_columns', int(1E12))
    # Display the full column width, even if it's very wide. Don't truncate it.
    set_option('display.max_colwidth', 10000)
    # Don't wrap the table we show
    set_option('display.expand_frame_repr', False)
    # Don't 'sparsify' sorted multi-index dataframes, since
    # sparsifying makes it harder to do bash-type processing of tabular data
    set_option('display.multi_sparse', False)

    # Handle non-JSON (presumably plain-text) responses
    try:
        j: dict = response.json()
    except json.decoder.JSONDecodeError:
        print_error(response.text)
        return

    # Handle JSON responses
    try:
        display_dict(j)
    except Exception as e:
        print_error(f"Unhandled exception during display; showing raw JSON", e)
        print_error(pformat(j))
예제 #16
0
    def retrieve_response(self, method, url, **kwargs) -> requests.Response:
        """
        Returns a final requests.Response object from cruise-control
        where Response.text is JSON-formatted.

        :return: requests.Response
        """
        # Alert the humans that long-running request is starting
        if 'params' in kwargs:
            url_with_params = f"{url}?{urlencode(kwargs['params'])}"
        else:
            url_with_params = url
        print_error(f"Starting long-running poll of {url_with_params}")
        for key, value in kwargs.items():
            if key == 'params':
                continue
            else:
                print_error(f"{key}: {value}")

        # Convenience closure to not have to copy-paste the parameters from
        # this current environment.
        def inner_request_helper():
            return self.request(method, url, **kwargs)

        def is_response_final(response: requests.Response):
            # Define an inner convenience closure to avoid
            # repeating ourselves
            def json_or_text_guesser():
                try:
                    # Try to guess whether the JSON response is final
                    return "progress" not in response.json().keys()

                except ValueError:
                    # We have a non-JSON (probably plain text) response,
                    # and a non-202 status code.
                    #
                    # This response may not be final, but we have no
                    # way of doing further guessing, so warn the humans
                    # as best as we can, then presume finality.
                    cc_version = response.headers.get('Cruise-Control-Version')
                    if cc_version is not None:
                        warnings.warn(
                            f"json=False received from cruise-control version ({cc_version}) "
                            f"that does not support 202 response codes. "
                            f"Please upgrade cruise-control to >=2.0.61, or "
                            f"use json=True with cruise-control-client. "
                            f"Returning a potentially non-final response.")
                    # No cc_version in the response headers
                    else:
                        # cruise-control won't return version information if
                        # servlet receives too-large-URI request
                        if response.status_code == 414:
                            pass
                        else:
                            warnings.warn(
                                "Unable to determine cruise-control version. "
                                "Returning a potentially non-final response.")
                    return True

            # We're talking to a version of cruise-control that supports
            # 202: accepted, and we know that this response is not final.
            if response.status_code == 202:
                return False
            else:
                # Guess about whether this version of cruise-control supports 202
                if "Cruise-Control-Version" in response.headers:
                    integer_semver = lambda x: [
                        int(elem) for elem in x.split('.')
                    ]
                    cc_version = integer_semver(
                        response.headers["Cruise-Control-Version"])
                    # 202 is supported and was not returned; response final
                    if cc_version >= [2, 0, 61]:
                        return True
                    # 202 is not supported and was not returned; guess further
                    else:
                        return json_or_text_guesser()
                # Probably we're getting a response (like a 414) before cruise-control
                # can decorate the headers with version information
                else:
                    return json_or_text_guesser()

        response = inner_request_helper()
        final_response = is_response_final(response)
        while not final_response:
            display_response(response)
            response = inner_request_helper()
            final_response = is_response_final(response)

        # return the requests.response object
        return response