16257 Support for zones configuration and installation should be included in AI
7041915 TransferFiles ICT should support transferring a directory that is more than one level deep.
7049824 System installed via AI ends up with incorrect mountpoints for shared ZFS datasets
#!/usr/bin/python
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
#
'''Transfer IPS checkpoint. Sub-class of the checkpoint class'''
import abc
import copy
import gettext
import locale
import os
import shutil
import sys
import pkg.client.api as api
import pkg.client.api_errors as api_errors
import pkg.client.image as image
import pkg.client.progress as progress
import pkg.client.publisher as publisher
import pkg.misc as misc
from pkg.client import global_settings
from pkg.client.api import IMG_TYPE_ENTIRE, IMG_TYPE_PARTIAL
from solaris_install.engine.checkpoint import AbstractCheckpoint as Checkpoint
from solaris_install.data_object import ObjectNotFoundError
from solaris_install.engine import InstallEngine
from solaris_install.transfer.info import Args
from solaris_install.transfer.info import Destination
from solaris_install.transfer.info import Facet
from solaris_install.transfer.info import Image
from solaris_install.transfer.info import ImType
from solaris_install.transfer.info import IPSSpec
from solaris_install.transfer.info import Mirror
from solaris_install.transfer.info import Origin
from solaris_install.transfer.info import Property
from solaris_install.transfer.info import Publisher
from solaris_install.transfer.info import Software
from solaris_install.transfer.info import Source
from solaris_install.transfer.info import ACTION, CONTENTS, \
PURGE_HISTORY, APP_CALLBACK, IPS_ARGS, UPDATE_INDEX
from solaris_install.transfer.prog import ProgressMon
PKG_CLIENT_NAME = "transfer module"
global_settings.client_name = PKG_CLIENT_NAME
misc.setlocale(locale.LC_ALL, "")
gettext.install("pkg", "/usr/share/locale")
class RedirectIPSTrans(object):
'''Helper class containing a file-like object that allows
the command line output from IPS pkg ProgressTracker
to be redirected to the logger defined for transfer.
'''
def __init__(self, trans_logger):
'''Initialize the logger and a variable
to hold the transmitted data
'''
self.trans_logger = trans_logger
self.data = ''
def write(self, data):
'''Write the data into the log file'''
self.data += data
# Once the output contains a line feed or
# carriage return it can be logged
if self.data.endswith("\r") or self.data.endswith("\n"):
self.flush()
def flush(self):
'''Print the data'''
if self.data.endswith("\r") or self.data.endswith("\n"):
# If the data ends with a return/line feed, strip it
# and add a space to compensate for any output that might
# get run together
self.data = self.data.rstrip('\r\n').ljust(1)
if self.data is not '':
self.trans_logger.debug(self.data)
self.data = ''
class AbstractIPS(Checkpoint):
'''Subclass for transfer IPS checkpoint'''
__metaclass__ = abc.ABCMeta
# Variables associated with the package image
CLIENT_API_VERSION = 58
DEF_REPO_URI = "http://pkg.opensolaris.org/release"
DEF_PROG_TRACKER = progress.CommandLineProgressTracker()
# Variables used in calculating the image size
DEFAULT_PROG_EST = 10
DEFAULT_SIZE = 1000
DEFAULT_PKG_NUM = 5
# Variables used to check system version info
SYSTEM_IMAGE = "/"
ENT_PKG = ["entire"]
SYSTEM_CLIENT_NAME = "host"
INFO_NEEDED = api.PackageInfo.ALL_OPTIONS
# Action variables
CREATE = "create"
EXISTING = "use_existing"
UPDATE = "update"
def __init__(self, name):
super(AbstractIPS, self).__init__(name)
# attributes per image
self.dst = None
self.img_action = self.CREATE
self.index = False
self.src = None # [(pub_name, [origin], [mirror])]
self.image_args = {}
self.completeness = IMG_TYPE_ENTIRE
self.is_zone = False
self.facets = {}
self.properties = {}
# To be used for progress reporting
self.distro_size = 0
self.give_progress = False
# Handle for the progress monitor
self.pmon = None
# handle for the ips api
self.api_inst = None
# Determines whether a dry run occurs
self.dry_run = False
# Flag to cancel whatever action is going on.
self._cancel_event = False
self.prog_tracker = self.DEF_PROG_TRACKER
# local attributes used to create the publisher.
self._publ = None
self._origin = []
self._mirror = []
self._add_publ = []
self._add_origin = []
self._add_mirror = []
self._image_args = {}
# publisher list to hold a reference between publishers and
# origins/mirrors
self.publisher_list = list()
# List to hold dictionaries of transfer actions
self._transfer_list = []
def get_size(self):
'''Compute the size of the transfer specified.'''
self._parse_input()
self._validate_input()
num_pkgs = 0
for trans in self._transfer_list:
num_pkgs += len(trans.get(CONTENTS))
return self.DEFAULT_SIZE * (num_pkgs / self.DEFAULT_PKG_NUM)
def get_progress_estimate(self):
'''Returns an estimate of the time this checkpoint will
take.
'''
if self.distro_size == 0:
self.distro_size = self.get_size()
progress_estimate = \
int((float(self.distro_size) / self.DEFAULT_SIZE) * \
self.DEFAULT_PROG_EST)
self.give_progress = True
return progress_estimate
def cancel(self):
'''Cancel the transfer in progress'''
self._cancel_event = True
if self.api_inst:
self.api_inst.cancel()
def execute(self, dry_run=False):
'''Execute method for the IPS checkpoint module. Will read the
input parameters and perform the specified transfer.
'''
self.logger.info("=== Executing %s Checkpoint ===" % self.name)
try:
if self.give_progress:
self.logger.report_progress("Beginning IPS transfer", 0)
self.dry_run = dry_run
# Read the parameters from the DOC and put into the
# local attributes.
self._parse_input()
# Validate the attributes
self._validate_input()
# Get the handle to the IPS image api.
if not self.dry_run:
self.get_ips_api_inst()
# Check to see that the entire package on the host system
# matches the package in the IPS repo, if the entire package
# is included in the package list. Log a message if the
# versions don't match.
# If it isn't found in the package list or on the system,
# this isn't an error. Continue with the installation.
for trans_val in self._transfer_list:
if trans_val.get(ACTION) == "install":
branchlist = []
version_dict = {}
for pkg in trans_val.get(CONTENTS):
if "entire" in pkg:
sysinst = api.ImageInterface(self.SYSTEM_IMAGE,
self.CLIENT_API_VERSION,
self.prog_tracker,
False,
self.SYSTEM_CLIENT_NAME)
version_dict["Installer"] = \
sysinst.info(fmri_strings=self.ENT_PKG,
local=True,
info_needed=self.INFO_NEEDED)
version_dict["Target"] = self.api_inst.info(
fmri_strings=self.ENT_PKG,
local=False,
info_needed=self.INFO_NEEDED)
for key, val in version_dict.items():
pkg_info = \
val[api.ImageInterface.INFO_FOUND]
for i, pi in enumerate(pkg_info):
if pi.branch not in branchlist:
branchlist.append(pi.branch)
version_dict[key] = pi.fmri
if len(branchlist) > 1:
self.logger.warning("Version mismatch: ")
for key, val in version_dict.items():
msg = "%s build version:%s" % (key,
val)
self.logger.warning(msg)
self.check_cancel_event()
# Perform the transferring of the bit/updating of the image.
self._transfer()
finally:
self._cleanup()
@abc.abstractmethod
def _parse_input(self):
'''This method is required to be implemented by all subclasses.'''
raise NotImplementedError
def _validate_input(self):
'''Do whatever validation is necessary on the attributes.'''
self.logger.debug("Validating IPS input")
# dst is required
if self.dst is None:
raise ValueError("IPS destination must be specified")
if self.img_action not in (self.EXISTING, self.CREATE, self.UPDATE):
raise ValueError("The IPS action is not valid: %s" %
self.img_action)
self.logger.debug("Destination: %s", self.dst)
self.logger.debug("Image action: %s", self.img_action)
if self.completeness == "full":
self.completeness = IMG_TYPE_ENTIRE
self.logger.debug("Image Type: full")
elif self.completeness == "partial":
self.completeness = IMG_TYPE_PARTIAL
self.logger.debug("Image Type: partial")
if self.is_zone:
self.logger.debug("Image Type: zone")
not_allowed = set(["prefix", "repo_uri", "origins", "mirrors"])
img_args = set(self.image_args.keys())
overlap = list(not_allowed & img_args)
if overlap:
raise ValueError("The following components may be specified "
"with the source component of the manifest but "
"are invalid as args: " + str(overlap))
self.prog_tracker = self.image_args.get("progtrack",
self.DEF_PROG_TRACKER)
# Set the image args we always want set.
if self.img_action == self.CREATE:
self.set_image_args()
def _transfer(self):
'''If an update of the image has been specified, the publishers of the
existing image are examined. If a matching publisher is found, the
origins and mirrors (if present) are reset to the desired entries.
All other publishers in the image are removed and the new additional
publishers are added. Then properties are set, if specified and the
transfer specific operations of install, uninstall and purge history
are performed.
If creation of the image has been specified, the additional
publishers are added to the image, properties are set and the
transfer specific operations of install, uninstall and purge history
are performed.
'''
if self.give_progress and self.distro_size != 0:
# Start up the ProgressMon to report progress
# while the actual transfer is taking place.
self.pmon = ProgressMon(logger=self.logger)
self.pmon.startmonitor(self.dst, self.distro_size, 0, 100)
if self.img_action == self.EXISTING and self._publ \
and not self.dry_run:
# See what publishers/origins/mirrors we have
self.logger.debug("Updating the publishers")
pub_list = self.api_inst.get_publishers(duplicate=True)
self.logger.info("Setting post-install publishers to:")
self.print_repository_uris()
# Look to see if the publisher in _publ is already in the Image
if self.api_inst.has_publisher(prefix=self._publ):
# update the publisher
pub = self.api_inst.get_publisher(prefix=self._publ,
duplicate=True)
self.logger.debug("Updating publisher information for " \
"%s" % str(self._publ))
repository = pub.repository
repository.reset_origins()
repository.reset_mirrors()
for origin in self._origin:
repository.add_origin(origin)
if self._mirror is not None:
for mirror in self._mirror:
repository.add_mirror(mirror)
self.api_inst.update_publisher(pub=pub, refresh_allowed=False,
search_first=True)
else:
# create a new publisher and set it to the highest ranked
# publisher
if self._mirror:
repo = publisher.Repository(mirrors=self._mirror,
origins=self._origin)
else:
repo = publisher.Repository(origins=self._origin)
pub = publisher.Publisher(prefix=self._publ, repository=repo)
self.api_inst.add_publisher(pub=pub, refresh_allowed=False,
search_first=True)
# the highest ranking publisher has been set. Walk the other
# publishers in the list and remove them.
for pub in self.api_inst.get_publishers(duplicate=True)[1:]:
self.api_inst.remove_publisher(prefix=pub.prefix)
# Add specified publishers/origins/mirrors to the image.
for idx, element in enumerate(self._add_publ):
# If this publisher doesn't already exist, add it.
if not self.api_inst.has_publisher(prefix=element):
self.logger.debug("Adding additional publisher %s" % \
str(element))
if self._add_mirror[idx]:
repo = publisher.Repository(mirrors=self._add_mirror[idx],
origins=self._add_origin[idx])
else:
repo = publisher.Repository(origins=self._add_origin[idx])
pub = publisher.Publisher(prefix=element, repository=repo)
if not self.dry_run:
self.api_inst.add_publisher(pub=pub, refresh_allowed=False)
# Else update the existing publisher with this spec
else:
self.logger.debug("Updating publisher information for " \
"%s" % str(element))
pub = self.api_inst.get_publisher(prefix=element,
duplicate=True)
repository = pub.repository
if self._add_origin[idx]:
for origin in self._add_origin[idx]:
if not repository.has_origin(origin):
repository.add_origin(origin)
if self._add_mirror[idx]:
for mirror in self._add_mirror[idx]:
if not repository.has_mirror(mirror):
repository.add_mirror(mirror)
self.api_inst.update_publisher(pub=pub, refresh_allowed=False)
# Get the publisher information of what the image is set with now.
# Re-set publisher_list to that list, so that it can be used later
# to be printed out
pub_list = self.api_inst.get_publishers(duplicate=True)
pub_list_for_print = list()
for pub in pub_list:
repo = pub.repository
origin_uris = list()
mirror_uris = list()
if repo.origins:
for origin in repo.origins:
origin_uris.append(origin.uri)
if repo.mirrors:
for mirror in repo.mirrors:
mirror_uris.append(mirror.uri)
pub_list_for_print.append((pub.prefix, origin_uris, mirror_uris))
self.publisher_list = pub_list_for_print
if self.dry_run:
self.logger.debug("Dry Run: publishers updated")
self.check_cancel_event()
if self.properties and not self.dry_run:
# Update properties if needed.
self.logger.debug("Updating image properties")
img = image.Image(root=self.dst, user_provided_dir=True)
for prop in self.properties.keys():
if prop == "preferred-publisher":
# Can't set preferred-publisher via set_property, you
# must use set_preferred_publisher
img.set_highest_ranked_publisher(
prefix=self.properties[prop])
else:
if isinstance(self.properties[prop], bool):
self.properties[prop] = str(self.properties[prop])
img.set_property(prop, self.properties[prop])
# Perform the transfer specific operations.
for trans_val in self._transfer_list:
if trans_val.get(ACTION) == "install":
self.check_cancel_event()
callback = trans_val.get(APP_CALLBACK)
if trans_val.get(CONTENTS):
self.logger.info("Installing packages from:")
self.print_repository_uris()
if not self.dry_run:
# Install packages
if trans_val.get(IPS_ARGS):
self.api_inst.plan_install(
pkg_list=trans_val.get(CONTENTS),
**trans_val.get(IPS_ARGS))
else:
self.api_inst.plan_install(
pkg_list=trans_val.get(CONTENTS))
if callback:
# execute the callback function passing it
# the api object we are using to perform the
# transfer to perform additional inspection or
# deal with things such as license acceptance.
callback(self.api_inst)
plan = self.api_inst.describe()
for pfmri, src, dest, accepted, displayed \
in plan.get_licenses():
if not dest.must_accept:
continue
self.api_inst.set_plan_license_status(pfmri,
dest.license, displayed=True, accepted=True)
# Redirect stdout and stderr from the pkg image in
# order to capture the command line output from the
# pkg progress tracker into the transfer logs.
tmp_stdout = sys.stdout
tmp_stderr = sys.stderr
sys.stdout = sys.stderr = RedirectIPSTrans(self.logger)
# Execute the transfer action
self.api_inst.prepare()
self.api_inst.execute_plan()
self.api_inst.reset()
# The above call will end up leaving our process's cwd
# in the image's root area, which will cause pain later
# in trying to unmount the image. So we manually
# change dir back to "/".
os.chdir("/")
# Release stdout and stderr
sys.stdout = tmp_stdout
sys.stderr = tmp_stderr
else:
self.logger.debug("Dry Run: Installing packages")
elif trans_val.get(ACTION) == "uninstall":
self.logger.debug("Uninstalling packages")
if not self.dry_run:
# Uninstall packages
if trans_val.get(IPS_ARGS):
if "recursive_removal" in trans_val.get(IPS_ARGS):
self.api_inst.plan_uninstall(
pkg_list=trans_val.get(CONTENTS),
recursive_removal=trans_val.get(
IPS_ARGS).pop(
"recursive_removal",
**trans_val.get(
IPS_ARGS)))
else:
self.api_inst.plan_uninstall(
pkg_list=trans_val.get(CONTENTS),
recursive_removal=False,
**trans_val.get(IPS_ARGS))
else:
self.api_inst.plan_uninstall(
pkg_list=trans_val.get(CONTENTS),
recursive_removal=False)
# Redirect stdout and stderr from the pkg image in order
# to capture the command line output from the pkg
# progress tracker into the transfer logs.
tmp_stdout = sys.stdout
tmp_stderr = sys.stderr
sys.stdout = sys.stderr = RedirectIPSTrans(self.logger)
# Execute the transfer action
self.api_inst.prepare()
self.api_inst.execute_plan()
self.api_inst.reset()
# Release stdout and stderr
sys.stdout = tmp_stdout
sys.stderr = tmp_stderr
else:
self.logger.debug("Dry Run: Uninstalling packages")
if trans_val.get(PURGE_HISTORY):
# purge history if requested.
self.logger.debug("Purging History")
if not self.dry_run:
img = image.Image(root=self.dst, user_provided_dir=True)
img.history.purge()
def check_cancel_event(self):
'''Check to see if a cancel event of the transfer is requested. If so,
cleanup changes made to the system and raise an exception.
'''
if self._cancel_event:
self._cleanup()
def _cleanup(self):
'''Method to perform any necessary cleanup needed.'''
self.logger.debug("Cleaning up")
if self.pmon:
self.pmon.done = True
self.pmon.wait()
self.pmon = None
def set_image_args(self):
'''Set the image args we need set because the information
was passed in via other attributes. These include progtrack,
prefix, repo_uri, origins, and mirrors. If we're creating a
zone image, we also need to set the use-system-repo property
in the props argument.
'''
self._image_args = copy.copy(self.image_args)
self._image_args["progtrack"] = self.prog_tracker
if self._publ and self._publ is not None:
self._image_args["prefix"] = self._publ
if self._origin and self._origin is not None:
self._image_args["repo_uri"] = self._origin[0]
if self._origin[1:]:
self._image_args["origins"] = self._origin[1:]
if self._mirror and self._mirror is not None:
self._image_args["mirrors"] = self._mirror
if self.is_zone:
if self._image_args.has_key("props"):
props_dict = self._image_args["props"]
props_dict["use-system-repo"] = True
else:
props_dict = {"use-system-repo": True}
self._image_args["props"] = props_dict
def get_ips_api_inst(self):
'''Get a handle to the api instance. If it is specified to use
the existing image grab the handle for the appropriate
image. If it's specified to create, remove any image that may
be located at the destination and create a new one there.
If any facets are specified, set them upon image creation.
'''
if self.img_action == self.EXISTING:
# An IPS image should exist and we are to use it.
self.logger.debug("Using existing image")
_img = image.Image(root=self.dst, user_provided_dir=True)
try:
self.api_inst = api.ImageInterface(self.dst,
self.CLIENT_API_VERSION, self.prog_tracker, None,
PKG_CLIENT_NAME)
except api_errors.VersionException, ips_err:
raise ValueError("The IPS API version specified, "
+ str(ips_err.received_version) +
" does not agree with "
"the expected version, "
+ str(ips_err.expected_version))
elif self.img_action == self.CREATE:
# Create a new image, destroy the old one if it exists.
self.logger.info("Creating IPS image")
if self.facets:
allow = {"TRUE": True, "FALSE": False}
for fname in self.facets.keys():
if not fname.startswith("facet."):
raise ValueError("Facet name, %s, must "
"begin with \"facet\"." % fname)
if not isinstance(self.facets[fname], bool):
if self.facets[fname].upper() not in allow:
raise ValueError("Facet argument must be "
"True|False")
self.facets[fname] = \
allow[self.facets[fname].upper()]
self._image_args.update({"facets": self.facets})
if os.path.exists(self.dst):
shutil.rmtree(self.dst, ignore_errors=True)
try:
self.api_inst = api.image_create(
pkg_client_name=PKG_CLIENT_NAME,
version_id=self.CLIENT_API_VERSION, root=self.dst,
imgtype=self.completeness, is_zone=self.is_zone,
force=True, **self._image_args)
# The above call will end up leaving our process's cwd in
# the image's root area, which will cause pain later on
# in tyring to unmount the image. So we manually change
# dir back to "/".
os.chdir("/")
except api_errors.VersionException, ips_err:
self.logger.exception("Error creating the IPS image")
raise ValueError("The IPS API version specified, "
+ str(ips_err.received_version) +
" does not agree with "
"the expected version, "
+ str(ips_err.expected_version))
def print_repository_uris(self):
'''print_repository_uris() - simple method to print out the
repository uris used
'''
indent = 4 * " "
indent2 = indent * 2
for publisher, origin_list, mirror_list in self.publisher_list:
self.logger.info(indent + publisher)
for origin in origin_list:
self.logger.info(indent2 + "origin: " + origin)
for mirror in mirror_list:
self.logger.info(indent2 + "mirror: " + mirror)
class TransferIPS(AbstractIPS):
'''IPS Transfer class to take input from the DOC. It implements the
checkpoint interface.
'''
VALUE_SEPARATOR = ","
def __init__(self, name):
super(TransferIPS, self).__init__(name)
# Holds the list of transfer dictionaries
self._transfer_list = []
# get the handle to the DOC
self._doc = InstallEngine.get_instance().data_object_cache
# Get the checkpoint info from the DOC
self.soft_list = self._doc.get_descendants(name=self.name,
class_type=Software)
# Add check that soft_list has only one entry
if len(self.soft_list) != 1:
raise ValueError("Only one value for Software node can be "
"specified with name " + self.name)
def _parse_input(self):
'''Method to read the parameters from the DOC and place
them into the local lists.
'''
self._publ = None
self._origin = []
self._mirror = []
self._add_publ = []
self._add_origin = []
self._add_mirror = []
self.logger.debug("Parsing the Data Object Cache")
soft_node = self.soft_list[0]
# Read the destination which for IPS involves reading the
# image node. The image node contains the action, img_root,
# and index. The image type node contains the completeness,
# and zone info.
dst_list = soft_node.get_children(Destination.DESTINATION_LABEL,
Destination, not_found_is_err=True)
dst_image = dst_list[0].get_children(Image.IMAGE_LABEL,
Image, not_found_is_err=True)[0]
self.dst = self._doc.str_replace_paths_refs(dst_image.img_root)
self.img_action = dst_image.action
self.index = dst_image.index
im_type = dst_image.get_first_child(ImType.IMTYPE_LABEL, ImType)
if im_type:
self.completeness = im_type.completeness
self.is_zone = bool(im_type.zone)
else:
self.completeness = "full"
self.is_zone = False
# Read properties to be set and put them into the properties
# dictionary.
prop_list = dst_image.get_children(Property.PROPERTY_LABEL,
Property)
for prop in prop_list:
self.properties[prop.prop_name] = prop.val
# Read the facets to be used in image creation and put them
# into the facet dictionary.
facet_list = dst_image.get_children(Facet.FACET_LABEL, Facet)
for facet in facet_list:
self.facets[facet.facet_name] = facet.val
# Parse the Source node
self._parse_src(soft_node)
# Get the IPS Image creations Args from the DOC if they exist.
img_arg_list = dst_image.get_children(Args.ARGS_LABEL, Args)
# If arguments were specified, validate that the
# user only specified them once, and that they
# didn't specify arguments they're not allowed to.
for args in img_arg_list:
# ssl_key and ssl_cert are part of the image specification.
# If the user has put them into the args that's an error
# since we wouldn't know which one to use if they were
# specified in both places.
not_allowed = set(["ssl_key", "ssl_cert"])
cur_img_args = set(args.arg_dict.keys())
overlap = list(not_allowed & cur_img_args)
if overlap:
raise ValueError("The following components may be specified "
"with the destination image of the manifest "
"but are invalid as args: %s" % str(overlap))
# Check that the current set of image args being processed
# are not duplicates of one we've already processed.
image_args = set(self.image_args.keys())
overlap = list(image_args & cur_img_args)
if overlap:
raise ValueError("The following components are specified "
"twice in the manifest: %s" % str(overlap))
# Update the image args with the current image args being processed.
self.image_args.update(args.arg_dict)
# Parse the transfer specific attributes.
self._parse_transfer_node(soft_node)
def _parse_transfer_node(self, soft_node):
'''Parse the DOC for the attributes that are specific for each
transfer specified. Create a list with the attributes for each
transfer.
'''
# Get the list of transfers from this specific node in the DOC
transfer_list = soft_node.get_children(class_type=IPSSpec)
for trans in transfer_list:
trans_attr = dict()
trans_attr[ACTION] = trans.action
trans_attr[CONTENTS] = trans.contents
trans_attr[PURGE_HISTORY] = trans.purge_history
trans_attr[APP_CALLBACK] = trans.app_callback
trans_args = trans.get_first_child(Args.ARGS_LABEL, Args)
if trans_args:
if not self.index:
trans_args.arg_dict[UPDATE_INDEX] = False
trans_attr[IPS_ARGS] = trans_args.arg_dict
else:
if not self.index:
trans_arg_dict = {UPDATE_INDEX: False}
trans_attr[IPS_ARGS] = trans_arg_dict
else:
trans_attr[IPS_ARGS] = None
# Append the information found to the list of
# transfers that will be performed
if trans_attr not in self._transfer_list:
self._transfer_list.append(trans_attr)
def _set_publisher_info(self, pub, preferred=False):
'''Set the preferred or additional publishers. Which publisher type to
set is determined by the boolean, preferred.
'''
if preferred:
self._publ = pub.publisher
if pub.publisher:
self.logger.debug("Primary Publisher Info: %s", pub.publisher)
else:
self.logger.debug("Primary Publisher Info:")
else:
self._add_publ.append(pub.publisher)
if pub.publisher:
self.logger.debug("Additional Publisher Info: %s",
pub.publisher)
else:
self.logger.debug("Additional Publisher Info: ")
origin_name = []
origin_uris = []
# Get the origins for this publisher. If one isn't specified,
# use the default origin.
origin_list = pub.get_children(Origin.ORIGIN_LABEL, Origin)
if origin_list:
for origin in origin_list:
if preferred:
or_repo = origin.origin
else:
or_repo = publisher.RepositoryURI(uri=origin.origin)
origin_name.append(or_repo)
origin_uris.append(origin.origin)
self.logger.debug(" Origin Info: %s", origin.origin)
else:
origin_name = self.DEF_REPO_URI
origin_uris.append(self.DEF_REPO_URI)
# Get the mirrors for the publisher if they are specified.
mirror_name = []
mirror_uris = []
mirror_list = pub.get_children(Mirror.MIRROR_LABEL, Mirror)
for mirror in mirror_list:
mirror_uris.append(mirror.mirror)
if preferred:
mir_repo = mirror.mirror
else:
mir_repo = publisher.RepositoryURI(uri=mirror.mirror)
mirror_name.append(mir_repo)
self.logger.debug(" Mirror Info: %s", mirror.mirror)
if len(mirror_name) == 0:
mirror_name = None
if preferred:
self._origin = origin_name
self._mirror = mirror_name
else:
self._add_origin.append(origin_name)
self._add_mirror.append(mirror_name)
# set the publisher_list for this publisher, only if it's not already
# been inserted.
entry = (pub.publisher, origin_uris, mirror_uris)
if entry not in self.publisher_list:
self.publisher_list.append(entry)
def _parse_src(self, soft_node):
'''Parse the DOC Source, filling in the local attributes for
_publ, _origin, _mirror, _add_publ, _add_origin, _add_mirror.
'''
self.logger.debug("Reading the IPS source")
src_list = soft_node.get_children(Source.SOURCE_LABEL, Source)
if len(src_list) > 1:
raise ValueError("Only one IPS image source may be specified")
if len(src_list) == 1:
src = src_list[0]
pub_list = src.get_children(Publisher.PUBLISHER_LABEL, Publisher,
not_found_is_err=True)
# If we're not installing a zone image, the first publisher is
# treated as the preferred one (i.e. it's passed as arguments to
# the image_create() call); for a zone, all publishers should
# be processed as additional publishers since a zone image will
# be created with the system repository already in place.
if not self.is_zone:
pub = pub_list.pop(0)
self._set_publisher_info(pub, preferred=True)
for pub in pub_list:
self._set_publisher_info(pub, preferred=False)
else:
if self.img_action != self.EXISTING:
# If the source isn't specified, use the defaults for create.
# For a zone image, the system repo will already be set up
# as the default repo so we don't need to set up a the default
# here if source isn't specified.
if not self.is_zone:
self._origin = [self.DEF_REPO_URI]
self.logger.debug(" Origin Info: %s", self.DEF_REPO_URI)
self._mirror = None
class TransferIPSAttr(AbstractIPS):
'''IPS Transfer class to take input from the attributes.'''
def __init__(self, name):
super(TransferIPSAttr, self).__init__(name)
# Attributes per transfer
self.action = None
self.contents = None
self.purge_history = False
self.app_callback = None
self.args = {}
self._transfer_list = []
def _parse_input(self):
'''Parse the source and transfer specific attributes and put
into local versions. The attributes to be parsed are:
src (for publisher, origin, mirror), pkg_install, pkg_uninstall,
purge_history, and args.
'''
self._publ = None
self._origin = []
self._mirror = []
self._add_publ = []
self._add_origin = []
self._add_mirror = []
trans_attr = dict()
self.logger.debug("Reading the Source")
if self.src:
self._publ, self._origin, self._mirror = self.src[0]
self.logger.debug("Source Info:")
if self._publ:
self.logger.debug("Primary Publisher name: %s",
str(self._publ))
for origin in self._origin:
self.logger.debug(" Origin name: %s", str(origin))
if self._mirror:
for mirror in self._mirror:
self.logger.debug(" Mirror name: %s", str(mirror))
for pub in self.src[1:]:
pub_name, origin_lst, mirror_lst = pub
if pub_name:
self.logger.debug("Additional publisher: %s",
str(pub_name))
origin_name = []
if origin_lst:
for origin in origin_lst:
or_repo = publisher.RepositoryURI(uri=origin)
origin_name.append(or_repo)
self.logger.debug(" Origin name: %s", str(origin))
else:
origin_name = [self.DEF_REPO_URI]
self.logger.debug(" Origin name: %s",
str(self.DEF_REPO_URI))
mirror_name = []
if mirror_lst:
for mirror in mirror_lst:
mir_repo = publisher.RepositoryURI(uri=mirror)
mirror_name.append(mir_repo)
self.logger.debug(" Mirror name: %s", str(mirror))
else:
mirror_name = None
self._add_publ.append(pub_name)
self._add_origin.append(origin_name)
self._add_mirror.append(mirror_name)
elif self.img_action != self.EXISTING:
# The source isn't specified so use the default.
self._origin = [self.DEF_REPO_URI]
self.logger.debug("Origin name: %s", str(self.DEF_REPO_URI))
self._mirror = None
trans_attr[ACTION] = self.action
trans_attr[CONTENTS] = self.contents
trans_attr[PURGE_HISTORY] = self.purge_history
trans_attr[APP_CALLBACK] = self.app_callback
if not self.index:
self.args[UPDATE_INDEX] = False
trans_attr[IPS_ARGS] = self.args
self._transfer_list.append(trans_attr)