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