1#!/usr/bin/env python3
2
3import os
4import sys
5
6from robot.libraries.BuiltIn import BuiltIn as robotBuildIn
7
8sys.path.append(__file__.split(__file__.split("/")[-1])[0] + "../ffdc")
9from ffdc_collector import ffdc_collector  # NOQA
10from ssh_utility import SSHRemoteclient  # NOQA
11
12# (Sub) String constants used for input dictionary key search
13HOST = "HOST"
14USER = "USERNAME"
15PASSWD = "PASSWORD"
16PORT_SSH = "SSH_PORT"
17PORT_HTTPS = "HTTPS_PORT"
18PORT_IPMI = "IPMI_PORT"
19CONFIG = "CONFIG"
20TYPE = "TYPE"
21LOC = "LOCATION"
22PROTOCOL = "PROTOCOL"
23ENV_VARS = "ENV_VARS"
24ECONFIG = "ECONFIG"
25LOGLEVEL = "LOG"
26
27
28def ffdc_robot_script_cli(**kwargs):
29    r"""
30
31    For the specified host, this method provide automation testcases the interface to
32    the new ffdc collector ../ffdc/ffdc_collector.py via robot variable FFDC_DEFAULT
33
34    variable FFDC_DEFAULT:1, by default use the existing ffdc collection method.
35    variable FFDC_DEFAULT:0 use the new ffdc method
36
37    Command examples:
38    (1) Legacy ffdc collection
39    python3 -m robot -v OPENBMC_HOST:<> -v OPENBMC_USERNAME:<> \
40                                        -v OPENBMC_PASSWORD:<> ./tools/myffdc.robot
41    (2) New ffdc collection
42    python3 -m robot -v OPENBMC_HOST:<> -v OPENBMC_USERNAME:<> \
43                        -v OPENBMC_PASSWORD:<> -v FFDC_DEFAULT:0  ./tools/myffdc.robot
44
45    Description of argument(s)in dictionary: xx can be anything appropriate
46
47        xx_HOST:hostname                name/ip of the targeted (remote) system
48        xx_USERNAME:username            user on the targeted system with access to FFDC files
49        xx_PASSWORD:password            password for user on targeted system
50        xx_CONFIG:ffdc_config           configuration file listing commands and files for FFDC
51        xx_LOCATION:location            where to store collected FFDC.  Default: <current dir>/logs/
52        xx_TYPE:remote_type             os type of the remote host.
53        xx_PROTOCOL:remote_protocol     Protocol to use to collect data. Default: 'ALL'
54        ENV_VAR:env_vars                User define CLI env vars '{"key : "value"}'. Default: ""
55        ECONFIG:econfig                 User define env vars YAML file. Default: ""
56        LOG_LEVEL:log_level             CRITICAL, ERROR, WARNING, INFO, DEBUG. Default: INFO
57
58    Code examples:
59    (1) openbmc_ffdc.robot activate this method with no parm
60        Run Keyword If  ${FFDC_DEFAULT} == ${1}  FFDC
61    ...    ELSE  ffdc_robot_script_cli
62
63    (2) Method invocation with parms
64        ffdc_from = {'OS_HOST' : 'os host name or ip',
65                     'OS_USERNAME' : 'os username',
66                     'OS_PASSWORD' : 'password for os_username',
67                     'OS_TYPE'     : 'os_type, ubuntu, rhel, aix, etc',
68                    }
69        ffdc_robot_script_cli(ffdc_from)
70
71    """
72
73    robotBuildIn().log_to_console("Collecting FFDC - CLI log collector script")
74
75    if not kwargs:
76        dict_of_parms = {}
77        # When method is invoked with no parm,
78        # use robot variables
79        # OPENBMC_HOST, OPENBMC_USERNAME, OPENBMC_PASSWORD, OPENBMC (type)
80        dict_of_parms["OPENBMC_HOST"] = robotBuildIn().get_variable_value(
81            "${OPENBMC_HOST}", default=None
82        )
83        dict_of_parms["OPENBMC_USERNAME"] = robotBuildIn().get_variable_value(
84            "${OPENBMC_USERNAME}", default=None
85        )
86        dict_of_parms["OPENBMC_PASSWORD"] = robotBuildIn().get_variable_value(
87            "${OPENBMC_PASSWORD}", default=None
88        )
89        dict_of_parms["SSH_PORT"] = robotBuildIn().get_variable_value(
90            "${SSH_PORT}", default=22
91        )
92        dict_of_parms["HTTPS_PORT"] = robotBuildIn().get_variable_value(
93            "${HTTPS_PORT}", default=443
94        )
95        dict_of_parms["IPMI_PORT"] = robotBuildIn().get_variable_value(
96            "${IPMI_PORT}", default=623
97        )
98        dict_of_parms["REMOTE_TYPE"] = "OPENBMC"
99
100        run_ffdc_collector(dict_of_parms)
101
102    else:
103        if isinstance(kwargs, dict):
104            # When method is invoked with user defined dictionary,
105            # dictionary keys has the following format
106            # xx_HOST; xx_USERNAME, xx_PASSWORD, xx_TYPE
107            # where xx is one of OPENBMC, OS, or os_type LINUX/UBUNTU/AIX
108            run_ffdc_collector(**kwargs)
109
110
111def run_ffdc_collector(dict_of_parm):
112    r"""
113
114    Process input parameters and collect information
115
116    Description of argument(s)in dictionary: xx can be anything appropriate
117
118        xx_HOST:hostname                name/ip of the targeted (remote) system
119        xx_USERNAME:username            user on the targeted system with access to FFDC files
120        xx_PASSWORD:password            password for user on targeted system
121        xx_CONFIG:ffdc_config           configuration file listing commands and files for FFDC
122        xx_LOCATION:location            where to store collected FFDC.  Default: <current dir>/logs/
123        xx_TYPE:remote_type             os type of the remote host.
124        xx_PROTOCOL:remote_protocol     Protocol to use to collect data. Default: 'ALL'
125        ENV_VAR:env_vars                User define CLI env vars '{"key : "value"}'. Default: ""
126        ECONFIG:econfig                 User define env vars YAML file. Default: ""
127        LOG_LEVEL:log_level             CRITICAL, ERROR, WARNING, INFO, DEBUG. Default: INFO
128
129    """
130
131    # Clear local variables
132    remote = None
133    username = None
134    password = None
135    config = None
136    location = None
137    remote_type = None
138    protocol = None
139    env_vars = None
140    econfig = None
141    log_level = None
142
143    # Process input key/value pairs
144    for key in dict_of_parm.keys():
145        if HOST in key:
146            remote = dict_of_parm[key]
147        elif USER in key:
148            username = dict_of_parm[key]
149        elif PASSWD in key:
150            password = dict_of_parm[key]
151        elif PORT_SSH in key:
152            port_ssh = dict_of_parm[key]
153        elif PORT_HTTPS in key:
154            port_https = dict_of_parm[key]
155        elif PORT_IPMI in key:
156            port_ipmi = dict_of_parm[key]
157        elif CONFIG in key:
158            config = dict_of_parm[key]
159        elif LOC in key:
160            location = dict_of_parm[key]
161        elif TYPE in key:
162            remote_type = dict_of_parm[key]
163        elif PROTOCOL in key:
164            protocol = dict_of_parm[key]
165        elif ENV_VARS in key:
166            env_vars = dict_of_parm[key]
167        elif ECONFIG in key:
168            econfig = dict_of_parm[key]
169        elif LOGLEVEL in key:
170            log_level = dict_of_parm[key]
171
172    # Set defaults values for parms
173    # that are not specified with input and have acceptable defaults.
174    if not location:
175        # Default FFDC store location
176        location = (
177            robotBuildIn().get_variable_value("${EXECDIR}", default=None)
178            + "/logs"
179        )
180        ffdc_collector.validate_local_store(location)
181
182    if not config:
183        # Default FFDC configuration
184        script_path = os.path.dirname(os.path.abspath(__file__))
185        config = script_path + "/../ffdc/ffdc_config.yaml"
186
187    if not protocol:
188        protocol = "ALL"
189
190    if not env_vars:
191        env_vars = ""
192
193    if not econfig:
194        econfig = ""
195
196    if not log_level:
197        log_level = "INFO"
198
199    # If minimum required inputs are met, go collect.
200    if remote and username and password and remote_type:
201        # Execute data collection
202        this_ffdc = ffdc_collector(
203            remote,
204            username,
205            password,
206            port_ssh,
207            port_https,
208            port_ipmi,
209            config,
210            location,
211            remote_type,
212            protocol,
213            env_vars,
214            econfig,
215            log_level,
216        )
217        this_ffdc.collect_ffdc()
218
219        # If original ffdc request is for BMC,
220        #  attempt to also collect ffdc for HOST_OS if possible.
221        if remote_type.upper() == "OPENBMC":
222            os_host = robotBuildIn().get_variable_value(
223                "${OS_HOST}", default=None
224            )
225            os_username = robotBuildIn().get_variable_value(
226                "${OS_USERNAME}", default=None
227            )
228            os_password = robotBuildIn().get_variable_value(
229                "${OS_PASSWORD}", default=None
230            )
231
232            if os_host and os_username and os_password:
233                os_type = get_os_type(os_host, os_username, os_password)
234                if os_type:
235                    os_ffdc = ffdc_collector(
236                        os_host,
237                        os_username,
238                        os_password,
239                        config,
240                        location,
241                        os_type,
242                        protocol,
243                        env_vars,
244                        econfig,
245                        log_level,
246                    )
247                    os_ffdc.collect_ffdc()
248
249
250def get_os_type(os_host, os_username, os_password):
251    os_type = None
252
253    # If HOST_OS is pingable
254    if os.system("ping -c 1 " + os_host) == 0:
255        r"""
256        Open a ssh connection to targeted system.
257        """
258        ssh_remoteclient = SSHRemoteclient(os_host, os_username, os_password)
259
260        if ssh_remoteclient.ssh_remoteclient_login():
261            # Find OS_TYPE
262            cmd_exit_code, err, response = ssh_remoteclient.execute_command(
263                "uname"
264            )
265            os_type = response.strip()
266
267            # If HOST_OS is linux, expands os_type to one of
268            # the 2 linux distros that have more details in ffdc_config.yaml
269            if os_type.upper() == "LINUX":
270                (
271                    cmd_exit_code,
272                    err,
273                    response,
274                ) = ssh_remoteclient.execute_command("cat /etc/os-release")
275                linux_distro = response
276                if "redhat" in linux_distro:
277                    os_type = "RHEL"
278                elif "ubuntu" in linux_distro:
279                    os_type = "UBUNTU"
280
281        if ssh_remoteclient:
282            ssh_remoteclient.ssh_remoteclient_disconnect()
283
284    return os_type
285