Fix DNS use for testcase 3
[sdnvpn.git] / odl-pipeline / lib / odl_reinstaller / odl_reinstaller.py
1 #!/bin/python
2 #
3 # Copyright (c) 2017 All rights reserved
4 # This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #
11 import re
12 import time
13
14 from utils.processutils import ProcessExecutionError
15 from tripleo_introspector.tripleo_introspector import TripleOIntrospector
16 from utils import processutils
17 from utils.utils_log import LOG, for_all_methods, log_enter_exit
18 from utils.service import Service
19 from utils.node_manager import NodeManager
20 from utils import utils_yaml
21
22
23 @for_all_methods(log_enter_exit)
24 class ODLReInstaller(Service):
25
26     def __init__(self):
27         self.netvirt_url = "restconf/operational/network-topology:" \
28                            "network-topology/topology/netvirt:1"
29         self.nodes = None
30         self.odl_node = None
31
32     def run(self, sys_args, config):
33         pod_config = sys_args.pod_config
34         odl_artifact = sys_args.odl_artifact
35         node_config = utils_yaml.read_dict_from_yaml(pod_config)
36         # TODO Add validation of incoming node config
37         # self.check_node_config()
38
39         # copy ODL to all nodes where it need to be copied
40         self.nodes = NodeManager(node_config['servers']).get_nodes()
41         for node in self.nodes:
42             node.execute('ovs-vsctl del-controller br-int', as_root=True)
43         for node in self.nodes:
44             # Check if ODL runs on this node
45             rv, _ = node.execute('ps aux |grep -v grep |grep karaf',
46                                  as_root=True, check_exit_code=[0, 1])
47             if 'java' in rv:
48                 self.odl_node = node
49                 LOG.info("ODL node found: {}".format(self.odl_node.name))
50                 node.execute('systemctl stop opendaylight', as_root=True)
51
52             self.disconnect_ovs(node)
53
54         # Upgrade ODL
55         self.reinstall_odl(self.odl_node, odl_artifact)
56
57         # Wait for ODL to come back up
58         full_netvirt_url = "http://{}:8081/{}".format(
59             self.odl_node.config['address'], self.netvirt_url)
60         counter = 1
61         while counter <= 10:
62             try:
63                 self.odl_node.execute("curl --fail -u admin:admin {}".format(
64                     full_netvirt_url))
65                 LOG.info("New OpenDaylight NetVirt is Up")
66                 break
67             except processutils.ProcessExecutionError:
68                 LOG.warning("NetVirt not up. Attempt: {}".format(counter))
69                 if counter >= 10:
70                     LOG.warning("NetVirt not detected as up after 10 "
71                                 "attempts...deployment may be unstable!")
72             counter += 1
73             time.sleep(10)
74
75         # Reconnect OVS instances
76         LOG.info("Reconnecting OVS instances")
77         for node in self.nodes:
78             self.connect_ovs(node)
79         # Sleep for a few seconds to allow TCP connections to come up
80         time.sleep(5)
81         # Validate OVS instances
82         LOG.info("Validating OVS configuration")
83         for node in self.nodes:
84             self.validate_ovs(node)
85         LOG.info("OpenDaylight Upgrade Successful!")
86
87     @staticmethod
88     def reinstall_odl(node, odl_tarball):
89         tar_tmp_path = '/tmp/odl-artifact/'
90         node.copy('to', odl_tarball, tar_tmp_path + odl_tarball)
91         node.execute('rm -rf /opt/opendaylight/*', as_root=True)
92         node.execute('mkdir -p /opt/opendaylight/*', as_root=True)
93         LOG.info('Extracting %s to /opt/opendaylight/ on node %s'
94                  % (odl_tarball, node.name))
95         node.execute('tar -zxf %s --strip-components=1 -C '
96                      '/opt/opendaylight/'
97                      % (tar_tmp_path + odl_tarball), as_root=True)
98         node.execute('chown -R odl:odl /opt/opendaylight', as_root=True)
99         node.execute('rm -rf ' + tar_tmp_path, as_root=True)
100         LOG.info('Installing and Starting Opendaylight on node %s' % node.name)
101         node.execute('puppet apply -e "include opendaylight" '
102                      '--modulepath=/etc/puppet/modules/ '
103                      '--verbose --debug --trace --detailed-exitcodes',
104                      check_exit_code=[2], as_root=True)
105         # --detailed-exitcodes: Provide extra information about the run via
106         # exit codes. If enabled, 'puppet apply' will use the following exit
107         # codes:
108         # 0: The run succeeded with no changes or failures; the system was
109         #    already in the desired state.
110         # 1: The run failed.
111         # 2: The run succeeded, and some resources were changed.
112         # 4: The run succeeded, and some resources failed.
113         # 6: The run succeeded, and included both changes and failures.
114
115     @staticmethod
116     def disconnect_ovs(node):
117         LOG.info('Disconnecting OpenVSwitch from controller on node %s'
118                  % node.name)
119         node.execute('ovs-vsctl del-controller br-int', as_root=True)
120         node.execute('ovs-vsctl del-manager', as_root=True)
121         LOG.info('Deleting Tunnel and Patch interfaces')
122         # Note this is required because ODL fails to reconcile pre-created
123         # ports
124         for br in 'br-int', 'br-ex':
125             LOG.info("Checking for ports on {}".format(br))
126             try:
127                 out, _ = node.execute('ovs-vsctl list-ports {} | grep -E '
128                                       '"tun|patch"'.format(br),
129                                       as_root=True, shell=True)
130                 ports = out.rstrip().split("\n")
131                 for port in ports:
132                     LOG.info('Deleting port: {}'.format(port))
133                     node.execute('ovs-vsctl del-port {} {}'.format(br, port),
134                                  as_root=True)
135             except ProcessExecutionError:
136                 LOG.info("No tunnel or patch ports configured")
137
138     @staticmethod
139     def connect_ovs(node):
140         LOG.info('Connecting OpenVSwitch to controller on node %s' % node.name)
141         ovs_manager_str = ' '.join(node.config['ovs-managers'])
142         node.execute('ovs-vsctl set-manager %s' % ovs_manager_str,
143                      as_root=True)
144
145     @staticmethod
146     def validate_ovs(node):
147         LOG.info("Validating OVS configuration for node: {}".format(node.name))
148         # Validate ovs manager is connected
149         out, _ = node.execute('ovs-vsctl show ', as_root=True)
150         mgr_search = \
151             re.search('Manager\s+\"tcp:[0-9.]+:6640\"\n\s*'
152                       'is_connected:\s*true', out)
153         if mgr_search is None:
154             raise ODLReinstallerException("OVS Manager is not connected")
155         else:
156             LOG.info("OVS is connected to OVSDB manager")
157
158         # Validate ovs controller is configured
159         cfg_controller = node.config['ovs-controller']
160         ovs_controller = TripleOIntrospector().get_ovs_controller(node)
161         if cfg_controller == '' or cfg_controller is None:
162             if ovs_controller is None or ovs_controller == '':
163                 raise ODLReinstallerException("OVS controller is not set "
164                                               "for node: {}"
165                                               "".format(node.address))
166         elif ovs_controller != cfg_controller:
167             raise ODLReinstallerException("OVS controller is not set to the "
168                                           "correct pod config value on {}. "
169                                           "Config controller: {}, current "
170                                           "controller: {}"
171                                           "".format(node.address,
172                                                     cfg_controller,
173                                                     ovs_controller))
174         LOG.info("OVS Controller set correctly")
175         # Validate ovs controller is connected
176         ctrl_search = \
177             re.search('Controller\s+\"tcp:[0-9\.]+:6653\"\n\s*'
178                       'is_connected:\s*true', out)
179         if ctrl_search is None:
180             raise ODLReinstallerException("OVS Controller is not connected")
181         else:
182             LOG.info("OVS is connected to OpenFlow controller")
183
184     def create_cli_parser(self, parser):
185         parser.add_argument('--pod-config',
186                             help="File containing pod configuration",
187                             dest='pod_config',
188                             required=True)
189         parser.add_argument('--odl-artifact',
190                             help="Path to Opendaylight tarball to use for "
191                                  "upgrade",
192                             dest='odl_artifact',
193                             required=True)
194         return parser
195
196
197 class ODLReinstallerException(Exception):
198
199     def __init__(self, value):
200         self.value = value
201
202     def __str__(self):
203         return self.value
204
205
206 def main():
207     ODLReInstaller().start()
208
209 if __name__ == '__main__':
210     main()