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