Hoststack perf infrastructure refactoring
[csit.git] / resources / libraries / python / Namespaces.py
1 # Copyright (c) 2020 Cisco and/or its affiliates.
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 """Linux namespace utilities library."""
15
16 from resources.libraries.python.ssh import exec_cmd_no_error, exec_cmd
17
18
19 class Namespaces:
20     """Linux namespace utilities."""
21     __namespaces = []
22
23     @staticmethod
24     def create_namespace(node, namespace, delete_before_create=True):
25         """Create namespace and add the name to the list for later clean-up.
26
27         :param node: Where to create namespace.
28         :param namespace: Name for namespace.
29         :param delete_before_create: Delete namespace prior to create
30         :type node: dict
31         :type namespace: str
32         :type delete_before_create: bool
33         """
34         if delete_before_create:
35             Namespaces.delete_namespace(node, namespace)
36
37         cmd = f"ip netns add {namespace}"
38         exec_cmd_no_error(node, cmd, sudo=True)
39         Namespaces.__namespaces.append(namespace)
40
41     @staticmethod
42     def delete_namespace(node, namespace):
43         """Delete namespace from the node and list.
44
45         :param node: Where to delete namespace.
46         :param namespace: Name for namespace.
47         :param delete_before_create: Delete namespace prior to create
48         :type node: dict
49         :type namespace: str
50         :type delete_before_create: bool
51         """
52         cmd_timeout = 5
53         cmd = f"ip netns delete {namespace}"
54         (ret_code, _, delete_errmsg) = \
55             exec_cmd(node, cmd, timeout=cmd_timeout, sudo=True)
56         if ret_code != 0:
57             cmd = f"ip netns list {namespace}"
58             (stdout, _) = \
59                 exec_cmd_no_error(node, cmd, timeout=cmd_timeout, sudo=True)
60             if stdout == namespace:
61                 raise RuntimeError(f"Could not delete namespace "
62                                    f"({namespace}): {delete_errmsg}")
63         try:
64             Namespaces.__namespaces.remove(namespace)
65         except ValueError:
66             pass
67
68     @staticmethod
69     def attach_interface_to_namespace(node, namespace, interface):
70         """Attach specific interface to namespace.
71
72         :param node: Node where to execute command.
73         :param namespace: Namespace to execute command on.
74         :param interface: Interface in namespace.
75         :type node: dict
76         :type namespace: str
77         :type interface: str
78         :raises RuntimeError: Interface could not be attached.
79         """
80         cmd = f"ip link set {interface} netns {namespace}"
81
82         ret_code, _, stderr = exec_cmd(node, cmd, timeout=5, sudo=True)
83         if ret_code != 0:
84             raise RuntimeError(f"Could not attach interface, reason:\n{stderr}")
85
86         cmd = f"ip netns exec {namespace} ip link set {interface} up"
87
88         ret_code, _, stderr = exec_cmd(node, cmd, timeout=5, sudo=True)
89         if ret_code != 0:
90             raise RuntimeError(
91                 f"Could not set interface state, reason:\n{stderr}"
92             )
93
94     @staticmethod
95     def add_default_route_to_namespace(node, namespace, default_route):
96         """Add IPv4 default route to interface in namespace.
97
98         :param node: Node where to execute command.
99         :param namespace: Namespace to execute command on.
100         :param default_route: Default route address.
101         :type node: dict
102         :type namespace: str
103         :type default_route: str
104         """
105         cmd = f"ip netns exec {namespace} ip route add default " \
106               f"via {default_route}"
107         exec_cmd_no_error(node, cmd, sudo=True)
108
109     @staticmethod
110     def create_bridge_for_int_in_namespace(
111             node, namespace, bridge_name, *interfaces):
112         """Setup bridge domain and add interfaces to it.
113
114         :param node: Node where to execute command.
115         :param namespace: Namespace to execute command on.
116         :param bridge_name: Name of the bridge to be created.
117         :param interfaces: List of interfaces to add to the namespace.
118         :type node: dict
119         :type namespace: str
120         :type bridge_name: str
121         :type interfaces: list
122         """
123         cmd = f"ip netns exec {namespace} brctl addbr {bridge_name}"
124         exec_cmd_no_error(node, cmd, sudo=True)
125
126         for interface in interfaces:
127             cmd = f"ip netns exec {namespace} brctl addif {bridge_name} " \
128                 f"{interface}"
129             exec_cmd_no_error(node, cmd, sudo=True)
130
131         cmd = f"ip netns exec {namespace} ip link set dev {bridge_name} up"
132         exec_cmd_no_error(node, cmd, sudo=True)
133
134     @staticmethod
135     def clean_up_namespaces(node, namespace=None):
136         """Delete all old namespaces.
137
138         :param node: Node where to execute command.
139         :param namespace: Namespace to delete, if None delete all namespaces
140         :type node: dict
141         :type namespace: str
142         :raises RuntimeError: Namespaces could not be cleaned properly.
143         """
144         if namespace is not None:
145             Namespaces.delete_namespace(node, namespace)
146             return
147
148         for namespace_name in Namespaces.__namespaces:
149             Namespaces.delete_namespace(node, namespace_name)