36 import urllib2 |
36 import urllib2 |
37 from urlparse import urlparse |
37 from urlparse import urlparse |
38 |
38 |
39 from lockfile import LockFile, LockTimeout |
39 from lockfile import LockFile, LockTimeout |
40 from oslo_concurrency import processutils |
40 from oslo_concurrency import processutils |
41 from oslo.config import cfg |
41 from oslo_config import cfg |
42 from oslo_utils import excutils |
42 from oslo_log import log as logging |
|
43 from oslo_service import loopingcall |
|
44 from oslo_utils import excutils, strutils |
43 from scp import SCPClient |
45 from scp import SCPClient |
|
46 import six |
44 |
47 |
45 from ironic.common import boot_devices, exception, images, keystone, states, \ |
48 from ironic.common import boot_devices, exception, images, keystone, states, \ |
46 utils |
49 utils |
47 from ironic.common.i18n import _, _LE, _LI, _LW |
50 from ironic.common.i18n import _, _LE, _LI, _LW |
48 from ironic.conductor import task_manager |
51 from ironic.conductor import task_manager |
49 from ironic.conductor import utils as manager_utils |
52 from ironic.conductor import utils as manager_utils |
50 from ironic.db import api as dbapi |
53 from ironic.db import api as dbapi |
51 from ironic.drivers import base |
54 from ironic.drivers import base |
52 from ironic.drivers.modules import ipmitool |
55 from ironic.drivers.modules import ipmitool |
53 from ironic.drivers import utils as driver_utils |
56 from ironic.drivers import utils as driver_utils |
54 from ironic.openstack.common import log as logging |
57 from ironic import objects |
55 from ironic.openstack.common import loopingcall |
|
56 |
58 |
57 PLATFORM = platform.system() |
59 PLATFORM = platform.system() |
58 if PLATFORM != "SunOS": |
60 if PLATFORM != "SunOS": |
59 import tarfile |
61 import tarfile |
60 else: |
62 else: |
88 help='SSH Filename to use.'), |
90 help='SSH Filename to use.'), |
89 cfg.StrOpt('ssh_key_contents', |
91 cfg.StrOpt('ssh_key_contents', |
90 help='Actual SSH Key contents to use.') |
92 help='Actual SSH Key contents to use.') |
91 ] |
93 ] |
92 |
94 |
93 AUTH_OPTS = [ |
|
94 cfg.StrOpt('auth_strategy', |
|
95 default='keystone', |
|
96 help='Method to use for authentication: noauth or keystone.') |
|
97 ] |
|
98 |
|
99 SOLARIS_IPMI_OPTS = [ |
95 SOLARIS_IPMI_OPTS = [ |
100 cfg.StrOpt('imagecache_dirname', |
96 cfg.StrOpt('imagecache_dirname', |
101 default='/var/lib/ironic/images', |
97 default='/var/lib/ironic/images', |
102 help='Default path to image cache.'), |
98 help='Default path to image cache.'), |
103 cfg.StrOpt('imagecache_lock_timeout', |
99 cfg.StrOpt('imagecache_lock_timeout', |
107 |
103 |
108 LOG = logging.getLogger(__name__) |
104 LOG = logging.getLogger(__name__) |
109 |
105 |
110 CONF = cfg.CONF |
106 CONF = cfg.CONF |
111 OPT_GROUP = cfg.OptGroup(name='ai', |
107 OPT_GROUP = cfg.OptGroup(name='ai', |
112 title='Options for the Automated Install driver') |
108 title='Options for the Solaris driver') |
113 CONF.register_group(OPT_GROUP) |
109 CONF.register_group(OPT_GROUP) |
114 CONF.register_opts(AI_OPTS, OPT_GROUP) |
110 CONF.register_opts(AI_OPTS, OPT_GROUP) |
115 CONF.register_opts(AUTH_OPTS) |
|
116 SOLARIS_IPMI_GROUP = cfg.OptGroup( |
111 SOLARIS_IPMI_GROUP = cfg.OptGroup( |
117 name="solaris_ipmi", |
112 name="solaris_ipmi", |
118 title="Options defined in ironic.drivers.modules.solaris_ipmi") |
113 title="Options defined in ironic.drivers.modules.solaris_ipmi") |
119 CONF.register_group(SOLARIS_IPMI_GROUP) |
114 CONF.register_group(SOLARIS_IPMI_GROUP) |
120 CONF.register_opts(SOLARIS_IPMI_OPTS, SOLARIS_IPMI_GROUP) |
115 CONF.register_opts(SOLARIS_IPMI_OPTS, SOLARIS_IPMI_GROUP) |
142 'fmri': _("List of IPS package FMRIs to be installed. " |
137 'fmri': _("List of IPS package FMRIs to be installed. " |
143 "Required if publishers property is set."), |
138 "Required if publishers property is set."), |
144 'install_profiles': _("List of configuration profiles to be applied " |
139 'install_profiles': _("List of configuration profiles to be applied " |
145 "to the installation environment during an install. " |
140 "to the installation environment during an install. " |
146 "Optional."), |
141 "Optional."), |
|
142 'publishers': _("List of IPS publishers to install from, in the format " |
|
143 "name@origin. Required if fmri property is set."), |
|
144 'sc_profiles': _("List of system configuration profiles to be applied " |
|
145 "to an installed system. Optional."), |
|
146 |
|
147 'ipmi_port': _("remote IPMI RMCP port. Optional."), |
|
148 'ipmi_priv_level': |
|
149 _("privilege level; default is ADMINISTRATOR. " |
|
150 "One of %s. Optional.") % '. '.join(ipmitool.VALID_PRIV_LEVELS), |
147 'ipmi_bridging': _("bridging_type; default is \"no\". One of \"single\", " |
151 'ipmi_bridging': _("bridging_type; default is \"no\". One of \"single\", " |
148 "\"dual\", \"no\". Optional."), |
152 "\"dual\", \"no\". Optional."), |
|
153 'ipmi_transit_channel': _("transit channel for bridged request. Required " |
|
154 "only if ipmi_bridging is set to \"dual\"."), |
|
155 'ipmi_transit_address': _("transit address for bridged request. Required " |
|
156 "only if ipmi_bridging is set to \"dual\"."), |
|
157 'ipmi_target_channel': _("destination channel for bridged request. " |
|
158 "Required only if ipmi_bridging is set to " |
|
159 "\"single\" or \"dual\"."), |
|
160 'ipmi_target_address': _("destination address for bridged request. " |
|
161 "Required only if ipmi_bridging is set " |
|
162 "to \"single\" or \"dual\"."), |
149 'ipmi_local_address': _("local IPMB address for bridged requests. " |
163 'ipmi_local_address': _("local IPMB address for bridged requests. " |
150 "Used only if ipmi_bridging is set " |
164 "Used only if ipmi_bridging is set " |
151 "to \"single\" or \"dual\". Optional."), |
165 "to \"single\" or \"dual\". Optional."), |
152 'ipmi_priv_level': |
166 'ipmi_protocol_version': _('the version of the IPMI protocol; default ' |
153 _("privilege level; default is ADMINISTRATOR. " |
167 'is "2.0". One of "1.5", "2.0". Optional.'), |
154 "One of %s. Optional.") % '. '.join(ipmitool.VALID_PRIV_LEVELS), |
168 'ipmi_force_boot_device': _("Whether Ironic should specify the boot " |
155 'ipmi_target_address': _("destination address for bridged request. " |
169 "device to the BMC each time the server " |
156 "Required only if ipmi_bridging is set " |
170 "is turned on, eg. because the BMC is not " |
157 "to \"single\" or \"dual\"."), |
171 "capable of remembering the selected boot " |
158 'ipmi_target_channel': _("destination channel for bridged request. " |
172 "device across power cycles; default value " |
159 "Required only if ipmi_bridging is set to " |
173 "is False. Optional.") |
160 "\"single\" or \"dual\"."), |
|
161 'ipmi_transit_address': _("transit address for bridged request. Required " |
|
162 "only if ipmi_bridging is set to \"dual\"."), |
|
163 'ipmi_transit_channel': _("transit channel for bridged request. Required " |
|
164 "only if ipmi_bridging is set to \"dual\"."), |
|
165 'publishers': _("List of IPS publishers to install from, in the format " |
|
166 "name@origin. Required if fmri property is set."), |
|
167 'sc_profiles': _("List of system configuration profiles to be applied " |
|
168 "to an installed system. Optional.") |
|
169 } |
174 } |
170 |
175 |
171 COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy() |
176 COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy() |
172 COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES) |
177 COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES) |
173 |
178 |
235 LOG.debug("_parse_driver_info()") |
237 LOG.debug("_parse_driver_info()") |
236 info = node.driver_info or {} |
238 info = node.driver_info or {} |
237 bridging_types = ['single', 'dual'] |
239 bridging_types = ['single', 'dual'] |
238 missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)] |
240 missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)] |
239 if missing_info: |
241 if missing_info: |
240 raise exception.MissingParameterValue( |
242 raise exception.MissingParameterValue(_( |
241 _("The following IPMI credentials are not supplied" |
243 "Missing the following IPMI credentials in node's" |
242 " to IPMI driver: %s.") % missing_info) |
244 " driver_info: %s.") % missing_info) |
243 |
245 |
244 address = info.get('ipmi_address') |
246 address = info.get('ipmi_address') |
245 username = info.get('ipmi_username') |
247 username = info.get('ipmi_username') |
246 password = info.get('ipmi_password') |
248 password = six.text_type(info.get('ipmi_password', '')) |
|
249 dest_port = info.get('ipmi_port') |
247 port = info.get('ipmi_terminal_port') |
250 port = info.get('ipmi_terminal_port') |
248 priv_level = info.get('ipmi_priv_level', 'ADMINISTRATOR') |
251 priv_level = info.get('ipmi_priv_level', 'ADMINISTRATOR') |
249 bridging_type = info.get('ipmi_bridging', 'no') |
252 bridging_type = info.get('ipmi_bridging', 'no') |
250 local_address = info.get('ipmi_local_address') |
253 local_address = info.get('ipmi_local_address') |
251 transit_channel = info.get('ipmi_transit_channel') |
254 transit_channel = info.get('ipmi_transit_channel') |
252 transit_address = info.get('ipmi_transit_address') |
255 transit_address = info.get('ipmi_transit_address') |
253 target_channel = info.get('ipmi_target_channel') |
256 target_channel = info.get('ipmi_target_channel') |
254 target_address = info.get('ipmi_target_address') |
257 target_address = info.get('ipmi_target_address') |
255 |
258 protocol_version = str(info.get('ipmi_protocol_version', '2.0')) |
256 if port: |
259 force_boot_device = info.get('ipmi_force_boot_device', False) |
257 try: |
260 |
258 port = int(port) |
261 if not username: |
259 except ValueError: |
262 LOG.warning(_LW('ipmi_username is not defined or empty for node %s: ' |
260 raise exception.InvalidParameterValue(_( |
263 'NULL user will be utilized.') % node.uuid) |
261 "IPMI terminal port is not an integer.")) |
264 if not password: |
|
265 LOG.warning(_LW('ipmi_password is not defined or empty for node %s: ' |
|
266 'NULL password will be utilized.') % node.uuid) |
|
267 |
|
268 if protocol_version not in ipmitool.VALID_PROTO_VERSIONS: |
|
269 valid_versions = ', '.join(ipmitool.VALID_PROTO_VERSIONS) |
|
270 raise exception.InvalidParameterValue(_( |
|
271 "Invalid IPMI protocol version value %(version)s, the valid " |
|
272 "value can be one of %(valid_versions)s") % |
|
273 {'version': protocol_version, 'valid_versions': valid_versions}) |
|
274 |
|
275 if port is not None: |
|
276 port = utils.validate_network_port(port, 'ipmi_terminal_port') |
|
277 |
|
278 if dest_port is not None: |
|
279 dest_port = utils.validate_network_port(dest_port, 'ipmi_port') |
262 |
280 |
263 # check if ipmi_bridging has proper value |
281 # check if ipmi_bridging has proper value |
264 if bridging_type == 'no': |
282 if bridging_type == 'no': |
265 # if bridging is not selected, then set all bridging params to None |
283 # if bridging is not selected, then set all bridging params to None |
266 local_address = transit_channel = transit_address = \ |
284 (local_address, transit_channel, transit_address, target_channel, |
267 target_channel = target_address = None |
285 target_address) = (None,) * 5 |
268 elif bridging_type in bridging_types: |
286 elif bridging_type in bridging_types: |
269 # check if the particular bridging option is supported on host |
287 # check if the particular bridging option is supported on host |
270 if not ipmitool._is_option_supported('%s_bridge' % bridging_type): |
288 if not ipmitool._is_option_supported('%s_bridge' % bridging_type): |
271 raise exception.InvalidParameterValue(_( |
289 raise exception.InvalidParameterValue(_( |
272 "Value for ipmi_bridging is provided as %s, but IPMI " |
290 "Value for ipmi_bridging is provided as %s, but IPMI " |
307 " can be one of %(valid_levels)s") % |
325 " can be one of %(valid_levels)s") % |
308 {'priv_level': priv_level, 'valid_levels': valid_priv_lvls}) |
326 {'priv_level': priv_level, 'valid_levels': valid_priv_lvls}) |
309 |
327 |
310 return { |
328 return { |
311 'address': address, |
329 'address': address, |
|
330 'dest_port': dest_port, |
312 'username': username, |
331 'username': username, |
313 'password': password, |
332 'password': password, |
314 'port': port, |
333 'port': port, |
315 'uuid': node.uuid, |
334 'uuid': node.uuid, |
316 'priv_level': priv_level, |
335 'priv_level': priv_level, |
317 'local_address': local_address, |
336 'local_address': local_address, |
318 'transit_channel': transit_channel, |
337 'transit_channel': transit_channel, |
319 'transit_address': transit_address, |
338 'transit_address': transit_address, |
320 'target_channel': target_channel, |
339 'target_channel': target_channel, |
321 'target_address': target_address |
340 'target_address': target_address, |
322 } |
341 'protocol_version': protocol_version, |
|
342 'force_boot_device': force_boot_device |
|
343 } |
323 |
344 |
324 |
345 |
325 def _exec_ipmitool(driver_info, command): |
346 def _exec_ipmitool(driver_info, command): |
326 """Execute the ipmitool command. |
347 """Execute the ipmitool command. |
327 |
348 |
342 :raises: processutils.ProcessExecutionError from executing the command. |
363 :raises: processutils.ProcessExecutionError from executing the command. |
343 |
364 |
344 """ |
365 """ |
345 LOG.debug("SolarisDeploy._exec_ipmitool:driver_info: '%s', " |
366 LOG.debug("SolarisDeploy._exec_ipmitool:driver_info: '%s', " |
346 "command: '%s'" % (driver_info, command)) |
367 "command: '%s'" % (driver_info, command)) |
|
368 ipmi_version = ('lanplus' |
|
369 if driver_info['protocol_version'] == '2.0' |
|
370 else 'lan') |
347 args = ['/usr/sbin/ipmitool', |
371 args = ['/usr/sbin/ipmitool', |
348 '-I', |
372 '-I', |
349 'lanplus', |
373 ipmi_version, |
350 '-H', |
374 '-H', |
351 driver_info['address'], |
375 driver_info['address'], |
352 '-L', driver_info['priv_level'] |
376 '-L', driver_info['priv_level'] |
353 ] |
377 ] |
|
378 |
|
379 if driver_info['dest_port']: |
|
380 args.append('-p') |
|
381 args.append(driver_info['dest_port']) |
|
382 |
354 if driver_info['username']: |
383 if driver_info['username']: |
355 args.append('-U') |
384 args.append('-U') |
356 args.append(driver_info['username']) |
385 args.append(driver_info['username']) |
357 |
386 |
358 for name, option in ipmitool.BRIDGING_OPTIONS: |
387 for name, option in ipmitool.BRIDGING_OPTIONS: |
376 while True: |
405 while True: |
377 num_tries = num_tries - 1 |
406 num_tries = num_tries - 1 |
378 # NOTE(deva): ensure that no communications are sent to a BMC more |
407 # NOTE(deva): ensure that no communications are sent to a BMC more |
379 # often than once every min_command_interval seconds. |
408 # often than once every min_command_interval seconds. |
380 time_till_next_poll = CONF.ipmi.min_command_interval - ( |
409 time_till_next_poll = CONF.ipmi.min_command_interval - ( |
381 time.time() - LAST_CMD_TIME.get(driver_info['address'], 0)) |
410 time.time() - LAST_CMD_TIME.get(driver_info['address'], 0)) |
382 if time_till_next_poll > 0: |
411 if time_till_next_poll > 0: |
383 time.sleep(time_till_next_poll) |
412 time.sleep(time_till_next_poll) |
384 # Resetting the list that will be utilized so the password arguments |
413 # Resetting the list that will be utilized so the password arguments |
385 # from any previous execution are preserved. |
414 # from any previous execution are preserved. |
386 cmd_args = args[:] |
415 cmd_args = args[:] |
441 driver_info = _parse_driver_info(node) |
470 driver_info = _parse_driver_info(node) |
442 try: |
471 try: |
443 cpu_arch, _err = _exec_ipmitool(driver_info, ipmi_cmd_args) |
472 cpu_arch, _err = _exec_ipmitool(driver_info, ipmi_cmd_args) |
444 except Exception as err: |
473 except Exception as err: |
445 LOG.error(_LE("Failed to get node architecture from IPMI : %s" % |
474 LOG.error(_LE("Failed to get node architecture from IPMI : %s" % |
446 (err))) |
475 (err))) |
447 raise exception.IPMIFailure(cmd=err) |
476 raise exception.IPMIFailure(cmd=err) |
448 |
477 |
449 propdict = {'cpu_arch': cpu_arch} |
478 propdict = {'cpu_arch': cpu_arch} |
450 node_properties = node.properties |
479 node_properties = node.properties |
451 node_properties.update(propdict) |
480 node_properties.update(propdict) |
482 """ |
511 """ |
483 LOG.debug("_check_deploy_state()") |
512 LOG.debug("_check_deploy_state()") |
484 LOG.debug("_check_deploy_state() deploy_thread_state: %s" % |
513 LOG.debug("_check_deploy_state() deploy_thread_state: %s" % |
485 (deploy_thread.state)) |
514 (deploy_thread.state)) |
486 |
515 |
487 # Get DB instance |
|
488 mydbapi = dbapi.get_instance() |
|
489 try: |
516 try: |
490 # Get current DB copy of node |
517 # Get current DB copy of node |
491 cur_node = mydbapi.get_node_by_uuid(node_uuid) |
518 cur_node = objects.Node.get_by_uuid(task.context, node_uuid) |
492 except exception.NodeNotFound: |
519 except exception.NodeNotFound: |
493 LOG.info(_("During check_deploy_state, node %(node)s was not " |
520 LOG.info(_LI("During check_deploy_state, node %(node)s was not " |
494 "found and presumed deleted by another process.") % |
521 "found and presumed deleted by another process.") % |
495 {'node': node_uuid}) |
522 {'node': node_uuid}) |
496 # Thread should have stopped already, but let's make sure. |
523 # Thread should have stopped already, but let's make sure. |
497 deploy_thread.stop() |
524 deploy_thread.stop() |
498 if deploy_thread.state in [states.DEPLOYING, states.DEPLOYWAIT]: |
525 if deploy_thread.state in [states.DEPLOYING, states.DEPLOYWAIT]: |
499 # Update node with done/fail state |
526 # Update node with done/fail state |
504 (task.node.target_provision_state) |
531 (task.node.target_provision_state) |
505 task.node.target_provision_state = states.NOSTATE |
532 task.node.target_provision_state = states.NOSTATE |
506 task.node.save() |
533 task.node.save() |
507 raise loopingcall.LoopingCallDone() |
534 raise loopingcall.LoopingCallDone() |
508 except Exception as err: |
535 except Exception as err: |
509 LOG.info(_("During check_deploy_state, node %(node)s could " |
536 LOG.info(_LI("During check_deploy_state, node %(node)s could " |
510 "not be retrieved: %(err)s") % |
537 "not be retrieved: %(err)s") % |
511 {'node': node_uuid, 'err': err}) |
538 {'node': node_uuid, 'err': err}) |
512 # Thread should have stopped already, but lets make sure. |
539 # Thread should have stopped already, but lets make sure. |
513 deploy_thread.stop() |
540 deploy_thread.stop() |
514 if deploy_thread.state in [states.DEPLOYING, states.DEPLOYWAIT]: |
541 if deploy_thread.state in [states.DEPLOYING, states.DEPLOYWAIT]: |
515 # Update node with done/fail state |
542 # Update node with done/fail state |
536 |
563 |
537 # Update node with done/fail state |
564 # Update node with done/fail state |
538 if deploy_thread.state == states.DEPLOYDONE: |
565 if deploy_thread.state == states.DEPLOYDONE: |
539 cur_node.provision_state = states.ACTIVE |
566 cur_node.provision_state = states.ACTIVE |
540 elif deploy_thread.state == states.DEPLOYFAIL: |
567 elif deploy_thread.state == states.DEPLOYFAIL: |
541 cur_node.last_error = "Install failed; check install.log for " + \ |
568 if deploy_thread.error is not None: |
542 "more details." |
569 cur_node.last_error = deploy_thread.error |
|
570 else: |
|
571 cur_node.last_error = "Install failed; check install.log " + \ |
|
572 "for more details." |
543 cur_node.provision_state = deploy_thread.state |
573 cur_node.provision_state = deploy_thread.state |
544 else: |
574 else: |
545 cur_node.provision_state = deploy_thread.state |
575 cur_node.provision_state = deploy_thread.state |
546 cur_node.target_provision_state = states.NOSTATE |
576 cur_node.target_provision_state = states.NOSTATE |
547 cur_node.save() |
577 cur_node.save() |
635 LOG.debug("_image_refcount_adjust: image_path: %s, " |
665 LOG.debug("_image_refcount_adjust: image_path: %s, " |
636 "count: %s" % (image_path, str(count))) |
666 "count: %s" % (image_path, str(count))) |
637 |
667 |
638 if count == 0: |
668 if count == 0: |
639 # Adjusting by zero makes no sense just return |
669 # Adjusting by zero makes no sense just return |
640 err_msg = _("Zero reference count adjustment attempted " |
670 err_msg = _LE("Zero reference count adjustment attempted " |
641 "on file: %s") % (image_path) |
671 "on file: %s") % (image_path) |
642 LOG.error(err_msg) |
672 LOG.error(err_msg) |
643 raise SolarisIPMIError(msg=err_msg) |
673 raise SolarisIPMIError(msg=err_msg) |
644 |
674 |
645 ref_filename = image_path + ".ref" |
675 ref_filename = image_path + ".ref" |
646 |
676 |
647 if not os.path.exists(ref_filename): |
677 if not os.path.exists(ref_filename): |
648 if count < 0: |
678 if count < 0: |
649 # Cannot decrement reference on non-existent file |
679 # Cannot decrement reference on non-existent file |
650 err_msg = _("Negative reference count adjustment attempted on " |
680 err_msg = _LE("Negative reference count adjustment attempted on " |
651 "non-existent file: %s") % (image_path) |
681 "non-existent file: %s") % (image_path) |
652 LOG.error(err_msg) |
682 LOG.error(err_msg) |
653 raise SolarisIPMIError(msg=err_msg) |
683 raise SolarisIPMIError(msg=err_msg) |
654 |
684 |
655 # Create reference count file |
685 # Create reference count file |
656 with open(ref_filename, "w") as fp: |
686 with open(ref_filename, "w") as fp: |
657 fp.write("0") |
687 fp.write("0") |
658 |
688 |
659 # Acquire lock on refcount file |
689 # Acquire lock on refcount file |
660 lock = _image_refcount_acquire_lock(image_path) |
690 lock = _image_refcount_acquire_lock(image_path) |
661 if lock is None: |
691 if lock is None: |
662 err_msg = _("Failed to acquire lock on image: %s") % (image_path) |
692 err_msg = _LE("Failed to acquire lock on image: %s") % (image_path) |
663 LOG.error(err_msg) |
693 LOG.error(err_msg) |
664 raise SolarisIPMIError(msg=err_msg) |
694 raise SolarisIPMIError(msg=err_msg) |
665 |
695 |
666 with open(ref_filename, "r+") as fp: |
696 with open(ref_filename, "r+") as fp: |
667 ref_count = fp.readline() |
697 ref_count = fp.readline() |
761 |
791 |
762 # Release acquired lock now that file is retrieved |
792 # Release acquired lock now that file is retrieved |
763 lock.release() |
793 lock.release() |
764 |
794 |
765 except Exception as err: |
795 except Exception as err: |
766 LOG.error(_("Unable to fetch image: id %s: %s") |
796 LOG.error(_LE("Unable to fetch image: id %s: %s") |
767 % (url.netloc, err)) |
797 % (url.netloc, err)) |
768 raise |
798 raise |
769 else: |
799 else: |
770 # Increase reference count for this image |
800 # Increase reference count for this image |
771 _image_refcount_adjust(temp_uri, 1) |
801 _image_refcount_adjust(temp_uri, 1) |
772 except Exception as err: |
802 except Exception as err: |
773 # Only remove the temporary file if exception occurs |
803 # Only remove the temporary file if exception occurs |
774 # as noted above Caller is responsible for its removal |
804 # as noted above Caller is responsible for its removal |
775 LOG.error(_("Unable to fetch image: uri %s: %s") % (uri, err)) |
805 LOG.error(_LE("Unable to fetch image: uri %s: %s") % (uri, err)) |
776 if url.scheme == "glance": |
806 if url.scheme == "glance": |
777 _image_refcount_adjust(temp_uri, -1) |
807 _image_refcount_adjust(temp_uri, -1) |
778 else: |
808 else: |
779 try: |
809 try: |
780 os.remove(temp_uri) |
810 os.remove(temp_uri) |
1239 try: |
1269 try: |
1240 # scp temp file to AI Server |
1270 # scp temp file to AI Server |
1241 remote_file = os.path.join("/tmp", obj_name) + ".xml" |
1271 remote_file = os.path.join("/tmp", obj_name) + ".xml" |
1242 aiservice.copy_remote_file(temp_file, remote_file) |
1272 aiservice.copy_remote_file(temp_file, remote_file) |
1243 except Exception as err: |
1273 except Exception as err: |
1244 LOG.error(_("Fetch and create failed for %s: name: %s: %s") % |
1274 LOG.error(_LE("Fetch and create failed for %s: name: %s: %s") % |
1245 (obj_type, obj_uri, err)) |
1275 (obj_type, obj_uri, err)) |
1246 if url.scheme == "glance": |
1276 if url.scheme == "glance": |
1247 _image_refcount_adjust(temp_file, -1) |
1277 _image_refcount_adjust(temp_file, -1) |
1248 else: |
1278 else: |
1249 os.remove(temp_file) |
1279 os.remove(temp_file) |
1282 Thread.__init__(self) |
1312 Thread.__init__(self) |
1283 |
1313 |
1284 self.task = task |
1314 self.task = task |
1285 self.node = task.node |
1315 self.node = task.node |
1286 self._state = states.DEPLOYWAIT |
1316 self._state = states.DEPLOYWAIT |
|
1317 self._error = None |
1287 self.ssh_connection = None |
1318 self.ssh_connection = None |
1288 self.running = True |
1319 self.running = True |
1289 |
1320 |
1290 @property |
1321 @property |
1291 def state(self): |
1322 def state(self): |
1292 """Deployment state property""" |
1323 """Deployment state property""" |
1293 return self._state |
1324 return self._state |
1294 |
1325 |
|
1326 @property |
|
1327 def error(self): |
|
1328 """Deployment error property""" |
|
1329 return self._error |
|
1330 |
1295 def run(self): |
1331 def run(self): |
1296 """Start the thread """ |
1332 """Start the thread """ |
1297 LOG.debug("DeployStateChecker.run(): Connecting...") |
1333 LOG.debug("DeployStateChecker.run(): Connecting...") |
1298 client = utils.ssh_connect(self._get_ssh_dict()) |
1334 # Occasionally SSH connection fails, make three attempts |
|
1335 # before returning failure. |
|
1336 connection_attempt = 0 |
|
1337 while (connection_attempt < 3): |
|
1338 try: |
|
1339 connection_attempt += 1 |
|
1340 client = utils.ssh_connect(self._get_ssh_dict()) |
|
1341 break |
|
1342 except SSHException as err: |
|
1343 if connection_attempt < 3: |
|
1344 continue |
|
1345 else: |
|
1346 self._state = states.DEPLOYFAIL |
|
1347 self._error = str(err) |
|
1348 self.stop() |
|
1349 return |
|
1350 else: |
|
1351 self._state = states.DEPLOYFAIL |
|
1352 self._error = "Failed to establish SSH Connection with node." |
|
1353 self.stop() |
|
1354 return |
|
1355 |
1299 channel = client.invoke_shell() |
1356 channel = client.invoke_shell() |
1300 channel.settimeout(0.0) |
1357 channel.settimeout(0.0) |
1301 channel.set_combine_stderr(True) |
1358 channel.set_combine_stderr(True) |
1302 |
1359 |
1303 # Continuously read stdout from console and parse |
1360 # Continuously read stdout from console and parse |
1823 aiservice = AIService(task, ai_service) |
1880 aiservice = AIService(task, ai_service) |
1824 |
1881 |
1825 # Check if AI Service exists, log message if already removed |
1882 # Check if AI Service exists, log message if already removed |
1826 if not aiservice.exists: |
1883 if not aiservice.exists: |
1827 # There is nothing to clean up as service removed |
1884 # There is nothing to clean up as service removed |
1828 LOG.info(_("AI Service %s already removed.") % (aiservice.name)) |
1885 LOG.info(_LI("AI Service %s already removed.") % (aiservice.name)) |
1829 else: |
1886 else: |
1830 for mac in driver_utils.get_node_mac_addresses(task): |
1887 for mac in driver_utils.get_node_mac_addresses(task): |
1831 # 1. Delete AI Client for this MAC Address |
1888 # 1. Delete AI Client for this MAC Address |
1832 if mac.lower() in aiservice.clients: |
1889 if mac.lower() in aiservice.clients: |
1833 aiservice.delete_client(mac) |
1890 aiservice.delete_client(mac) |
1911 if cpu_arch == 'x86': |
1969 if cpu_arch == 'x86': |
1912 return [boot_devices.PXE, boot_devices.DISK, |
1970 return [boot_devices.PXE, boot_devices.DISK, |
1913 boot_devices.CDROM, boot_devices.BIOS, |
1971 boot_devices.CDROM, boot_devices.BIOS, |
1914 boot_devices.SAFE] |
1972 boot_devices.SAFE] |
1915 elif cpu_arch == 'SPARC': |
1973 elif cpu_arch == 'SPARC': |
1916 return [boot_devices.DISK, 'wanboot'] |
1974 return [boot_devices.DISK, boot_devices.WANBOOT] |
1917 else: |
1975 else: |
1918 raise exception.InvalidParameterValue( |
1976 raise exception.InvalidParameterValue( |
1919 _("Invalid node architecture of '%s'.") % (cpu_arch)) |
1977 _("Invalid node architecture of '%s'.") % (cpu_arch)) |
1920 |
1978 |
1921 @task_manager.require_exclusive_lock |
1979 @task_manager.require_exclusive_lock |
1940 cpu_arch = _get_node_architecture(task.node) |
1998 cpu_arch = _get_node_architecture(task.node) |
1941 archive_uri = task.node.driver_info.get('archive_uri') |
1999 archive_uri = task.node.driver_info.get('archive_uri') |
1942 publishers = task.node.driver_info.get('publishers') |
2000 publishers = task.node.driver_info.get('publishers') |
1943 fmri = task.node.driver_info.get('fmri') |
2001 fmri = task.node.driver_info.get('fmri') |
1944 |
2002 |
|
2003 if task.node.driver_info.get('ipmi_force_boot_device', False): |
|
2004 driver_utils.force_persistent_boot(task, |
|
2005 device, |
|
2006 persistent) |
|
2007 # Reset persistent to False, in case of BMC does not support |
|
2008 # persistent or we do not have admin rights. |
|
2009 persistent = False |
|
2010 |
1945 if cpu_arch == 'x86': |
2011 if cpu_arch == 'x86': |
1946 if device not in self.get_supported_boot_devices(task=task): |
2012 if device not in self.get_supported_boot_devices(task=task): |
1947 raise exception.InvalidParameterValue(_( |
2013 raise exception.InvalidParameterValue(_( |
1948 "Invalid boot device %s specified.") % device) |
2014 "Invalid boot device %s specified.") % device) |
1949 cmd = ["chassis", "bootdev", device] |
2015 cmd = ["chassis", "bootdev", device] |
1950 if persistent: |
2016 if persistent: |
1951 cmd.append("options=persistent") |
2017 cmd.append("options=persistent") |
1952 elif cpu_arch == 'SPARC': |
2018 elif cpu_arch == 'SPARC': |
1953 # Set bootmode script to network DHCP or disk |
2019 # Set bootmode script to network DHCP or disk |
1954 if device == 'wanboot': |
2020 if device == boot_devices.WANBOOT: |
1955 boot_cmd = 'set /HOST/bootmode script="' |
2021 boot_cmd = 'set /HOST/bootmode script="' |
1956 script_str = 'boot net:dhcp - install' |
2022 script_str = 'boot net:dhcp - install' |
1957 if archive_uri: |
2023 if archive_uri: |
1958 new_uri, auth_token = _format_archive_uri(task, |
2024 new_uri, auth_token = _format_archive_uri(task, |
1959 archive_uri) |
2025 archive_uri) |
2023 :persistent: Whether the boot device will persist to all |
2089 :persistent: Whether the boot device will persist to all |
2024 future boots or not, None if it is unknown. |
2090 future boots or not, None if it is unknown. |
2025 |
2091 |
2026 """ |
2092 """ |
2027 LOG.debug("SolarisManagement.get_boot_device") |
2093 LOG.debug("SolarisManagement.get_boot_device") |
|
2094 driver_info = task.node.driver_info |
|
2095 driver_internal_info = task.node.driver_internal_info |
|
2096 |
|
2097 if (driver_info.get('ipmi_force_boot_device', False) and |
|
2098 driver_internal_info.get('persistent_boot_device') and |
|
2099 driver_internal_info.get('is_next_boot_persistent', True)): |
|
2100 return { |
|
2101 'boot_device': driver_internal_info['persistent_boot_device'], |
|
2102 'persistent': True |
|
2103 } |
|
2104 |
2028 cpu_arch = _get_node_architecture(task.node) |
2105 cpu_arch = _get_node_architecture(task.node) |
2029 driver_info = _parse_driver_info(task.node) |
2106 driver_info = _parse_driver_info(task.node) |
2030 response = {'boot_device': None, 'persistent': None} |
2107 response = {'boot_device': None, 'persistent': None} |
2031 |
2108 |
2032 if cpu_arch == 'x86': |
2109 if cpu_arch == 'x86': |
2139 for line in ipmi_props.strip().splitlines()] |
2216 for line in ipmi_props.strip().splitlines()] |
2140 propdict = dict(zip(keys, vallist)) |
2217 propdict = dict(zip(keys, vallist)) |
2141 |
2218 |
2142 # Installed memory size is returned in GB, Nova assumes this is MB |
2219 # Installed memory size is returned in GB, Nova assumes this is MB |
2143 # so convert if returned in GB |
2220 # so convert if returned in GB |
2144 memsize, memtype = propdict['memory_mb'].split(' ') |
2221 try: |
2145 if memtype == 'GB': |
2222 size_bytes = strutils.string_to_bytes( |
2146 propdict['memory_mb'] = int(memsize) * 1024 |
2223 propdict['memory_mb'].replace(' ', '')) |
|
2224 propdict['memory_mb'] = int(size_bytes / float(1 << 20)) |
|
2225 except ValueError: |
|
2226 # Size conversion failed, just ensure value is an int |
|
2227 propdict['memory_mb'] = int(propdict['memory_mb']) |
|
2228 |
|
2229 if propdict['local_gb'] == 'Not Available': |
|
2230 del propdict['local_gb'] |
2147 else: |
2231 else: |
2148 propdict['memory_mb'] = int(memsize) |
2232 # Local disk size can be returned with size type identifier |
|
2233 # remove identifier as this needs to be an int value |
|
2234 try: |
|
2235 size_bytes = strutils.string_to_bytes( |
|
2236 propdict['local_gb'].replace(' ', '')) |
|
2237 propdict['local_gb'] = int(size_bytes / float(1 << 30)) |
|
2238 except ValueError: |
|
2239 # Size conversion failed, just ensure value is an int |
|
2240 propdict['local_gb'] = int(propdict['local_gb']) |
2149 |
2241 |
2150 cpu_props = self._get_cpu_cores(task, propdict['cpus']) |
2242 cpu_props = self._get_cpu_cores(task, propdict['cpus']) |
2151 |
2243 |
2152 vallist = [line.split(': ')[1] |
2244 vallist = [line.split(': ')[1] |
2153 for line in cpu_props.strip().splitlines()] |
2245 for line in cpu_props.strip().splitlines()] |