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