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
8 # http://www.apache.org/licenses/LICENSE-2.0
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
23 @for_all_methods(log_enter_exit)
24 class ODLReInstaller(Service):
27 self.netvirt_url = "restconf/operational/network-topology:" \
28 "network-topology/topology/netvirt:1"
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()
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])
49 LOG.info("ODL node found: {}".format(self.odl_node.name))
50 node.execute('systemctl stop opendaylight', as_root=True)
52 self.disconnect_ovs(node)
55 self.reinstall_odl(self.odl_node, odl_artifact)
57 # Wait for ODL to come back up
58 full_netvirt_url = "http://{}:8081/{}".format(
59 self.odl_node.config['address'], self.netvirt_url)
63 self.odl_node.execute("curl --fail -u admin:admin {}".format(
65 LOG.info("New OpenDaylight NetVirt is Up")
67 except processutils.ProcessExecutionError:
68 LOG.warning("NetVirt not up. Attempt: {}".format(counter))
70 LOG.warning("NetVirt not detected as up after 10 "
71 "attempts...deployment may be unstable!")
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
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!")
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 '
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
108 # 0: The run succeeded with no changes or failures; the system was
109 # already in the desired state.
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.
116 def disconnect_ovs(node):
117 LOG.info('Disconnecting OpenVSwitch from controller on node %s'
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
124 for br in 'br-int', 'br-ex':
125 LOG.info("Checking for ports on {}".format(br))
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")
132 LOG.info('Deleting port: {}'.format(port))
133 node.execute('ovs-vsctl del-port {} {}'.format(br, port),
135 except ProcessExecutionError:
136 LOG.info("No tunnel or patch ports configured")
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,
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)
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")
156 LOG.info("OVS is connected to OVSDB manager")
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 "
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 "
171 "".format(node.address,
174 LOG.info("OVS Controller set correctly")
175 # Validate ovs controller is connected
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")
182 LOG.info("OVS is connected to OpenFlow controller")
184 def create_cli_parser(self, parser):
185 parser.add_argument('--pod-config',
186 help="File containing pod configuration",
189 parser.add_argument('--odl-artifact',
190 help="Path to Opendaylight tarball to use for "
197 class ODLReinstallerException(Exception):
199 def __init__(self, value):
207 ODLReInstaller().start()
209 if __name__ == '__main__':