FIX: integer divisions
[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 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_prereq.sh {2} 2>&1 | tee '
104         'log_install_prereq.txt'
105         .format(con.REMOTE_FW_DIR, con.DMM_SCRIPTS, arch), timeout=600)
106
107     if ret_code != 0:
108         logger.error('Install DMM error: {0}'.format(stderr))
109         raise RuntimeError('Install prereq failed')
110     else:
111         logger.console('Install DMM on {0} success!'.format(node['host']))
112
113 def setup_node(args):
114     """Run all set-up methods for a node.
115
116     This method is used as map_async parameter. It receives tuple with all
117     parameters as passed to map_async function.
118
119     :param args: All parameters needed to setup one node.
120     :type args: tuple
121     :returns: True - success, False - error
122     :rtype: bool
123     :raises RuntimeError: If node setup failed.
124     """
125     tarball, remote_tarball, node = args
126
127     # if unset, arch defaults to x86_64
128     Topology.get_node_arch(node)
129
130     try:
131         if node['type'] == NodeType.DUT:
132             copy_tarball_to_node(tarball, node)
133             extract_tarball_at_node(remote_tarball, node)
134             install_dmm_test(node)
135     except RuntimeError as exc:
136         logger.error("Node setup failed, error:'{0}'".format(exc.message))
137         return False
138     else:
139         logger.console('Setup of node {0} done'.format(node['host']))
140         return True
141
142 class SetupDMMTest:
143     """Setup suite run on topology nodes.
144
145     Many VAT/CLI based tests need the scripts at remote hosts before executing
146     them. This class packs the whole testing directory and copies it over
147     to all nodes in topology under /tmp/
148     """
149
150     @staticmethod
151     def setup_dmm_test(nodes):
152         """Pack the whole directory and extract in temp on each node."""
153
154         tarball = pack_framework_dir()
155         msg = 'Framework packed to {0}'.format(tarball)
156         logger.console(msg)
157         logger.trace(msg)
158         remote_tarball = "/tmp/{0}".format(basename(tarball))
159
160         # Turn off logging since we use multiprocessing.
161         log_level = BuiltIn().set_log_level('NONE')
162         params = ((tarball, remote_tarball, node) for node in nodes.values())
163         pool = Pool(processes=len(nodes))
164         result = pool.map_async(setup_node, params)
165         pool.close()
166         pool.join()
167
168         # Turn on logging.
169         BuiltIn().set_log_level(log_level)
170
171         logger.info(
172             'Executed node setups in parallel, waiting for processes to end')
173         result.wait()
174
175         results = result.get()
176         node_setup_success = all(results)
177         logger.info('Results: {0}'.format(results))
178
179         logger.trace('Test framework copied to all topology nodes')
180         delete_local_tarball(tarball)
181         if node_setup_success:
182             logger.console('All nodes are ready')
183         else:
184             logger.console('Failed to setup dpdk on all the nodes')