168 except rad.client.ObjectError: |
168 except rad.client.ObjectError: |
169 return None |
169 return None |
170 except Exception: |
170 except Exception: |
171 raise |
171 raise |
172 |
172 |
|
173 def zonemgr_strerror(ex): |
|
174 """Format the payload from a zonemgr(3RAD) rad.client.ObjectError |
|
175 exception into a sensible error string that can be logged. Newlines |
|
176 are converted to a colon-space string to create a single line. |
|
177 |
|
178 If the exception was something other than rad.client.ObjectError, |
|
179 just return it as a string. |
|
180 """ |
|
181 if not isinstance(ex, rad.client.ObjectError): |
|
182 return str(ex) |
|
183 payload = ex.get_payload() |
|
184 if payload.code == zonemgr.ErrorCode.NONE: |
|
185 return str(ex) |
|
186 error = [str(payload.code)] |
|
187 if payload.str is not None and payload.str != '': |
|
188 error.append(payload.str) |
|
189 if payload.stderr is not None and payload.stderr != '': |
|
190 stderr = payload.stderr.rstrip() |
|
191 error.append(stderr.replace('\n', ': ')) |
|
192 result = ': '.join(error) |
|
193 return result |
|
194 |
173 |
195 |
174 class ZoneConfig(object): |
196 class ZoneConfig(object): |
175 """ZoneConfig - context manager for access zone configurations. |
197 """ZoneConfig - context manager for access zone configurations. |
176 Automatically opens the configuration for a zone and commits any changes |
198 Automatically opens the configuration for a zone and commits any changes |
177 before exiting |
199 before exiting |
187 """enables the editing of the zone.""" |
209 """enables the editing of the zone.""" |
188 try: |
210 try: |
189 self.zone.editConfig() |
211 self.zone.editConfig() |
190 self.editing = True |
212 self.editing = True |
191 return self |
213 return self |
192 except rad.client.ObjectError as err: |
214 except Exception as ex: |
|
215 reason = zonemgr_strerror(ex) |
193 LOG.error(_("Unable to initialize editing of instance '%s' via " |
216 LOG.error(_("Unable to initialize editing of instance '%s' via " |
194 "zonemgr(3RAD): %s") % (self.zone.name, err)) |
217 "zonemgr(3RAD): %s") % (self.zone.name, reason)) |
195 raise |
218 raise |
196 |
219 |
197 def __exit__(self, exc_type, exc_val, exc_tb): |
220 def __exit__(self, exc_type, exc_val, exc_tb): |
198 """looks for any kind of exception before exiting. If one is found, |
221 """looks for any kind of exception before exiting. If one is found, |
199 cancel any configuration changes and reraise the exception. If not, |
222 cancel any configuration changes and reraise the exception. If not, |
205 raise |
228 raise |
206 else: |
229 else: |
207 # commit the config |
230 # commit the config |
208 try: |
231 try: |
209 self.zone.commitConfig() |
232 self.zone.commitConfig() |
210 except rad.client.ObjectError as err: |
233 except Exception as ex: |
|
234 reason = zonemgr_strerror(ex) |
211 LOG.error(_("Unable to commit the new configuration for " |
235 LOG.error(_("Unable to commit the new configuration for " |
212 "instance '%s' via zonemgr(3RAD): %s") |
236 "instance '%s' via zonemgr(3RAD): %s") |
213 % (self.zone.name, err)) |
237 % (self.zone.name, reason)) |
214 |
238 |
215 # Last ditch effort to cleanup. |
239 # Last ditch effort to cleanup. |
216 self.zone.cancelConfig() |
240 self.zone.cancelConfig() |
217 raise |
241 raise |
218 |
242 |
231 resource, [zonemgr.Property(prop, value)])) |
255 resource, [zonemgr.Property(prop, value)])) |
232 else: |
256 else: |
233 self.zone.setResourceProperties( |
257 self.zone.setResourceProperties( |
234 zonemgr.Resource(resource), |
258 zonemgr.Resource(resource), |
235 [zonemgr.Property(prop, value)]) |
259 [zonemgr.Property(prop, value)]) |
236 except rad.client.ObjectError as err: |
260 except Exception as ex: |
|
261 reason = zonemgr_strerror(ex) |
237 LOG.error(_("Unable to set '%s' property on '%s' resource for " |
262 LOG.error(_("Unable to set '%s' property on '%s' resource for " |
238 "instance '%s' via zonemgr(3RAD): %s") |
263 "instance '%s' via zonemgr(3RAD): %s") |
239 % (prop, resource, self.zone.name, err)) |
264 % (prop, resource, self.zone.name, reason)) |
240 raise |
265 raise |
241 |
266 |
242 def addresource(self, resource, props=None): |
267 def addresource(self, resource, props=None): |
243 """creates a new resource with an optional property list.""" |
268 """creates a new resource with an optional property list.""" |
244 if props is None: |
269 if props is None: |
245 props = [] |
270 props = [] |
246 |
271 |
247 try: |
272 try: |
248 self.zone.addResource(zonemgr.Resource(resource, props)) |
273 self.zone.addResource(zonemgr.Resource(resource, props)) |
249 except rad.client.ObjectError as err: |
274 except Exception as ex: |
|
275 if isinstance(ex, rad.client.ObjectError): |
|
276 code = ex.get_payload().code |
|
277 if (ignore_exists and |
|
278 code == zonemgr.ErrorCode.RESOURCE_ALREADY_EXISTS): |
|
279 self.zone.setResourceProperties(zonemgr.Resource( |
|
280 resource, None), props) |
|
281 return |
|
282 reason = zonemgr_strerror(ex) |
250 LOG.error(_("Unable to create new resource '%s' for instance '%s'" |
283 LOG.error(_("Unable to create new resource '%s' for instance '%s'" |
251 "via zonemgr(3RAD): %s") |
284 "via zonemgr(3RAD): %s") |
252 % (resource, self.zone.name, err)) |
285 % (resource, self.zone.name, reason)) |
253 raise |
286 raise |
254 |
287 |
255 def removeresources(self, resource, props=None): |
288 def removeresources(self, resource, props=None): |
256 """removes resources whose properties include the optional property |
289 """removes resources whose properties include the optional property |
257 list specified in props. |
290 list specified in props. |
259 if props is None: |
292 if props is None: |
260 props = [] |
293 props = [] |
261 |
294 |
262 try: |
295 try: |
263 self.zone.removeResources(zonemgr.Resource(resource, props)) |
296 self.zone.removeResources(zonemgr.Resource(resource, props)) |
264 except rad.client.ObjectError as err: |
297 except Exception as ex: |
|
298 reason = zonemgr_strerror(ex) |
265 LOG.error(_("Unable to remove resource '%s' for instance '%s' via " |
299 LOG.error(_("Unable to remove resource '%s' for instance '%s' via " |
266 "zonemgr(3RAD): %s") % (resource, self.zone.name, err)) |
300 "zonemgr(3RAD): %s") |
|
301 % (resource, self.zone.name, reason)) |
267 raise |
302 raise |
268 |
303 |
269 |
304 |
270 class SolarisZonesDriver(driver.ComputeDriver): |
305 class SolarisZonesDriver(driver.ComputeDriver): |
271 """Solaris Zones Driver using the zonemgr(3RAD) and kstat(3RAD) providers. |
306 """Solaris Zones Driver using the zonemgr(3RAD) and kstat(3RAD) providers. |
576 """ |
611 """ |
577 # TODO(Vek): Need to pass context in for access to auth_token |
612 # TODO(Vek): Need to pass context in for access to auth_token |
578 name = instance['name'] |
613 name = instance['name'] |
579 zone = self._get_zone_by_name(name) |
614 zone = self._get_zone_by_name(name) |
580 if zone is None: |
615 if zone is None: |
581 LOG.error(_("Unable to find instance '%s' via zonemgr(3RAD)") |
|
582 % name) |
|
583 raise exception.InstanceNotFound(instance_id=name) |
616 raise exception.InstanceNotFound(instance_id=name) |
584 return { |
617 return { |
585 'state': self._get_state(zone), |
618 'state': self._get_state(zone), |
586 'max_mem': self._get_max_mem(zone), |
619 'max_mem': self._get_max_mem(zone), |
587 'mem': self._get_mem(zone), |
620 'mem': self._get_mem(zone), |
833 # Use suriadm(1M) to generate a Fibre Channel storage URI. |
866 # Use suriadm(1M) to generate a Fibre Channel storage URI. |
834 try: |
867 try: |
835 out, err = utils.execute('/usr/sbin/suriadm', 'lookup-uri', |
868 out, err = utils.execute('/usr/sbin/suriadm', 'lookup-uri', |
836 '-p', 'target=naa.%s' % target_wwn, |
869 '-p', 'target=naa.%s' % target_wwn, |
837 '-p', 'lun=%s' % target_lun) |
870 '-p', 'lun=%s' % target_lun) |
838 except processutils.ProcessExecutionError as err: |
871 except processutils.ProcessExecutionError as ex: |
|
872 reason = ex.stderr |
839 LOG.error(_("Lookup failure of Fibre Channel volume '%s', lun " |
873 LOG.error(_("Lookup failure of Fibre Channel volume '%s', lun " |
840 "%s: %s") % (target_wwn, target_lun, err.stderr)) |
874 "%s: %s") % (target_wwn, target_lun, reason)) |
841 raise |
875 raise |
842 |
876 |
843 lines = out.split('\n') |
877 lines = out.split('\n') |
844 # Use the long form SURI on the second output line. |
878 # Use the long form SURI on the second output line. |
845 suri = lines[1].strip() |
879 suri = lines[1].strip() |
1184 self._set_boot_device(name, connection_info, brand) |
1218 self._set_boot_device(name, connection_info, brand) |
1185 self._set_num_cpu(name, instance['vcpus'], brand) |
1219 self._set_num_cpu(name, instance['vcpus'], brand) |
1186 self._set_memory_cap(name, instance['memory_mb'], brand) |
1220 self._set_memory_cap(name, instance['memory_mb'], brand) |
1187 self._set_network(context, name, instance, network_info, brand, |
1221 self._set_network(context, name, instance, network_info, brand, |
1188 sc_dir) |
1222 sc_dir) |
1189 except Exception as reason: |
1223 except Exception as ex: |
|
1224 reason = zonemgr_strerror(ex) |
1190 LOG.error(_("Unable to create configuration for instance '%s' via " |
1225 LOG.error(_("Unable to create configuration for instance '%s' via " |
1191 "zonemgr(3RAD): %s") % (name, reason)) |
1226 "zonemgr(3RAD): %s") % (name, reason)) |
1192 raise |
1227 raise |
1193 |
1228 |
1194 def _create_vnc_console_service(self, instance): |
1229 def _create_vnc_console_service(self, instance): |
1209 name = instance['name'] |
1244 name = instance['name'] |
1210 # TODO(npower): investigate using RAD instead of CLI invocation |
1245 # TODO(npower): investigate using RAD instead of CLI invocation |
1211 try: |
1246 try: |
1212 out, err = utils.execute('/usr/sbin/svccfg', '-s', |
1247 out, err = utils.execute('/usr/sbin/svccfg', '-s', |
1213 VNC_CONSOLE_BASE_FMRI, 'add', name) |
1248 VNC_CONSOLE_BASE_FMRI, 'add', name) |
1214 except processutils.ProcessExecutionError as err: |
1249 except processutils.ProcessExecutionError as ex: |
1215 if self._has_vnc_console_service(instance): |
1250 if self._has_vnc_console_service(instance): |
1216 LOG.debug(_("Ignoring attempt to create existing zone VNC " |
1251 LOG.debug(_("Ignoring attempt to create existing zone VNC " |
1217 "console SMF service for instance '%s'") % name) |
1252 "console SMF service for instance '%s'") % name) |
1218 return |
1253 return |
1219 else: |
1254 reason = ex.stderr |
1220 LOG.error(_("Unable to create zone VNC console SMF service " |
1255 LOG.error(_("Unable to create zone VNC console SMF service " |
1221 "'{0}': {1}").format( |
1256 "'{0}': {1}").format( |
1222 VNC_CONSOLE_BASE_FMRI + ':' + name, err)) |
1257 VNC_CONSOLE_BASE_FMRI + ':' + name, reason)) |
1223 raise |
1258 raise |
1224 |
1259 |
1225 def _delete_vnc_console_service(self, instance): |
1260 def _delete_vnc_console_service(self, instance): |
1226 """Delete a VNC console SMF service for a Solaris Zone""" |
1261 """Delete a VNC console SMF service for a Solaris Zone""" |
1227 name = instance['name'] |
1262 name = instance['name'] |
1228 self._disable_vnc_console_service(instance) |
1263 self._disable_vnc_console_service(instance) |
1229 # TODO(npower): investigate using RAD instead of CLI invocation |
1264 # TODO(npower): investigate using RAD instead of CLI invocation |
1230 try: |
1265 try: |
1231 out, err = utils.execute('/usr/sbin/svccfg', '-s', |
1266 out, err = utils.execute('/usr/sbin/svccfg', '-s', |
1232 VNC_CONSOLE_BASE_FMRI, 'delete', '-f', |
1267 VNC_CONSOLE_BASE_FMRI, 'delete', '-f', |
1233 name) |
1268 name) |
1234 except processutils.ProcessExecutionError as err: |
1269 except processutils.ProcessExecutionError as ex: |
1235 if not self._has_vnc_console_service(instance): |
1270 if not self._has_vnc_console_service(instance): |
1236 LOG.debug(_("Ignoring attempt to delete a non-existent zone " |
1271 LOG.debug(_("Ignoring attempt to delete a non-existent zone " |
1237 "VNC console SMF service for instance '%s'") |
1272 "VNC console SMF service for instance '%s'") |
1238 % name) |
1273 % name) |
1239 return |
1274 return |
1240 else: |
1275 reason = ex.stderr |
1241 LOG.error(_("Unable to delete zone VNC console SMF service " |
1276 LOG.error(_("Unable to delete zone VNC console SMF service '%s': " |
1242 "'%s': %s") |
1277 "%s") |
1243 % (VNC_CONSOLE_BASE_FMRI + ':' + name, err)) |
1278 % (VNC_CONSOLE_BASE_FMRI + ':' + name, reason)) |
1244 raise |
1279 raise |
1245 |
1280 |
1246 def _enable_vnc_console_service(self, instance): |
1281 def _enable_vnc_console_service(self, instance): |
1247 """Enable a zone VNC console SMF service""" |
1282 """Enable a zone VNC console SMF service""" |
1248 name = instance['name'] |
1283 name = instance['name'] |
1249 |
1284 |
1257 'setprop', 'vnc/nova-enabled=true') |
1292 'setprop', 'vnc/nova-enabled=true') |
1258 out, err = utils.execute('/usr/sbin/svccfg', '-s', console_fmri, |
1293 out, err = utils.execute('/usr/sbin/svccfg', '-s', console_fmri, |
1259 'refresh') |
1294 'refresh') |
1260 out, err = utils.execute('/usr/sbin/svcadm', 'enable', |
1295 out, err = utils.execute('/usr/sbin/svcadm', 'enable', |
1261 console_fmri) |
1296 console_fmri) |
1262 except processutils.ProcessExecutionError as err: |
1297 except processutils.ProcessExecutionError as ex: |
1263 if not self._has_vnc_console_service(instance): |
1298 if not self._has_vnc_console_service(instance): |
1264 LOG.debug(_("Ignoring attempt to enable a non-existent zone " |
1299 LOG.debug(_("Ignoring attempt to enable a non-existent zone " |
1265 "VNC console SMF service for instance '%s'") |
1300 "VNC console SMF service for instance '%s'") |
1266 % name) |
1301 % name) |
|
1302 return |
|
1303 reason = ex.stderr |
1267 LOG.error(_("Unable to start zone VNC console SMF service " |
1304 LOG.error(_("Unable to start zone VNC console SMF service " |
1268 "'%s': %s") % (console_fmri, err)) |
1305 "'%s': %s") % (console_fmri, reason)) |
1269 raise |
1306 raise |
1270 |
1307 |
1271 # Allow some time for the console service to come online. |
1308 # Allow some time for the console service to come online. |
1272 greenthread.sleep(2) |
1309 greenthread.sleep(2) |
1273 while True: |
1310 while True: |
1284 raise exception.ConsoleNotFoundForInstance( |
1321 raise exception.ConsoleNotFoundForInstance( |
1285 instance_uuid=instance['uuid']) |
1322 instance_uuid=instance['uuid']) |
1286 # Wait for service state to transition to (hopefully) online |
1323 # Wait for service state to transition to (hopefully) online |
1287 # state or offline/maintenance states. |
1324 # state or offline/maintenance states. |
1288 greenthread.sleep(2) |
1325 greenthread.sleep(2) |
1289 except processutils.ProcessExecutionError as err: |
1326 except processutils.ProcessExecutionError as ex: |
|
1327 reason = ex.stderr |
1290 LOG.error(_("Error querying state of zone VNC console SMF " |
1328 LOG.error(_("Error querying state of zone VNC console SMF " |
1291 "service '%s': %s") % (console_fmri, err)) |
1329 "service '%s': %s") % (console_fmri, reason)) |
1292 raise |
1330 raise |
1293 # TODO(npower): investigate using RAD instead of CLI invocation |
1331 # TODO(npower): investigate using RAD instead of CLI invocation |
1294 try: |
1332 try: |
1295 # The console SMF service exits with SMF_TEMP_DISABLE to prevent |
1333 # The console SMF service exits with SMF_TEMP_DISABLE to prevent |
1296 # unnecessarily coming online at boot. Make that happen. |
1334 # unnecessarily coming online at boot. Make that happen. |
1297 out, err = utils.execute('/usr/sbin/svccfg', '-s', console_fmri, |
1335 out, err = utils.execute('/usr/sbin/svccfg', '-s', console_fmri, |
1298 'setprop', 'vnc/nova-enabled=false') |
1336 'setprop', 'vnc/nova-enabled=false') |
1299 out, err = utils.execute('/usr/sbin/svccfg', '-s', console_fmri, |
1337 out, err = utils.execute('/usr/sbin/svccfg', '-s', console_fmri, |
1300 'refresh') |
1338 'refresh') |
1301 except processutils.ProcessExecutionError as err: |
1339 except processutils.ProcessExecutionError as ex: |
|
1340 reason = ex.stderr |
1302 LOG.error(_("Unable to update 'vnc/nova-enabled' property for " |
1341 LOG.error(_("Unable to update 'vnc/nova-enabled' property for " |
1303 "zone VNC console SMF service " |
1342 "zone VNC console SMF service " |
1304 "'%s': %s") % (console_fmri, err)) |
1343 "'%s': %s") % (console_fmri, reason)) |
1305 raise |
1344 raise |
1306 |
1345 |
1307 def _disable_vnc_console_service(self, instance): |
1346 def _disable_vnc_console_service(self, instance): |
1308 """Disable a zone VNC console SMF service""" |
1347 """Disable a zone VNC console SMF service""" |
1309 name = instance['name'] |
1348 name = instance['name'] |
1314 console_fmri = VNC_CONSOLE_BASE_FMRI + ':' + name |
1353 console_fmri = VNC_CONSOLE_BASE_FMRI + ':' + name |
1315 # TODO(npower): investigate using RAD instead of CLI invocation |
1354 # TODO(npower): investigate using RAD instead of CLI invocation |
1316 try: |
1355 try: |
1317 out, err = utils.execute('/usr/sbin/svcadm', 'disable', |
1356 out, err = utils.execute('/usr/sbin/svcadm', 'disable', |
1318 console_fmri) |
1357 console_fmri) |
1319 except processutils.ProcessExecutionError as err: |
1358 except processutils.ProcessExecutionError as ex: |
|
1359 reason = ex.stderr |
1320 LOG.error(_("Unable to disable zone VNC console SMF service " |
1360 LOG.error(_("Unable to disable zone VNC console SMF service " |
1321 "'%s': %s") % (console_fmri, err)) |
1361 "'%s': %s") % (console_fmri, reason)) |
1322 # The console service sets a SMF instance property for the port |
1362 # The console service sets a SMF instance property for the port |
1323 # on which the VNC service is listening. The service needs to be |
1363 # on which the VNC service is listening. The service needs to be |
1324 # refreshed to reset the property value |
1364 # refreshed to reset the property value |
1325 try: |
1365 try: |
1326 out, err = utils.execute('/usr/sbin/svccfg', '-s', console_fmri, |
1366 out, err = utils.execute('/usr/sbin/svccfg', '-s', console_fmri, |
1327 'refresh') |
1367 'refresh') |
1328 except processutils.ProcessExecutionError as err: |
1368 except processutils.ProcessExecutionError as ex: |
|
1369 reason = ex.stderr |
1329 LOG.error(_("Unable to refresh zone VNC console SMF service " |
1370 LOG.error(_("Unable to refresh zone VNC console SMF service " |
1330 "'%s': %s") % (console_fmri, err)) |
1371 "'%s': %s") % (console_fmri, reason)) |
1331 |
1372 |
1332 def _get_vnc_console_service_state(self, instance): |
1373 def _get_vnc_console_service_state(self, instance): |
1333 """Returns state of the instance zone VNC console SMF service""" |
1374 """Returns state of the instance zone VNC console SMF service""" |
1334 name = instance['name'] |
1375 name = instance['name'] |
1335 if not self._has_vnc_console_service(instance): |
1376 if not self._has_vnc_console_service(instance): |
1341 # TODO(npower): investigate using RAD instead of CLI invocation |
1382 # TODO(npower): investigate using RAD instead of CLI invocation |
1342 try: |
1383 try: |
1343 state, err = utils.execute('/usr/sbin/svcs', '-H', '-o', 'state', |
1384 state, err = utils.execute('/usr/sbin/svcs', '-H', '-o', 'state', |
1344 console_fmri) |
1385 console_fmri) |
1345 return state.strip() |
1386 return state.strip() |
1346 except processutils.ProcessExecutionError as err: |
1387 except processutils.ProcessExecutionError as ex: |
|
1388 reason = ex.stderr |
1347 LOG.error(_("Console state request failed for zone VNC console " |
1389 LOG.error(_("Console state request failed for zone VNC console " |
1348 "SMF service for instance '%s': %s") % (name, err)) |
1390 "SMF service for instance '%s': %s") |
|
1391 % (name, reason)) |
1349 raise |
1392 raise |
1350 |
1393 |
1351 def _has_vnc_console_service(self, instance): |
1394 def _has_vnc_console_service(self, instance): |
1352 """Returns True if the instance has a zone VNC console SMF service""" |
1395 """Returns True if the instance has a zone VNC console SMF service""" |
1353 name = instance['name'] |
1396 name = instance['name'] |
1355 # TODO(npower): investigate using RAD instead of CLI invocation |
1398 # TODO(npower): investigate using RAD instead of CLI invocation |
1356 try: |
1399 try: |
1357 utils.execute('/usr/bin/svcs', '-H', '-o', 'state', |
1400 utils.execute('/usr/bin/svcs', '-H', '-o', 'state', |
1358 console_fmri) |
1401 console_fmri) |
1359 return True |
1402 return True |
1360 except processutils.ProcessExecutionError as err: |
1403 except Exception: |
1361 return False |
1404 return False |
1362 |
1405 |
1363 def _install(self, instance, image, sc_dir): |
1406 def _install(self, instance, image, sc_dir): |
1364 """Install a new Solaris Zone root file system.""" |
1407 """Install a new Solaris Zone root file system.""" |
1365 name = instance['name'] |
1408 name = instance['name'] |
1381 |
1424 |
1382 try: |
1425 try: |
1383 LOG.debug(_("Installing instance '%s' (%s)") % |
1426 LOG.debug(_("Installing instance '%s' (%s)") % |
1384 (name, instance['display_name'])) |
1427 (name, instance['display_name'])) |
1385 zone.install(options=options) |
1428 zone.install(options=options) |
1386 except Exception as reason: |
1429 except Exception as ex: |
|
1430 reason = zonemgr_strerror(ex) |
1387 LOG.error(_("Unable to install root file system for instance '%s' " |
1431 LOG.error(_("Unable to install root file system for instance '%s' " |
1388 "via zonemgr(3RAD): %s") % (name, reason)) |
1432 "via zonemgr(3RAD): %s") % (name, reason)) |
1389 raise |
1433 raise |
1390 |
1434 |
1391 LOG.debug(_("Installation of instance '%s' (%s) complete") % |
1435 LOG.debug(_("Installation of instance '%s' (%s) complete") % |
1398 if zone is None: |
1442 if zone is None: |
1399 raise exception.InstanceNotFound(instance_id=name) |
1443 raise exception.InstanceNotFound(instance_id=name) |
1400 |
1444 |
1401 try: |
1445 try: |
1402 zone.boot() |
1446 zone.boot() |
1403 except Exception as reason: |
1447 except Exception as ex: |
|
1448 reason = zonemgr_strerror(ex) |
1404 LOG.error(_("Unable to power on instance '%s' via zonemgr(3RAD): " |
1449 LOG.error(_("Unable to power on instance '%s' via zonemgr(3RAD): " |
1405 "%s") % (name, reason)) |
1450 "%s") % (name, reason)) |
1406 raise exception.InstancePowerOnFailure(reason=reason) |
1451 raise exception.InstancePowerOnFailure(reason=reason) |
1407 |
1452 |
1408 def _uninstall(self, instance): |
1453 def _uninstall(self, instance): |
1416 LOG.debug(_("Uninstall not required for zone '%s' in state '%s'") |
1461 LOG.debug(_("Uninstall not required for zone '%s' in state '%s'") |
1417 % (name, zone.state)) |
1462 % (name, zone.state)) |
1418 return |
1463 return |
1419 try: |
1464 try: |
1420 zone.uninstall(['-F']) |
1465 zone.uninstall(['-F']) |
1421 except Exception as reason: |
1466 except Exception as ex: |
|
1467 reason = zonemgr_strerror(ex) |
1422 LOG.error(_("Unable to uninstall root file system for instance " |
1468 LOG.error(_("Unable to uninstall root file system for instance " |
1423 "'%s' via zonemgr(3RAD): %s") % (name, reason)) |
1469 "'%s' via zonemgr(3RAD): %s") % (name, reason)) |
1424 raise |
1470 raise |
1425 |
1471 |
1426 def _delete_config(self, instance): |
1472 def _delete_config(self, instance): |
1430 raise exception.InstanceNotFound(instance_id=name) |
1476 raise exception.InstanceNotFound(instance_id=name) |
1431 |
1477 |
1432 zonemanager = self.rad_connection.get_object(zonemgr.ZoneManager()) |
1478 zonemanager = self.rad_connection.get_object(zonemgr.ZoneManager()) |
1433 try: |
1479 try: |
1434 zonemanager.delete(name) |
1480 zonemanager.delete(name) |
1435 except Exception as reason: |
1481 except Exception as ex: |
|
1482 reason = zonemgr_strerror(ex) |
1436 LOG.error(_("Unable to delete configuration for instance '%s' via " |
1483 LOG.error(_("Unable to delete configuration for instance '%s' via " |
1437 "zonemgr(3RAD): %s") % (name, reason)) |
1484 "zonemgr(3RAD): %s") % (name, reason)) |
1438 raise |
1485 raise |
1439 |
1486 |
1440 def spawn(self, context, instance, image_meta, injected_files, |
1487 def spawn(self, context, instance, image_meta, injected_files, |
1477 mountpoint = "c1d0" |
1524 mountpoint = "c1d0" |
1478 name = instance['name'] |
1525 name = instance['name'] |
1479 try: |
1526 try: |
1480 connection_info = self._connect_boot_volume(volume, mountpoint, |
1527 connection_info = self._connect_boot_volume(volume, mountpoint, |
1481 context, instance) |
1528 context, instance) |
1482 except exception.InvalidVolume as badvol: |
1529 except exception.InvalidVolume as reason: |
1483 # This Cinder volume is not usable for ZOSS so discard it. |
1530 # This Cinder volume is not usable for ZOSS so discard it. |
1484 # zonecfg will apply default zonepath dataset configuration |
1531 # zonecfg will apply default zonepath dataset configuration |
1485 # instead. Carry on |
1532 # instead. Carry on |
1486 LOG.warning(_("Volume '%s' is being discarded: %s") |
1533 LOG.warning(_("Volume '%s' is being discarded: %s") |
1487 % (volume_id, badvol)) |
1534 % (volume_id, reason)) |
1488 self._volume_api.delete(context, volume_id) |
1535 self._volume_api.delete(context, volume_id) |
1489 connection_info = None |
1536 connection_info = None |
1490 except Exception as reason: |
1537 except Exception as reason: |
1491 # Something really bad happened. Don't pass Go. |
1538 # Something really bad happened. Don't pass Go. |
1492 LOG.error(_("Unable to attach root zpool volume '%s' to instance " |
1539 LOG.error(_("Unable to attach root zpool volume '%s' to instance " |
1503 connection_info, sc_dir) |
1550 connection_info, sc_dir) |
1504 configured = True |
1551 configured = True |
1505 self._install(instance, image, sc_dir) |
1552 self._install(instance, image, sc_dir) |
1506 installed = True |
1553 installed = True |
1507 self._power_on(instance) |
1554 self._power_on(instance) |
1508 except Exception as reason: |
1555 except Exception as ex: |
|
1556 reason = zonemgr_strerror(ex) |
1509 LOG.error(_("Unable to spawn instance '%s' via zonemgr(3RAD): %s") |
1557 LOG.error(_("Unable to spawn instance '%s' via zonemgr(3RAD): %s") |
1510 % (name, reason)) |
1558 % (name, reason)) |
1511 if installed: |
1559 if installed: |
1512 self._uninstall(instance) |
1560 self._uninstall(instance) |
1513 if configured: |
1561 if configured: |
1543 if halt_type == 'SOFT': |
1591 if halt_type == 'SOFT': |
1544 zone.shutdown() |
1592 zone.shutdown() |
1545 else: |
1593 else: |
1546 # 'HARD' |
1594 # 'HARD' |
1547 zone.halt() |
1595 zone.halt() |
1548 except rad.client.ObjectError as reason: |
1596 except Exception as ex: |
1549 result = reason.get_payload() |
1597 reason = zonemgr_strerror(ex) |
1550 if result.code == zonemgr.ErrorCode.COMMAND_ERROR: |
1598 if isinstance(ex, rad.client.ObjectError): |
1551 LOG.warning(_("Ignoring command error returned while trying " |
1599 code = ex.get_payload().code |
1552 "to power off instance '%s' via zonemgr(3RAD): " |
1600 if code == zonemgr.ErrorCode.COMMAND_ERROR: |
1553 "%s" % (name, reason))) |
1601 LOG.warning(_("Ignoring command error returned while " |
1554 return |
1602 "trying to power off instance '%s' via " |
1555 except Exception as reason: |
1603 "zonemgr(3RAD): %s" % (name, reason))) |
|
1604 return |
1556 LOG.error(_("Unable to power off instance '%s' via zonemgr(3RAD): " |
1605 LOG.error(_("Unable to power off instance '%s' via zonemgr(3RAD): " |
1557 "%s") % (name, reason)) |
1606 "%s") % (name, reason)) |
1558 raise exception.InstancePowerOffFailure(reason=reason) |
1607 raise exception.InstancePowerOffFailure(reason=reason) |
1559 |
1608 |
1560 def destroy(self, context, instance, network_info, block_device_info=None, |
1609 def destroy(self, context, instance, network_info, block_device_info=None, |
1597 self._power_off(instance, 'HARD') |
1646 self._power_off(instance, 'HARD') |
1598 if self._get_state(zone) == power_state.SHUTDOWN: |
1647 if self._get_state(zone) == power_state.SHUTDOWN: |
1599 self._uninstall(instance) |
1648 self._uninstall(instance) |
1600 if self._get_state(zone) == power_state.NOSTATE: |
1649 if self._get_state(zone) == power_state.NOSTATE: |
1601 self._delete_config(instance) |
1650 self._delete_config(instance) |
1602 except Exception as reason: |
1651 except Exception as ex: |
|
1652 reason = zonemgr_strerror(ex) |
1603 LOG.warning(_("Unable to destroy instance '%s' via zonemgr(3RAD): " |
1653 LOG.warning(_("Unable to destroy instance '%s' via zonemgr(3RAD): " |
1604 "%s") % (name, reason)) |
1654 "%s") % (name, reason)) |
1605 |
1655 |
1606 def cleanup(self, context, instance, network_info, block_device_info=None, |
1656 def cleanup(self, context, instance, network_info, block_device_info=None, |
1607 destroy_disks=True, migrate_data=None, destroy_vifs=True): |
1657 destroy_disks=True, migrate_data=None, destroy_vifs=True): |
1739 # refreshed to reflect the current property value |
1790 # refreshed to reflect the current property value |
1740 # TODO(npower): investigate using RAD instead of CLI invocation |
1791 # TODO(npower): investigate using RAD instead of CLI invocation |
1741 try: |
1792 try: |
1742 out, err = utils.execute('/usr/sbin/svccfg', '-s', console_fmri, |
1793 out, err = utils.execute('/usr/sbin/svccfg', '-s', console_fmri, |
1743 'refresh') |
1794 'refresh') |
1744 except processutils.ProcessExecutionError as err: |
1795 except processutils.ProcessExecutionError as ex: |
|
1796 reason = ex.stderr |
1745 LOG.error(_("Unable to refresh zone VNC console SMF service " |
1797 LOG.error(_("Unable to refresh zone VNC console SMF service " |
1746 "'%s': %s" % (console_fmri, err))) |
1798 "'%s': %s" % (console_fmri, reason))) |
1747 raise |
1799 raise |
1748 |
1800 |
1749 host = CONF.vncserver_proxyclient_address |
1801 host = CONF.vncserver_proxyclient_address |
1750 try: |
1802 try: |
1751 out, err = utils.execute('/usr/bin/svcprop', '-p', 'vnc/port', |
1803 out, err = utils.execute('/usr/bin/svcprop', '-p', 'vnc/port', |
1752 console_fmri) |
1804 console_fmri) |
1753 port = int(out.strip()) |
1805 port = int(out.strip()) |
1754 return ctype.ConsoleVNC(host=host, |
1806 return ctype.ConsoleVNC(host=host, |
1755 port=port, |
1807 port=port, |
1756 internal_access_path=None) |
1808 internal_access_path=None) |
1757 except processutils.ProcessExecutionError as err: |
1809 except processutils.ProcessExecutionError as ex: |
|
1810 reason = ex.stderr |
1758 LOG.error(_("Unable to read VNC console port from zone VNC " |
1811 LOG.error(_("Unable to read VNC console port from zone VNC " |
1759 "console SMF service '%s': %s" |
1812 "console SMF service '%s': %s" |
1760 % (console_fmri, err))) |
1813 % (console_fmri, reason))) |
1761 |
1814 |
1762 def get_spice_console(self, context, instance): |
1815 def get_spice_console(self, context, instance): |
1763 """Get connection info for a spice console. |
1816 """Get connection info for a spice console. |
1764 |
1817 |
1765 :param context: security context |
1818 :param context: security context |
1827 """ |
1880 """ |
1828 # TODO(Vek): Need to pass context in for access to auth_token |
1881 # TODO(Vek): Need to pass context in for access to auth_token |
1829 name = instance['name'] |
1882 name = instance['name'] |
1830 zone = self._get_zone_by_name(name) |
1883 zone = self._get_zone_by_name(name) |
1831 if zone is None: |
1884 if zone is None: |
1832 LOG.error(_("Unable to find instance '%s' via zonemgr(3RAD)") |
|
1833 % name) |
|
1834 raise exception.InstanceNotFound(instance_id=name) |
1885 raise exception.InstanceNotFound(instance_id=name) |
1835 return self._get_zone_diagnostics(zone) |
1886 return self._get_zone_diagnostics(zone) |
1836 |
1887 |
1837 def get_instance_diagnostics(self, instance): |
1888 def get_instance_diagnostics(self, instance): |
1838 """Return data about VM diagnostics. |
1889 """Return data about VM diagnostics. |
2028 metadata['disk_format'] = 'zfs' |
2079 metadata['disk_format'] = 'zfs' |
2029 snapshot_service.update(context, |
2080 snapshot_service.update(context, |
2030 image_id, |
2081 image_id, |
2031 metadata, |
2082 metadata, |
2032 None) |
2083 None) |
2033 except exception.Invalid as invalid: |
2084 except exception.Invalid: |
2034 LOG.warning(_("Image service rejected image metadata " |
2085 LOG.warning(_("Image service rejected image metadata " |
2035 "container and disk formats 'uar' and " |
2086 "container and disk formats 'uar' and " |
2036 "'zfs'. Using generic values 'ovf' and " |
2087 "'zfs'. Using generic values 'ovf' and " |
2037 "'raw' as fallbacks.")) |
2088 "'raw' as fallbacks.")) |
2038 finally: |
2089 finally: |
2203 def _get_zpool_property(self, prop, zpool): |
2254 def _get_zpool_property(self, prop, zpool): |
2204 """Get the value of property from the zpool.""" |
2255 """Get the value of property from the zpool.""" |
2205 try: |
2256 try: |
2206 value = None |
2257 value = None |
2207 (out, _err) = utils.execute('/usr/sbin/zpool', 'get', prop, zpool) |
2258 (out, _err) = utils.execute('/usr/sbin/zpool', 'get', prop, zpool) |
2208 except processutils.ProcessExecutionError as err: |
2259 except processutils.ProcessExecutionError as ex: |
|
2260 reason = ex.stderr |
2209 LOG.error(_("Failed to get property '%s' from zpool '%s': %s") |
2261 LOG.error(_("Failed to get property '%s' from zpool '%s': %s") |
2210 % (prop, zpool, err.stderr)) |
2262 % (prop, zpool, reason)) |
2211 return value |
2263 return value |
2212 |
2264 |
2213 zpool_prop = out.splitlines()[1].split() |
2265 zpool_prop = out.splitlines()[1].split() |
2214 if zpool_prop[1] == prop: |
2266 if zpool_prop[1] == prop: |
2215 value = zpool_prop[2] |
2267 value = zpool_prop[2] |
2361 name = instance['name'] |
2413 name = instance['name'] |
2362 try: |
2414 try: |
2363 self._live_migration(name, dest, dry_run=False) |
2415 self._live_migration(name, dest, dry_run=False) |
2364 except Exception as ex: |
2416 except Exception as ex: |
2365 with excutils.save_and_reraise_exception(): |
2417 with excutils.save_and_reraise_exception(): |
|
2418 reason = zonemgr_strerror(ex) |
2366 LOG.error(_("Unable to live migrate instance '%s' to host " |
2419 LOG.error(_("Unable to live migrate instance '%s' to host " |
2367 "'%s' via zonemgr(3RAD): %s") |
2420 "'%s' via zonemgr(3RAD): %s") |
2368 % (name, dest, ex)) |
2421 % (name, dest, reason)) |
2369 recover_method(context, instance, dest, block_migration) |
2422 recover_method(context, instance, dest, block_migration) |
2370 |
2423 |
2371 post_method(context, instance, dest, block_migration, migrate_data) |
2424 post_method(context, instance, dest, block_migration, migrate_data) |
2372 |
2425 |
2373 def rollback_live_migration_at_destination(self, context, instance, |
2426 def rollback_live_migration_at_destination(self, context, instance, |
2416 return |
2469 return |
2417 |
2470 |
2418 try: |
2471 try: |
2419 self._delete_config(instance) |
2472 self._delete_config(instance) |
2420 except Exception as ex: |
2473 except Exception as ex: |
|
2474 reason = zonemgr_strerror(ex) |
2421 LOG.error(_("Unable to delete configuration for instance '%s' via " |
2475 LOG.error(_("Unable to delete configuration for instance '%s' via " |
2422 "zonemgr(3RAD): %s") % (name, ex)) |
2476 "zonemgr(3RAD): %s") % (name, reason)) |
2423 raise |
2477 raise |
2424 |
2478 |
2425 def post_live_migration_at_source(self, context, instance, network_info): |
2479 def post_live_migration_at_source(self, context, instance, network_info): |
2426 """Unplug VIFs from networks at source. |
2480 """Unplug VIFs from networks at source. |
2427 |
2481 |
2558 name = instance['name'] |
2612 name = instance['name'] |
2559 dest = dest_check_data['hypervisor_hostname'] |
2613 dest = dest_check_data['hypervisor_hostname'] |
2560 try: |
2614 try: |
2561 self._live_migration(name, dest, dry_run=True) |
2615 self._live_migration(name, dest, dry_run=True) |
2562 except Exception as ex: |
2616 except Exception as ex: |
2563 raise exception.MigrationPreCheckError(reason=ex) |
2617 reason = zonemgr_strerror(ex) |
|
2618 raise exception.MigrationPreCheckError(reason=reason) |
2564 return dest_check_data |
2619 return dest_check_data |
2565 |
2620 |
2566 def get_instance_disk_info(self, instance_name, |
2621 def get_instance_disk_info(self, instance_name, |
2567 block_device_info=None): |
2622 block_device_info=None): |
2568 """Retrieve information about actual disk sizes of an instance. |
2623 """Retrieve information about actual disk sizes of an instance. |