def _link_ip_address_pxe_configs(task, ipxe_enabled=False): """Link each IP address with the PXE configuration file. :param task: A TaskManager instance. :param ipxe_enabled: Default false boolean to indicate if ipxe is in use by the caller. :raises: FailedToGetIPAddressOnPort :raises: InvalidIPv4Address """ pxe_config_file_path = get_pxe_config_file_path(task.node.uuid, ipxe_enabled=ipxe_enabled) api = dhcp_factory.DHCPFactory().provider ip_addrs = api.get_ip_addresses(task) if not ip_addrs: raise exception.FailedToGetIPAddressOnPort( _("Failed to get IP address for any port on node %s.") % task.node.uuid) for port_ip_address in ip_addrs: ip_address_path = _get_pxe_ip_address_path(port_ip_address) ironic_utils.unlink_without_raise(ip_address_path) relative_source_path = os.path.relpath( pxe_config_file_path, os.path.dirname(ip_address_path)) utils.create_link_without_raise(relative_source_path, ip_address_path)
def test_create_link(self): self.mox.StubOutWithMock(os, "symlink") os.symlink("/fake/source", "/fake/link") self.mox.ReplayAll() utils.create_link_without_raise("/fake/source", "/fake/link") self.mox.VerifyAll()
def _link_ip_address_pxe_configs(task, hex_form): """Link each IP address with the PXE configuration file. :param task: A TaskManager instance. :param hex_form: Boolean value indicating if the conf file name should be hexadecimal equivalent of supplied ipv4 address. :raises: FailedToGetIPAddressOnPort :raises: InvalidIPv4Address """ pxe_config_file_path = get_pxe_config_file_path(task.node.uuid) api = dhcp_factory.DHCPFactory().provider ip_addrs = api.get_ip_addresses(task) if not ip_addrs: raise exception.FailedToGetIPAddressOnPort(_( "Failed to get IP address for any port on node %s.") % task.node.uuid) for port_ip_address in ip_addrs: ip_address_path = _get_pxe_ip_address_path(port_ip_address, hex_form) ironic_utils.unlink_without_raise(ip_address_path) relative_source_path = os.path.relpath( pxe_config_file_path, os.path.dirname(ip_address_path)) utils.create_link_without_raise(relative_source_path, ip_address_path)
def _link_ip_address_pxe_configs(task, ipxe_enabled=False): """Link each IP address with the PXE configuration file. :param task: A TaskManager instance. :param ipxe_enabled: Default false boolean to indicate if ipxe is in use by the caller. :raises: FailedToGetIPAddressOnPort :raises: InvalidIPv4Address """ pxe_config_file_path = get_pxe_config_file_path(task.node.uuid, ipxe_enabled=ipxe_enabled) api = dhcp_factory.DHCPFactory().provider ip_addrs = api.get_ip_addresses(task) if not ip_addrs: if ip_addrs == []: LOG.warning("No IP addresses assigned for node %(node)s.", {'node': task.node.uuid}) else: LOG.warning( "DHCP address management is not available for node " "%(node)s. Operators without Neutron can ignore this " "warning.", {'node': task.node.uuid}) # Just in case, reset to empty list if we got nothing. ip_addrs = [] for port_ip_address in ip_addrs: ip_address_path = _get_pxe_ip_address_path(port_ip_address) ironic_utils.unlink_without_raise(ip_address_path) relative_source_path = os.path.relpath( pxe_config_file_path, os.path.dirname(ip_address_path)) utils.create_link_without_raise(relative_source_path, ip_address_path)
def test_create_link_EEXIST(self): self.mox.StubOutWithMock(os, "symlink") os.symlink("/fake/source", "/fake/link").AndRaise( OSError(errno.EEXIST)) self.mox.ReplayAll() utils.create_link_without_raise("/fake/source", "/fake/link") self.mox.VerifyAll()
def activate_bootloader(self, context, node, instance): """Configure PXE boot loader for an instance Kernel and ramdisk images are downloaded by cache_tftp_images, and stored in /tftpboot/{uuid}/ This method writes the instances config file, and then creates symlinks for each MAC address in the instance. By default, the complete layout looks like this: /tftpboot/ ./{uuid}/ kernel ramdisk deploy_kernel deploy_ramdisk config ./pxelinux.cfg/ {mac} -> ../{uuid}/config """ instance_type = self.virtapi.instance_type_get( context, instance['instance_type_id']) image_info = get_tftp_image_info(instance, instance_type) (root_mb, swap_mb) = get_partition_sizes(instance) pxe_config_file_path = get_pxe_config_file_path(instance) image_file_path = get_image_file_path(instance) deployment_key = utils.random_alnum(32) deployment_iscsi_iqn = "iqn-%s" % instance['uuid'] db.bm_node_update( context, node['id'], { 'deploy_key': deployment_key, 'image_path': image_file_path, 'pxe_config_path': pxe_config_file_path, 'root_mb': root_mb, 'swap_mb': swap_mb }) pxe_config = build_pxe_config( node['id'], deployment_key, deployment_iscsi_iqn, image_info['deploy_kernel'][1], image_info['deploy_ramdisk'][1], image_info['kernel'][1], image_info['ramdisk'][1], ) utils.write_to_file(pxe_config_file_path, pxe_config) macs = self._collect_mac_addresses(context, node) for mac in macs: mac_path = get_pxe_mac_path(mac) utils.unlink_without_raise(mac_path) utils.create_link_without_raise(pxe_config_file_path, mac_path)
def _link_mac_pxe_configs(task): """Link each MAC address with the PXE configuration file. :param task: A TaskManager instance. """ pxe_config_file_path = get_pxe_config_file_path(task.node.uuid) for mac in driver_utils.get_node_mac_addresses(task): mac_path = _get_pxe_mac_path(mac) utils.unlink_without_raise(mac_path) utils.create_link_without_raise(pxe_config_file_path, mac_path)
def activate_bootloader(self, context, node, instance): """Configure PXE boot loader for an instance Kernel and ramdisk images are downloaded by cache_tftp_images, and stored in /tftpboot/{uuid}/ This method writes the instances config file, and then creates symlinks for each MAC address in the instance. By default, the complete layout looks like this: /tftpboot/ ./{uuid}/ kernel ramdisk deploy_kernel deploy_ramdisk config ./pxelinux.cfg/ {mac} -> ../{uuid}/config """ instance_type = self.virtapi.instance_type_get( context, instance['instance_type_id']) image_info = get_tftp_image_info(instance, instance_type) (root_mb, swap_mb) = get_partition_sizes(instance) pxe_config_file_path = get_pxe_config_file_path(instance) image_file_path = get_image_file_path(instance) deployment_key = utils.random_alnum(32) deployment_iscsi_iqn = "iqn-%s" % instance['uuid'] db.bm_node_update(context, node['id'], {'deploy_key': deployment_key, 'image_path': image_file_path, 'pxe_config_path': pxe_config_file_path, 'root_mb': root_mb, 'swap_mb': swap_mb}) pxe_config = build_pxe_config( node['id'], deployment_key, deployment_iscsi_iqn, image_info['deploy_kernel'][1], image_info['deploy_ramdisk'][1], image_info['kernel'][1], image_info['ramdisk'][1], ) utils.write_to_file(pxe_config_file_path, pxe_config) macs = self._collect_mac_addresses(context, node) for mac in macs: mac_path = get_pxe_mac_path(mac) utils.unlink_without_raise(mac_path) utils.create_link_without_raise(pxe_config_file_path, mac_path)
def _create_pxe_config(task, pxe_info): """Generate pxe configuration file and link mac ports to it for tftp booting. """ fileutils.ensure_tree(os.path.join(CONF.pxe.tftp_root, task.node.uuid)) fileutils.ensure_tree(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg')) pxe_config_file_path = _get_pxe_config_file_path(task.node.uuid) pxe_config = _build_pxe_config(task.node, pxe_info, task.context) utils.write_to_file(pxe_config_file_path, pxe_config) for port in driver_utils.get_node_mac_addresses(task): mac_path = _get_pxe_mac_path(port) utils.unlink_without_raise(mac_path) utils.create_link_without_raise(pxe_config_file_path, mac_path)
def _create_pxe_config(task, node, pxe_info): """Generate pxe configuration file and link mac ports to it for tftp booting. """ fileutils.ensure_tree(os.path.join(CONF.pxe.tftp_root, node.uuid)) fileutils.ensure_tree(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg')) pxe_config_file_path = _get_pxe_config_file_path(node.uuid) pxe_config = _build_pxe_config(node, pxe_info, task.context) utils.write_to_file(pxe_config_file_path, pxe_config) for port in driver_utils.get_node_mac_addresses(task, node): mac_path = _get_pxe_mac_path(port) utils.unlink_without_raise(mac_path) utils.create_link_without_raise(pxe_config_file_path, mac_path)
def _link_ip_address_pxe_configs(task): """Link each IP address with the PXE configuration file. :param task: A TaskManager instance. :raises: FailedToGetIPAddressOnPort :raises: InvalidIPv4Address """ pxe_config_file_path = get_pxe_config_file_path(task.node.uuid) api = dhcp_factory.DHCPFactory().provider ip_addrs = api.get_ip_addresses(task) if not ip_addrs: raise exception.FailedToGetIPAddressOnPort( _("Failed to get IP address for any port on node %s.") % task.node.uuid) for port_ip_address in ip_addrs: ip_address_path = _get_pxe_ip_address_path(port_ip_address) utils.unlink_without_raise(ip_address_path) utils.create_link_without_raise(pxe_config_file_path, ip_address_path)
def _link_ip_address_pxe_configs(task): """Link each IP address with the PXE configuration file. :param task: A TaskManager instance. :raises: FailedToGetIPAddressOnPort :raises: InvalidIPv4Address """ pxe_config_file_path = get_pxe_config_file_path(task.node.uuid) api = dhcp_factory.DHCPFactory().provider ip_addrs = api.get_ip_addresses(task) if not ip_addrs: raise exception.FailedToGetIPAddressOnPort(_( "Failed to get IP address for any port on node %s.") % task.node.uuid) for port_ip_address in ip_addrs: ip_address_path = _get_pxe_ip_address_path(port_ip_address) utils.unlink_without_raise(ip_address_path) utils.create_link_without_raise(pxe_config_file_path, ip_address_path)
def test_create_link_EEXIST(self): with mock.patch.object(os, "symlink", autospec=True) as symlink_mock: symlink_mock.side_effect = OSError(errno.EEXIST) utils.create_link_without_raise("/fake/source", "/fake/link") symlink_mock.assert_called_once_with("/fake/source", "/fake/link")
def create_link(mac_path): ironic_utils.unlink_without_raise(mac_path) relative_source_path = os.path.relpath(pxe_config_file_path, os.path.dirname(mac_path)) utils.create_link_without_raise(relative_source_path, mac_path)
def test_create_link_EEXIST(self): with mock.patch.object(os, "symlink") as symlink_mock: symlink_mock.side_effect = OSError(errno.EEXIST) utils.create_link_without_raise("/fake/source", "/fake/link") symlink_mock.assert_called_once_with("/fake/source", "/fake/link")
def test_create_link(self): with mock.patch.object(os, "symlink") as symlink_mock: symlink_mock.return_value = None utils.create_link_without_raise("/fake/source", "/fake/link") symlink_mock.assert_called_once_with("/fake/source", "/fake/link")
def create_link(mac_path): utils.unlink_without_raise(mac_path) utils.create_link_without_raise(pxe_config_file_path, mac_path)
def create_link(mac_path): ironic_utils.unlink_without_raise(mac_path) relative_source_path = os.path.relpath( pxe_config_file_path, os.path.dirname(mac_path)) utils.create_link_without_raise(relative_source_path, mac_path)
def test_create_link(self): with mock.patch.object(os, "symlink", autospec=True) as symlink_mock: symlink_mock.return_value = None utils.create_link_without_raise("/fake/source", "/fake/link") symlink_mock.assert_called_once_with("/fake/source", "/fake/link")
def build_instance_info_for_deploy(task): """Build instance_info necessary for deploying to a node. :param task: a TaskManager object containing the node :returns: a dictionary containing the properties to be updated in instance_info :raises: exception.ImageRefValidationFailed if image_source is not Glance href and is not HTTP(S) URL. """ def validate_image_url(url, secret=False): """Validates image URL through the HEAD request. :param url: URL to be validated :param secret: if URL is secret (e.g. swift temp url), it will not be shown in logs. """ try: image_service.HttpImageService().validate_href(url, secret) except exception.ImageRefValidationFailed as e: with excutils.save_and_reraise_exception(): LOG.error( "Agent deploy supports only HTTP(S) URLs as " "instance_info['image_source'] or swift " "temporary URL. Either the specified URL is not " "a valid HTTP(S) URL or is not reachable " "for node %(node)s. Error: %(msg)s", { 'node': node.uuid, 'msg': e }) node = task.node instance_info = node.instance_info iwdi = node.driver_internal_info.get('is_whole_disk_image') image_source = instance_info['image_source'] if service_utils.is_glance_image(image_source): glance = image_service.GlanceImageService(context=task.context) image_info = glance.show(image_source) LOG.debug('Got image info: %(info)s for node %(node)s.', { 'info': image_info, 'node': node.uuid }) if CONF.agent.image_download_source == 'swift': swift_temp_url = glance.swift_temp_url(image_info) validate_image_url(swift_temp_url, secret=True) instance_info['image_url'] = swift_temp_url instance_info['image_checksum'] = image_info['checksum'] instance_info['image_disk_format'] = image_info['disk_format'] instance_info['image_os_hash_algo'] = image_info['os_hash_algo'] instance_info['image_os_hash_value'] = image_info['os_hash_value'] else: # Ironic cache and serve images from httpboot server force_raw = direct_deploy_should_convert_raw_image(node) _, image_path = cache_instance_image(task.context, node, force_raw=force_raw) if force_raw: instance_info['image_disk_format'] = 'raw' # Standard behavior is for image_checksum to be MD5, # so if the hash algorithm is None, then we will use # sha256. os_hash_algo = image_info.get('os_hash_algo') if os_hash_algo == 'md5': LOG.debug( 'Checksum calculation for image %(image)s is ' 'set to \'%(algo)s\', changing to \'sha256\'', { 'algo': os_hash_algo, 'image': image_path }) os_hash_algo = 'sha256' LOG.debug( 'Recalculating checksum for image %(image)s due to ' 'image conversion.', {'image': image_path}) instance_info['image_checksum'] = None hash_value = compute_image_checksum(image_path, os_hash_algo) instance_info['image_os_hash_algo'] = os_hash_algo instance_info['image_os_hash_value'] = hash_value else: instance_info['image_checksum'] = image_info['checksum'] instance_info['image_disk_format'] = image_info['disk_format'] instance_info['image_os_hash_algo'] = image_info[ 'os_hash_algo'] instance_info['image_os_hash_value'] = image_info[ 'os_hash_value'] # Create symlink and update image url symlink_dir = _get_http_image_symlink_dir_path() fileutils.ensure_tree(symlink_dir) symlink_path = _get_http_image_symlink_file_path(node.uuid) utils.create_link_without_raise(image_path, symlink_path) base_url = CONF.deploy.http_url if base_url.endswith('/'): base_url = base_url[:-1] http_image_url = '/'.join( [base_url, CONF.deploy.http_image_subdir, node.uuid]) validate_image_url(http_image_url, secret=True) instance_info['image_url'] = http_image_url instance_info['image_container_format'] = ( image_info['container_format']) instance_info['image_tags'] = image_info.get('tags', []) instance_info['image_properties'] = image_info['properties'] if not iwdi: instance_info['kernel'] = image_info['properties']['kernel_id'] instance_info['ramdisk'] = image_info['properties']['ramdisk_id'] else: validate_image_url(image_source) instance_info['image_url'] = image_source if not iwdi: instance_info['image_type'] = 'partition' i_info = parse_instance_info(node) instance_info.update(i_info) else: instance_info['image_type'] = 'whole-disk-image' return instance_info