1 ##############################################################################
2 # Copyright (c) 2018 Tim Rozet (trozet@redhat.com) and others.
4 # All rights reserved. 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 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
18 import apex.common.constants as con
19 from apex.common import exceptions as exc
20 from apex.common import utils
21 from apex.overcloud.node import OvercloudNode
22 import apex.settings.deploy_settings as ds
25 SNAP_FILE = 'snapshot.properties'
26 CHECKSUM = 'OPNFV_SNAP_SHA512SUM'
27 OVERCLOUD_RC = 'overcloudrc'
29 OPENSTACK = 'openstack'
30 OPENDAYLIGHT = 'opendaylight'
31 SERVICES = (OPENSTACK, OPENDAYLIGHT)
34 class SnapshotDeployment:
35 def __init__(self, deploy_settings, snap_cache_dir, fetch=True,
39 ds_opts = deploy_settings['deploy_options']
40 self.os_version = ds_opts['os_version']
41 self.ha_enabled = deploy_settings['global_params']['ha_enabled']
45 self.ha_ext = 'noha-allinone'
48 self.snap_cache_dir = os.path.join(snap_cache_dir,
49 "{}/{}".format(self.os_version,
53 self.properties_url = "{}/apex/{}/{}".format(con.OPNFV_ARTIFACTS,
56 self.conn = libvirt.open('qemu:///system')
58 raise exc.SnapshotDeployException(
59 'Unable to open libvirt connection')
61 self.pull_snapshot(self.properties_url, self.snap_cache_dir)
63 logging.info('No fetch enabled. Will not attempt to pull latest '
65 self.deploy_snapshot()
68 def pull_snapshot(url_path, snap_cache_dir):
70 Compare opnfv properties file and download and unpack snapshot if
72 :param url_path: path of latest snap info
73 :param snap_cache_dir: local directory for snap cache
76 full_url = os.path.join(url_path, SNAP_FILE)
77 upstream_props = utils.fetch_properties(full_url)
78 logging.debug("Upstream properties are: {}".format(upstream_props))
80 upstream_sha = upstream_props[CHECKSUM]
82 logging.error('Unable to find {} for upstream properties: '
83 '{}'.format(CHECKSUM, upstream_props))
84 raise exc.SnapshotDeployException('Unable to find upstream '
85 'properties checksum value')
86 local_prop_file = os.path.join(snap_cache_dir, SNAP_FILE)
88 local_props = utils.fetch_properties(local_prop_file)
89 local_sha = local_props[CHECKSUM]
90 pull_snap = local_sha != upstream_sha
91 except (exc.FetchException, KeyError):
92 logging.info("No locally cached properties found, will pull "
96 logging.debug('Local sha: {}, Upstream sha: {}'.format(local_sha,
99 logging.info('SHA mismatch, will download latest snapshot')
100 full_snap_url = upstream_props['OPNFV_SNAP_URL']
101 snap_file = os.path.basename(full_snap_url)
102 snap_url = full_snap_url.replace(snap_file, '')
103 if not snap_url.startswith('http://'):
104 snap_url = 'http://' + snap_url
105 utils.fetch_upstream_and_unpack(dest=snap_cache_dir,
107 targets=[SNAP_FILE, snap_file]
110 logging.info('SHA match, artifacts in cache are already latest. '
111 'Will not download.')
113 def create_networks(self):
114 logging.info("Detecting snapshot networks")
116 xmls = fnmatch.filter(os.listdir(self.snap_cache_dir), '*.xml')
117 except FileNotFoundError:
118 raise exc.SnapshotDeployException(
119 'No XML files found in snap cache directory: {}'.format(
120 self.snap_cache_dir))
123 if xml.startswith('baremetal'):
125 net_xmls.append(os.path.join(self.snap_cache_dir, xml))
127 raise exc.SnapshotDeployException(
128 'No network XML files detected in snap cache, '
129 'please check local snap cache contents')
130 logging.info('Snapshot networks found: {}'.format(net_xmls))
132 logging.debug('Creating network from {}'.format(xml))
133 with open(xml, 'r') as fh:
135 net = self.conn.networkCreateXML(net_xml)
136 self.networks.append(net)
137 logging.info('Network started: {}'.format(net.name()))
139 def parse_and_create_nodes(self):
141 Parse snapshot node.yaml config file and create overcloud nodes
144 node_file = os.path.join(self.snap_cache_dir, 'node.yaml')
145 if not os.path.isfile(node_file):
146 raise exc.SnapshotDeployException('Missing node definitions from '
147 ''.format(node_file))
148 node_data = utils.parse_yaml(node_file)
149 if 'servers' not in node_data:
150 raise exc.SnapshotDeployException('Invalid node.yaml format')
151 for node, data in node_data['servers'].items():
152 logging.info('Creating node: {}'.format(node))
153 logging.debug('Node data is:\n{}'.format(pprint.pformat(data)))
154 node_xml = os.path.join(self.snap_cache_dir,
155 '{}.xml'.format(data['vNode-name']))
156 node_qcow = os.path.join(self.snap_cache_dir,
157 '{}.qcow2'.format(data['vNode-name']))
158 self.oc_nodes.append(
159 OvercloudNode(ip=data['address'],
160 ovs_ctrlrs=data['ovs-controller'],
161 ovs_mgrs=data['ovs-managers'],
167 logging.info('Node Created')
168 logging.info('Starting nodes')
169 for node in self.oc_nodes:
172 def get_controllers(self):
174 for node in self.oc_nodes:
175 if node.role == 'controller':
176 controllers.append(node)
179 def is_service_up(self, service):
180 assert service in SERVICES
181 if service == OPENSTACK:
182 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
184 controllers = self.get_controllers()
186 raise exc.SnapshotDeployException('No OpenStack controllers found')
188 for node in controllers:
189 logging.info('Waiting until {} is up on controller: '
190 '{}'.format(service, node.name))
192 logging.debug('Checking {} is up attempt {}'.format(service,
194 if service == OPENSTACK:
195 # Check if Neutron is up
196 if sock.connect_ex((node.ip, 9696)) == 0:
197 logging.info('{} is up on controller {}'.format(
200 elif service == OPENDAYLIGHT:
201 url = 'http://{}:8081/diagstatus'.format(node.ip)
203 utils.open_webpage(url)
204 logging.info('{} is up on controller {}'.format(
207 except Exception as e:
208 logging.debug('Cannot contact ODL. Reason: '
212 logging.error('{} is not running after 10 attempts'.format(
217 def deploy_snapshot(self):
219 self.create_networks()
220 # check overcloudrc exists, id_rsa
221 for snap_file in (OVERCLOUD_RC, SSH_KEY):
222 if not os.path.isfile(os.path.join(self.snap_cache_dir,
224 logging.warning('File is missing form snap cache: '
225 '{}'.format(snap_file))
227 self.parse_and_create_nodes()
228 # validate deployment
229 if self.is_service_up(OPENSTACK):
230 logging.info('OpenStack is up')
232 raise exc.SnapshotDeployException('OpenStack is not alive')
233 if self.is_service_up(OPENDAYLIGHT):
234 logging.info('OpenDaylight is up')
236 raise exc.SnapshotDeployException(
237 'OpenDaylight {} is not reporting diag status')
238 # TODO(trozet): recreate external network/subnet if missing
239 logging.info('Snapshot deployment complete. Please use the {} file '
240 'in {} to interact with '
241 'OpenStack'.format(OVERCLOUD_RC, self.snap_cache_dir))