def setUpClass(cls): '''Class-level set up''' tempdirname = tempfile.mkdtemp(dir="/tmp") cls.config_svcdirpath = config.AI_SERVICE_DIR_PATH config.AI_SERVICE_DIR_PATH = tempdirname # create service cls.imagedir = tempfile.mkdtemp(dir="/tmp") props = { config.PROP_SERVICE_NAME: 'mysvc', config.PROP_STATUS: 'off', config.PROP_TXT_RECORD: 'aiwebserver=myserver:5555', config.PROP_IMAGE_PATH: cls.imagedir } config.create_service_props('mysvc', props) cls.myservice = service.AIService('mysvc') # create alias props = { config.PROP_SERVICE_NAME: 'myalias', config.PROP_STATUS: 'off', config.PROP_TXT_RECORD: 'aiwebserver=myserver:5555', config.PROP_ALIAS_OF: 'mysvc' } config.create_service_props('myalias', props) cls.myalias = service.AIService('myalias')
def set_aliasof(options): '''Change a service's base service''' logging.debug("set alias %s's basesvc to %s", options.svcname, options.value) basesvcname = options.value aliasname = options.svcname if not config.is_service(basesvcname): raise SystemExit( _('\nError: Service does not exist: %s\n') % basesvcname) if aliasname == basesvcname: raise SystemExit( _('\nError: Alias name same as service name: %s\n') % aliasname) aliassvc = svc.AIService(aliasname) if not aliassvc.is_alias(): raise SystemExit( _('\nError: Service exists, but is not an ' 'alias: %s\n') % aliasname) basesvc_arch = svc.AIService(basesvcname).arch aliassvc_arch = aliassvc.arch if basesvc_arch != aliassvc_arch: raise SystemExit( _("\nError: Architectures of service and alias " "are different.\n")) if aliassvc.is_aliasof(basesvcname): raise SystemExit( _("\nError: %s is already an alias of %s\n") % (aliasname, basesvcname)) if svc.AIService(basesvcname).is_alias(): raise SystemExit(_("\nError: Cannot alias to another alias.\n")) # Make sure we aren't creating inter dependencies all_aliases = config.get_aliased_services(aliasname, recurse=True) if basesvcname in all_aliases: raise SystemExit( cw( _("\nError: %s can not be made an alias of %s " "because %s is dependent on %s\n") % (aliasname, basesvcname, basesvcname, aliasname))) try: aliassvc.update_basesvc(basesvcname) except (OSError, config.ServiceCfgError) as err: raise SystemExit( _("Failed to set 'aliasof' property of : %s") % aliasname) except svc.MultipleUnmountError as err: print >> sys.stderr, _("Failed to disable alias") raise SystemExit(err) except svc.MountError as err: print >> sys.stderr, _("Failed to enable alias") raise SystemExit(err) except svc.UnsupportedAliasError as err: raise SystemExit(err)
def set_aliasof(options): '''Change a service's base service''' logging.debug("set alias %s's basesvc to %s", options.svcname, options.value) basesvcname = options.value aliasname = options.svcname if not config.is_service(basesvcname): raise SystemExit(_('\nError: Service does not exist: %s\n') % basesvcname) if aliasname == basesvcname: raise SystemExit(_('\nError: Alias name same as service name: %s\n') % aliasname) aliassvc = svc.AIService(aliasname) if not aliassvc.is_alias(): raise SystemExit(_('\nError: Service exists, but is not an ' 'alias: %s\n') % aliasname) basesvc_arch = svc.AIService(basesvcname).arch aliassvc_arch = aliassvc.arch if basesvc_arch != aliassvc_arch: raise SystemExit(_("\nError: Architectures of service and alias " "are different.\n")) if aliassvc.is_aliasof(basesvcname): raise SystemExit(_("\nError: %(aliasname)s is already an alias " "of %(svcname)s\n") % {'aliasname': aliasname, 'svcname': basesvcname}) if svc.AIService(basesvcname).is_alias(): raise SystemExit(_("\nError: Cannot alias to another alias.\n")) # Make sure we aren't creating inter dependencies all_aliases = config.get_aliased_services(aliasname, recurse=True) if basesvcname in all_aliases: raise SystemExit(cw(_("\nError: %(aliasname)s can not be made an " "alias of %(svcname)s because %(svcname)s is " "dependent on %(aliasname)s\n") % {'aliasname': aliasname, 'svcname': basesvcname})) failures = do_update_basesvc(aliassvc, basesvcname) if failures: return 1 return 0
def do_create_client(cmd_options=None): '''Parse the user supplied arguments and create the specified client''' # check for authorization and euid try: check_auth_and_euid(CLIENT_AUTH) except UnauthorizedUserError as err: raise SystemExit(err) # parse server options options = parse_options(cmd_options) bootargs = '' if options.boot_args: bootargs = ",".join(options.boot_args).lstrip().rstrip() + "," logging.debug('bootargs=%s', bootargs) clientctrl.remove_client("01" + options.mac_address, suppress_dhcp_msgs=True) # wrap the whole program's execution to catch exceptions as we should not # throw them anywhere service = svc.AIService(options.service_name) try: create_new_client(options.arch, service, options.mac_address, bootargs) except (OSError, BootmgmtError, aismf.ServicesError, config.ServiceCfgError, svc.MountError) as err: raise SystemExit( _('\nError: Unable to create client, ' '%(mac)s:\n%(error)s') % { 'mac': options.mac_address, 'error': err })
def test_update_wanboot_imagepath(self): '''verify update_wanboot_imagepath updates imagepath correctly''' # create original wanboot.conf file wanboot_txt = ( "root_server=http://10.134.125.136:5555/cgi-bin/wanboot-cgi\n" "root_file=%(path)s/boot/platform/sun4v/boot_archive\n" "boot_file=%(path)s/platform/sun4v/wanboot\n" "system_conf=system.conf\n" "encryption_type=\n" "signature_type=\n" "server_authentication=no\n" "client_authentication=no=\n") % {'path': self.imagedir} wanbootconf = os.path.join(self.imagedir, service.WANBOOTCONF) with open(wanbootconf, 'w') as wfp: wfp.write(wanboot_txt) # create service props = {config.PROP_SERVICE_NAME: 'mysvc', config.PROP_STATUS: 'off', config.PROP_TXT_RECORD: 'aiwebserver=myserver:5555', config.PROP_IMAGE_PATH: self.imagedir} config.create_service_props('mysvc', props) myservice = service.AIService('mysvc') # Update path in wanboot file, read file back in, and # ensure file is updated properly. newpath = '/export/mydir/newpath' expected_text = wanboot_txt.replace(self.imagedir, newpath) myservice.update_wanboot_imagepath(self.imagedir, newpath) with open(wanbootconf, 'r') as wanboot_file: new_wanboot_txt = wanboot_file.read() self.assertEqual(new_wanboot_txt, expected_text)
def set_imagepath(options): '''Change the location of a service's image''' logging.debug("set %s imagepath to %s", options.svcname, options.value) new_imagepath = options.value.strip() service = svc.AIService(options.svcname) if service.is_alias(): raise SystemExit(cw(_('\nError: Can not change the imagepath of an ' 'alias.'))) if not os.path.isabs(new_imagepath): raise SystemExit(_("\nError: A full pathname is required for the " "imagepath.\n")) if os.path.exists(new_imagepath): raise SystemExit(_("\nError: The imagepath already exists: %s\n") % new_imagepath) if os.path.islink(new_imagepath): raise SystemExit(_("\nError: The imagepath may not be a symlink.\n")) new_imagepath = new_imagepath.rstrip('/') try: service.relocate_imagedir(new_imagepath) except (svc.MountError, aismf.ServicesError) as error: raise SystemExit(error)
def do_create_client(cmd_options=None): '''Parse the user supplied arguments and create the specified client''' # check that we are root if os.geteuid() != 0: raise SystemExit(_("Error: Root privileges are required for " "this command.")) # parse server options options = parse_options(cmd_options) bootargs = '' if options.boot_args: bootargs = ",".join(options.boot_args).lstrip().rstrip() + "," logging.debug('bootargs=%s', bootargs) clientctrl.remove_client("01" + options.mac_address) # wrap the whole program's execution to catch exceptions as we should not # throw them anywhere service = svc.AIService(options.service_name) try: create_new_client(options.arch, service, options.mac_address, bootargs) except OSError as err: raise SystemExit(err) except (aismf.ServicesError, config.ServiceCfgError, svc.MountError) as err: raise SystemExit(err)
def parse_options(cmd_options=None): '''Parse and validate options Args: Optional cmd_options, used for unit testing. Otherwise, cmd line options handled by OptionParser Returns: options object ''' usage = '\n' + get_usage() parser = OptionParser(usage=usage) parser.add_option('-s', '--source', dest='srcimage', help=_('FMRI')) parser.add_option( '-p', '--publisher', help=_("A pkg(5) publisher, in " "the form '<prefix>=<uri>', from which to update the " "image. Specified publisher becomes the sole publisher " "of the updated image.")) # Get the parsed arguments using parse_args() options, args = parser.parse_args(cmd_options) # Confirm service name was passed in if not args: parser.error(_("Missing required argument, <svcname>")) elif len(args) > 1: parser.error(_("Too many arguments: %s") % args) options.service_name = args[0] # ensure service exists if not config.is_service(options.service_name): raise SystemExit( _("\nError: The specified service does " "not exist: %s\n") % options.service_name) service = svc.AIService(options.service_name, image_class=InstalladmPkgImage) if not service.is_alias(): raise SystemExit( cw( _("Error: '%s' is not an alias. The target of " "'update-service' must be an alias.") % service.name)) options.service = service if options.publisher is not None: # Convert options.publisher from a string of form 'prefix=uri' to a # tuple (prefix, uri) and strip each entry publisher = map(str.strip, options.publisher.split("=")) if len(publisher) != 2: parser.error( _('Publisher information must match the form: ' '"<prefix>=<URI>"')) options.publisher = publisher logging.debug("options=%s", options) return options
def do_set_service_default_manifest(options): ''' Handle default_manifest property processing. ''' service = svc.AIService(options.svcname) try: service.set_default_manifest(options.value) except ValueError as error: raise SystemExit(error)
def do_update_service(cmd_options=None): '''Update a service - currently only an alias Copy the baseservice of an alias, update it, create a service using the updated image, and re-alias the alias to the new service. ''' # check for authorization and euid try: check_auth_and_euid(SERVICE_AUTH) except UnauthorizedUserError as err: raise SystemExit(err) options = parse_options(cmd_options) service = options.service try: if not service.image.is_pkg_based(): raise SystemExit( cw( _("\nError: '%s' is aliased to an iso based service. Only " "aliases of pkg(5) based services are updatable.") % options.service_name)) except pkg.client.api_errors.VersionException as err: print >> sys.stderr, cw( _("The IPS API version specified, %(specver)s," " is incompatible with the expected version, %(expver)s.") % { 'specver': str(err.received_version), 'expver': str(err.expected_version) }) raise SystemExit() fmri = [options.srcimage] if options.srcimage else None base_svc = svc.AIService(service.basesvc, image_class=InstalladmPkgImage) new_image = None try: print _("Copying image ...") new_image = base_svc.image.copy(prefix=base_svc.name) print _('Updating image ...') update_needed = new_image.update(fmri=fmri, publisher=options.publisher) except (ValueError, img.ImageError, pkg.client.api_errors.ApiException) as err: # clean up copied image if new_image: new_image.delete() print >> sys.stderr, cw( _("Attempting to update the service %s" " failed for the following reasons:") % service.name) if isinstance(err, pkg.client.api_errors.CatalogRefreshException): for pub, error in err.failed: print >> sys.stderr, " " print >> sys.stderr, str(error) if err.errmessage: print >> sys.stderr, err.errmessage raise SystemExit(err) if not update_needed: # No update needed, remove copied image new_image.delete() print _("No updates are available.") return 4 # Get the service name from the updated image metadata new_svcname = img.get_default_service_name(image=new_image) # Determine imagepath and move image there new_path = new_image.path new_imagepath = os.path.join(os.path.dirname(new_path), new_svcname) try: img.check_imagepath(new_imagepath) except ValueError as error: # Leave image in temp location rather than fail. User can # update imagepath later if desired. logging.debug('unable to move image from %s to %s: %s', new_path, new_imagepath, error) else: new_path = new_image.move(new_imagepath) logging.debug('image moved to %s', new_path) finally: img.set_permissions(new_path) # create new service based on updated image print _("Creating new %(arch)s service: %(newsvc)s") % \ {'arch': new_image.arch, 'newsvc': new_svcname} new_service = svc.AIService.create(new_svcname, new_image) # Register & enable service # (Also enables system/install/server, as needed) try: service.enable() except (config.ServiceCfgError, svc.MountError) as err: raise SystemExit(err) except aismf.ServicesError as err: # don't error out if the service is successfully created # but the services fail to start - just print out the error # and proceed print err print _("Aliasing %(alename)s to %(newsvc)s ...\n") % \ {'alename': service.name, 'newsvc': new_svcname} failures = setsvc.do_update_basesvc(service, new_service.name) if failures: return 1 return 0
def parse_options(cmd_options=None): ''' Parse and validate options ''' def check_MAC_address(option, opt_str, value, parser): ''' Check MAC address as an OptionParser callback Postcondition: sets value to proper option if check passes Raises: OptionValueError if MAC address is malformed ''' try: value = str(com.MACAddress(value)) except com.MACAddress.MACAddressError as err: raise OptionValueError(str(err)) setattr(parser.values, option.dest, value) usage = '\n' + get_usage() parser = OptionParser(usage=usage) # accept multiple -b options (so append to a list) parser.add_option("-b", "--boot-args", dest="boot_args", action="append", type="string", nargs=1, help=_("boot arguments to pass to Solaris kernel")) parser.add_option("-e", "--macaddr", dest="mac_address", action="callback", nargs=1, type="string", help=_("MAC address of client to add"), callback=check_MAC_address) parser.add_option("-n", "--service", dest="service_name", action="store", type="string", help=_("Service to associate client with"), nargs=1) (options, args) = parser.parse_args(cmd_options) if args: parser.error(_("Unexpected argument(s): %s" % args)) # check that we got a service name and mac address if options.service_name is None: parser.error( _("Service name is required " "(-n|--service <service name>).")) if options.mac_address is None: parser.error(_("MAC address is required (-e|--macaddr <macaddr>).")) # Verify that the server settings are not obviously broken. # These checks cannot be complete, but check for things which # will definitely cause failure. logging.debug("Calling %s", com.CHECK_SETUP_SCRIPT) ret = Popen([com.CHECK_SETUP_SCRIPT]).wait() if ret: raise SystemExit(1) # validate service name try: com.validate_service_name(options.service_name) except ValueError as err: raise SystemExit(err) # check that the service exists service_props = config.get_service_props(options.service_name) if not service_props: raise SystemExit( _("The specified service does not exist: %s\n") % options.service_name) # get the image_path from the service try: # set image to be a InstalladmImage object image = svc.AIService(options.service_name).image except KeyError: raise SystemExit( _("\nThe specified service does not have an " "image_path property.\n")) # ensure we are not passed bootargs for a SPARC as we do not # support that if options.boot_args and image.arch == "sparc": parser.error(_("Boot arguments not supported for SPARC clients.\n")) options.arch = image.arch logging.debug("options = %s", options) return options