initial CSIT-DMM test scripts
[csit.git] / resources / libraries / python / DMM / SetupDMMTest.py
1 # Copyright (c) 2018 Huawei Technologies Co.,Ltd.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """This module exists to provide setup utilities for the framework on topology
15 nodes. All tasks required to be run before the actual tests are started is
16 supposed to end up here.
17 """
18
19 from shlex import split
20 from subprocess import Popen, PIPE
21 from multiprocessing import Pool
22 from tempfile import NamedTemporaryFile
23 from os.path import basename
24
25 from robot.api import logger
26 from robot.libraries.BuiltIn import BuiltIn
27
28 from resources.libraries.python.ssh import SSH
29 from resources.libraries.python.DMM.DMMConstants import DMMConstants as con
30 from resources.libraries.python.topology import NodeType, Topology
31 from resources.libraries.python.TLDK.SetupTLDKTest import copy_tarball_to_node,\
32      delete_local_tarball
33
34 __all__ = ["SetupDMMTest"]
35
36
37 def pack_framework_dir():
38     """Pack the testing WS into temp file, return its name.
39     :returns: file_name
40     :rtype: str
41     :raises RuntimeError: If pack the testing framework failed.
42     """
43     tmpfile = NamedTemporaryFile(suffix=".tgz", prefix="DMM-testing-")
44     file_name = tmpfile.name
45     tmpfile.close()
46
47     proc = Popen(
48         split("tar --exclude-vcs --exclude=./tmp --exclude=dmm_depends.tar.gz"
49               " -zcf {0} .".format(file_name)), stdout=PIPE, stderr=PIPE)
50     (stdout, stderr) = proc.communicate()
51
52     logger.debug(stdout)
53     logger.debug(stderr)
54
55     return_code = proc.wait()
56     if return_code != 0:
57         raise RuntimeError("Could not pack testing framework.")
58
59     return file_name
60
61 def extract_tarball_at_node(tarball, node):
62     """Extract tarball at given node.
63
64     Extracts tarball using tar on given node to specific CSIT location.
65     Raise runtime errors when failed.
66
67     :param tarball: Path to tarball to upload.
68     :param node: Dictionary created from topology.
69     :type tarball: str
70     :type node: dict
71     :return: nothing
72     :raises RuntimeError: If extract tarball failed.
73     """
74     logger.console('Extracting tarball to {0} on {1}'.format(
75         con.REMOTE_FW_DIR, node['host']))
76     ssh = SSH()
77     ssh.connect(node)
78
79     cmd = 'sudo rm -rf {1}; mkdir {1} ; tar -zxf {0} -C {1}; ' \
80           'rm -f {0};'.format(tarball, con.REMOTE_FW_DIR)
81     (ret_code, _, stderr) = ssh.exec_command(cmd, timeout=30)
82     if ret_code != 0:
83         logger.error('Unpack error: {0}'.format(stderr))
84         raise RuntimeError('Failed to unpack {0} at node {1}'.format(
85             tarball, node['host']))
86
87 def install_dmm_test(node):
88     """Prepare the DMM test envrionment.
89     Raise errors when failed.
90
91     :param node: Dictionary created from topology.
92     :type node: dict
93     :returns: nothing.
94     :raises RuntimeError: If install dmm failed.
95     """
96
97     arch = Topology.get_node_arch(node)
98     logger.console('Install the DMM on {0} ({1})'.format(node['host'], arch))
99
100     ssh = SSH()
101     ssh.connect(node)
102     (ret_code, _, stderr) = ssh.exec_command(
103         'cd {0}/{1} && ./install_dmm.sh {2} 2>&1 | tee log_install_dmm.txt'
104         .format(con.REMOTE_FW_DIR, con.DMM_SCRIPTS, arch), timeout=600)
105
106     if ret_code != 0:
107         logger.error('Install the DMM error: {0}'.format(stderr))
108         raise RuntimeError('Install the DMM failed')
109     else:
110         logger.console('Install the DMM on {0} success!'.format(node['host']))
111
112 def setup_node(args):
113     """Run all set-up methods for a node.
114
115     This method is used as map_async parameter. It receives tuple with all
116     parameters as passed to map_async function.
117
118     :param args: All parameters needed to setup one node.
119     :type args: tuple
120     :returns: True - success, False - error
121     :rtype: bool
122     :raises RuntimeError: If node setup failed.
123     """
124     tarball, remote_tarball, node = args
125
126     # if unset, arch defaults to x86_64
127     Topology.get_node_arch(node)
128
129     try:
130         if node['type'] == NodeType.DUT:
131             copy_tarball_to_node(tarball, node)
132             extract_tarball_at_node(remote_tarball, node)
133             install_dmm_test(node)
134     except RuntimeError as exc:
135         logger.error("Node setup failed, error:'{0}'".format(exc.message))
136         return False
137     else:
138         logger.console('Setup of node {0} done'.format(node['host']))
139         return True
140
141 class SetupDMMTest(object):
142     """Setup suite run on topology nodes.
143
144     Many VAT/CLI based tests need the scripts at remote hosts before executing
145     them. This class packs the whole testing directory and copies it over
146     to all nodes in topology under /tmp/
147     """
148
149     @staticmethod
150     def setup_dmm_test(nodes):
151         """Pack the whole directory and extract in temp on each node."""
152
153         tarball = pack_framework_dir()
154         msg = 'Framework packed to {0}'.format(tarball)
155         logger.console(msg)
156         logger.trace(msg)
157         remote_tarball = "/tmp/{0}".format(basename(tarball))
158
159         # Turn off logging since we use multiprocessing.
160         log_level = BuiltIn().set_log_level('NONE')
161         params = ((tarball, remote_tarball, node) for node in nodes.values())
162         pool = Pool(processes=len(nodes))
163         result = pool.map_async(setup_node, params)
164         pool.close()
165         pool.join()
166
167         # Turn on logging.
168         BuiltIn().set_log_level(log_level)
169
170         logger.info(
171             'Executed node setups in parallel, waiting for processes to end')
172         result.wait()
173
174         results = result.get()
175         node_setup_success = all(results)
176         logger.info('Results: {0}'.format(results))
177
178         logger.trace('Test framework copied to all topology nodes')
179         delete_local_tarball(tarball)
180         if node_setup_success:
181             logger.console('All nodes are ready')
182         else:
183             logger.console('Failed to setup dpdk on all the nodes')