def delete_profiles(profs, dbo, table):
    ''' deletes all database entries matching user's command line options
    Args:
        profs - list of profiles to delete by name
        dbo - database object
        table - database table name
    Returns: True if any errors encountered, False otherwise
    Exceptions: none
    '''
    # if any serious errors encountered, set exit status
    has_errors = False
    queue = dbo.getQueue()
    # Build a list of criteria for WHERE clause
    db_cols = [u'rowid'] + [u'file']
    # loop through all profiles from command line and delete them
    for profile_name in profs:
        query_str = "SELECT " + ", ".join(db_cols) + " FROM " + table + \
                " WHERE name=" + AIdb.format_value('name', profile_name)
        logging.debug("query=" + query_str)
        query = AIdb.DBrequest(query_str, commit=True)
        queue.put(query)
        query.waitAns()
        # check response, if failure, getResponse prints error
        rsp = query.getResponse()
        if rsp is None:
            has_errors = True
            continue
        if len(rsp) == 0:
            print >> sys.stderr, _("\tProfile %s not found.") % profile_name
            has_errors = True
            continue
        # delete database record and any accompanying internal profile file
        for response in rsp:
            deldict = dict()
            iresponse = iter(response)
            for crit in db_cols:
                deldict[crit] = next(iresponse)
            query_str = "DELETE FROM %s WHERE rowid=%d" % \
                    (table, deldict['rowid'])
            delquery = AIdb.DBrequest(query_str, commit=True)
            queue.put(delquery)
            delquery.waitAns()
            # check response, if failure, getResponse prints error
            if delquery.getResponse() is None:
                has_errors = True
                continue
            print >> sys.stderr, _("\tDeleted profile %s.") % profile_name
            # delete static (internal) files only
            if deldict['file'] is None or \
                not deldict['file'].startswith(sc.INTERNAL_PROFILE_DIRECTORY):
                continue
            try:
                os.unlink(deldict['file'])
            except OSError, (errno, errmsg):
                if errno != ENOENT:  # does not exist
                    print >> sys.stderr, _(
                            "Error (%s):  Problem deleting %s (%s): %s") \
                            % (errno, profile_name, deldict['file'], errmsg)
                has_errors = True
                continue
def parse_options(cmd_options=None):
    ''' Parse and validate options
    Args: options handled by OptionParser
    Returns: options
    '''
    parser = OptionParser(usage='\n' + get_usage())

    parser.add_option("-p", "--profile", dest="profile_name", action="append",
                      default=list(), help=_("Name of profile"))
    parser.add_option("-n", "--service", dest="service_name", default='',
                      help=_("Name of install service."))
    # Get the parsed options using parse_args().  We know we don't have
    # args, so check to make sure there are none.
    options, args = parser.parse_args(cmd_options)

    if not options.profile_name or not options.service_name:
        parser.error(_("Both -p|--profile and -n|--service are required."))
    if len(args):
        parser.error(_("Unexpected argument(s): %s" % args))

    try:
        validate_service_name(options.service_name)
    except ValueError as err:
        parser.error(err)

    return options
    def verifyDBStructure(self):
        '''Ensures reasonable DB schema and columns or else
        raises a SystemExit
        '''
        # get the names of each table in the database
        query = DBrequest("SELECT * FROM SQLITE_MASTER")
        self._requests.put(query)
        query.waitAns()

        # if query fails, getResponse prints error message
        if query.getResponse() is None:
            raise SystemExit(1)

        # iterate over each table in the database
        for row in iter(query.getResponse()):
            if "manifests" == row['tbl_name']:
                break
        # if we do not break out we do not have a manifest table
        else:
            raise SystemExit(_("Error:\tNo manifests table"))
        # iterate over each column of the manifests table
        query = DBrequest("PRAGMA table_info(manifests)")
        self._requests.put(query)
        query.waitAns()

        # gather column names in a list
        columns = list()
        for col in iter(query.getResponse()):
            columns.append(col['name'])

        # ensure we have a name, instance and at least one criteria column
        if "name" not in columns or "instance" not in columns or \
            len(columns) < 3:
            raise SystemExit(_("Error:\tDatabase columns appear malformed"))
def criteria_to_dict(criteria):
    """
    Convert criteria list into dictionary. This function is intended to be
    called by a main function, or the options parser, so it can potentially
    raise the SystemExit exception.
    Args: criteria in list format: [ criteria=value, criteria=value, ... ]
          where value can be a:  single string value
                                 space-separated string value (list of values)
                                 range (<lower>-<upper>)
    Returns: dictionary of criteria { criteria: value, criteria: value, ... }
             with all keys in lower case, values are case-sensitive.
    Raises: ValueError on malformed name=value strings in input list.
    """
    cri_dict = dict()
    for entry in criteria:
        entries = entry.partition("=")

        if entries[1]:
            if not entries[0]:
                raise ValueError(_("Missing criteria name in "
                                   "'%s'\n") % entry)
            elif entries[0].lower() in cri_dict:
                raise ValueError(_("Duplicate criteria: '%s'\n") %
                             entries[0])
            elif not entries[2]:
                raise ValueError(_("Missing value for criteria "
                                   "'%s'\n") % entries[0])
            cri_dict[entries[0].lower()] = entries[2]
        else:
            raise ValueError(_("Criteria must be of the form "
                               "<criteria>=<value>\n"))

    return cri_dict
def check_published_manifest(service, dbn, manifest_name):
    """
    Used for checking that a manifest is already published in the
    install service specified.  Checks to make sure manifest
    exists in the install service's DB, and that the manifest also
    exists in the install service's published files area.
    Args:
          service - service object for service
          dbn - dbn object of install service to check against.
          manifest_name - name of manifest to check.
    Postconditions: None
    Returns: True if manifest exists in install service
             False if manifest does not exist.
    """

    # Check if manifest exists in the service's criteria DB.
    if AIdb.sanitizeSQL(manifest_name) not in AIdb.getManNames(dbn.getQueue()):
        print(_("Error: install service does not contain the specified "
                "manifest: %s") % manifest_name)
        return False

    # Check if manifest file exists in the service's published area.
    published_path = os.path.join(service.manifest_dir, manifest_name)

    if not os.path.exists(published_path):
        print(_("Error: manifest missing from published area: %s") %
                published_path)
        return False

    return True
def do_update_manifest(cmd_options=None):
    '''
    Update the contents of an existing manifest.

    '''
    # check that we are root
    if os.geteuid() != 0:
        raise SystemExit(_("Error:\tRoot privileges are required for "
                           "this command."))

    # load in all the options and file data.  Validate proper manifests.
    data = parse_options(DO_UPDATE, cmd_options)

    service = AIService(data.service_name)
    manifest_path = os.path.join(service.manifest_dir, data.manifest_name)

    if not os.path.exists(manifest_path):
        raise SystemExit(_("Error:\tNo manifest or script with name "
                           "%s is registered with this service.\n"
                           "\tPlease use installadm "
                           "create-manifest instead.") %
                           data.manifest_name)

    # move the manifest into place
    df.place_manifest(data, manifest_path)
 def verify_SC_manifest(self, data, name=None):
     """
     Used for verifying and loading SC manifest
     Args:    data - file path, or StringIO object.
              name - Optionally, takes a name to provide error output,
                     as a StringIO object will not have a file path to
                     provide.
     Returns: Provide an XML DOM for the SC manifest
     Raises:  SystemExit on validation or file open error.
     """
     if not isinstance(data, StringIO.StringIO):
         try:
             data = file(data, 'r')
         except IOError:
             if name is None:
                 raise SystemExit(_("Error:\tCan not open: %s") % data)
             else:
                 raise SystemExit(_("Error:\tCan not open: %s") % name)
     xml_root, errors = verifyXML.verifyDTDManifest(self.smfDtd, data)
     if errors:
         if not isinstance(data, StringIO.StringIO):
             print >> sys.stderr, (_("Error:\tFile %s failed validation:") %
                                   data.name)
         else:
             print >> sys.stderr, (_("Error:\tSC Manifest %s failed "
                                     "validation:") % name)
         for err in xml_root:
             print >> sys.stderr, err
         raise SystemExit()
     return(xml_root)
def check_imagepath(imagepath):
    '''
    Check if image path exists.  If it exists, check whether it has
    a valid net image. An empty dir is ok.

    Raises: ValueError if a problem exists with imagepath

    '''
    # imagepath must be a full path
    if not os.path.isabs(imagepath):
        raise ValueError(_("\nA full pathname is required for the "
                           "image path.\n"))

    # imagepath must not exist, or must be empty
    if os.path.exists(imagepath):
        try:
            dirlist = os.listdir(imagepath)
        except OSError as err:
            raise ValueError(err)
        
        if dirlist:
            if com.AI_NETIMAGE_REQUIRED_FILE in dirlist:
                raise ValueError(_("\nThere is a valid image at (%s)."
                                   "\nPlease delete the image and try "
                                   "again.\n") % imagepath)
            else:
                raise ValueError(_("\nTarget directory is not empty: %s\n") %
                                 imagepath)
def validate_file(profile_name, profile, image_dir=None, verbose=True):
    '''validate a profile file, given the file path and profile name
    Args:
        profile_name - reference name of profile
        profile - file path of profile
        image_dir - path of service image, used to locate service_bundle
        verbose - boolean, True if verbosity desired, False otherwise
    
    Return: Raw profile in string format if it is valid, None otherwise.

    '''
    if verbose:
        print >> sys.stderr, (_("Validating static profile %s...") %
                              profile_name)
    try:
        with open(profile, 'r') as fip:
            raw_profile = fip.read()
    except IOError as strerror:
        print >> sys.stderr, _("Error opening profile %s:  %s") % \
                (profile_name, strerror)
        return 

    errmsg = ''
    validated_xml = ''
    tmpl_profile = None
    try:
        # do any templating
        tmpl_profile = sc.perform_templating(raw_profile)
        # validate
        validated_xml = sc.validate_profile_string(tmpl_profile, image_dir,
                                                   dtd_validation=True,
                                                   warn_if_dtd_missing=True)
    except lxml.etree.XMLSyntaxError, err:
        errmsg = _('XML syntax error in profile %s:' % profile_name)
    def set_AI_schema(self, manifest_file=None):
        """
        Sets self._AIschema and errors if imagepath not yet set.
        Args: None
        Raises: SystemExit if unable to find a valid AI schema
        Returns: None
        """

        # If manifest is provided, it must have a DOCTYPE string that
        # references a DTD.
        if not os.path.exists(manifest_file):
            raise SystemExit(_("Error: Cannot access Manifest \"%s\"." %
                manifest_file))

        # Try first to get schema basename from DOCINFO in XML
        schema_basename = None
        try:
            manifest_doc = lxml.etree.parse(manifest_file)
            system_url = manifest_doc.docinfo.system_url
            if system_url is not None:
                schema_basename = os.path.basename(system_url)
            else:
                raise SystemExit(_("Error: manifest must have a DOCTYPE string"
                                   " with DTD reference."))
        except lxml.etree.XMLSyntaxError, syntax_error:
            raise SystemExit(_("Error: There was a syntax error parsing the "
                               "manifest %(mf)s:\n  %(error)s") %
                               {'mf': manifest_file,
                                'error': str(syntax_error)})
def remove_client_dhcp_config(client_id):
    '''
    If a local DHCP server is running, remove any client configuration for
    this client from its configuration. If not, inform end-user that the
    client-service binding should no longer be referenced in the DHCP
    configuration.
    '''
    server = dhcp.DHCPServer()
    if server.is_configured():
        # A local DHCP server is configured. Check for a host entry and remove
        # it if found.
        mac_address = client_id[2:]
        mac_address = AIdb.formatValue('mac', mac_address)
        if server.host_is_configured(mac_address):
            print cw(_("Removing host entry '%s' from local DHCP "
                       "configuration.") % mac_address)
            server.remove_host(mac_address)

            if server.is_online():
                try:
                    server.control('restart')
                except dhcp.DHCPServerError as err:
                    print >> sys.stderr, cw(_("Unable to restart the DHCP "
                                              "SMF service: %s" % err))
                    return
    else:
        # No local DHCP configuration, inform user that it needs to be
        # unconfigured elsewhere.
        print cw(_("No local DHCP configuration found. Unless it will be "
                   "reused, the bootfile '%s' may be removed from the DHCP "
                   "configuration\n" % client_id))
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_publish_manifest(cmd_options=None):
    '''
    Publish a manifest, associating it with an install service.

    '''
    # check that we are root
    if os.geteuid() != 0:
        raise SystemExit(_("Error:\tRoot privileges are required for "
                           "this command."))

    # load in all the options and file data.  Validate proper manifests.
    data = parse_options(DO_CREATE, cmd_options)

    service = AIService(data.service_name)
    # Disallow multiple manifests or scripts with the same mname.
    manifest_path = os.path.join(service.manifest_dir, data.manifest_name)
    if os.path.exists(manifest_path):
        raise SystemExit(_("Error:\tName %s is already registered with "
                           "this service.") % data.manifest_name)

    # if criteria are provided, make sure they are a unique set.
    if data.criteria:
        find_colliding_manifests(data.criteria, data.database,
            find_colliding_criteria(data.criteria, data.database))

    # Add all manifests to the database, whether default or not, and whether
    # they have criteria or not.
    df.insert_SQL(data)

    # move the manifest into place
    df.place_manifest(data, manifest_path)

    # if we have a default manifest do default manifest handling
    if data.set_as_default:
        service.set_default_manifest(data.manifest_name)
Пример #14
0
def do_export_manifest(options):
    '''
    Export a manifest.
    '''
    save_errno = 0

    for mname in options.mnames:
        # Get the pathname of the manifest to export.
        input_mname = os.path.join(options.service.manifest_dir, mname)

        if options.output_isdir:
            output_name = "/".join([options.output_name, mname])
        else:
            output_name = options.output_name
        if output_name == SCREEN and options.file_count > 1:
            display_file_header(_("manifest: ") + mname)

        # Find the file in the directory and copy it to screen or file.
        try:
            shutil.copyfile(input_mname, output_name)
        except IOError as err:
            print >> sys.stderr, _("Error exporting manifest: "
                                   "%(error)s: %(file)s") % (
                                   {"error": err.strerror,
                                    "file": err.filename})
            save_errno = err.errno
        print
    return save_errno
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: tuple consisting of svcname and newsvcname
    """

    usage = '\n' + get_usage()

    parser = OptionParser(usage=usage)

    # Get the parsed arguments using parse_args()
    unused, args = parser.parse_args(cmd_options)

    if len(args) < 2:
        parser.error(_("Missing one or more required arguments."))
    elif len(args) > 2:
        parser.error(_("Too many arguments: %s" % args))

    svcname = args[0]
    newsvcname = args[1]

    # validate service names
    try:
        validate_service_name(newsvcname)
    except ValueError as err:
        parser.error(err)

    logging.debug("Renaming %s to %s", svcname, newsvcname)

    return (svcname, newsvcname)
def do_set_service(cmd_options=None):
    '''
    Set a property of a service
    '''
    # check that we are root
    if os.geteuid() != 0:
        raise SystemExit(_("Error: Root privileges are required for this "
                           "command."))

    options = parse_options(cmd_options)

    # validate service name
    try:
        validate_service_name(options.svcname)
    except ValueError as err:
        raise SystemExit(str(err))

    logging.debug("options %s", options)

    if not config.is_service(options.svcname):
        raise SystemExit(_('\nError: Service does not exist: %s\n') %
                         options.svcname)

    if options.prop == "default-manifest":
        do_set_service_default_manifest(options)
    elif options.prop == "aliasof":
        return set_aliasof(options)
    elif options.prop == "imagepath":
        return set_imagepath(options)
    def _register_a_service(self, name, interfaces=None, port=0,
                            comments=None):
        '''Method: _register_a_service, private to class

        Description:
            Register a single service on the interfaces

        Args
            interfaces - the interfaces to register the service on
            instance   - the SMF service instance handle
            name       - the service name to be registered
            port       - the port that the service is listening on, if
                         port is 0 then registering a service listed in
                         the AI SMF service instance.
            comments   - comments for the ad hoc registered service

        Returns
            list_sdrefs - list of service references

        Raises
            AImDNSError - if SMF status property does not exist, OR
                          if SMF txt_record property does not exist, OR
                          if SMF port property does not exist.
        '''
        if not self.register_initialized:
            self.exclude = libaimdns.getboolean_property(common.SRVINST,
                                                         common.EXCLPROP)
            self.networks = libaimdns.getstrings_property(common.SRVINST,
                                                          common.NETSPROP)
            self.register_initialized = True

        smf_port = None
        # if port is 0 then processing an AI service
        if port == 0:
            serv = config.get_service_props(name)
            if not serv:
                raise AIMDNSError(cw(_('error: aiMDNSError: no such '
                                       'installation service "%s"') % name))

            # ensure the service is enabled
            if config.PROP_STATUS not in serv:
                raise AIMDNSError(cw(_('error: aiMDNSError: installation '
                                       'service key "status" property does '
                                       'not exist')))

            if serv[config.PROP_STATUS] != config.STATUS_ON:
                print(cw(_('warning: Installation service "%s" is not enabled '
                           % name)))
                return None

            smf_port = config.get_service_port(name)
            if not smf_port:
                try:
                    smf_port = libaimdns.getinteger_property(common.SRVINST,
                                                             common.PORTPROP)
                    smf_port = str(smf_port)
                except libaimdns.aiMDNSError, err:
                    raise AIMDNSError(cw(_('error: aiMDNSError: port property '
                                           'failure (%s)') % err))
    def register(self, servicename=None, port=0, interfaces=None,
                 comments=None):
        '''Method: register
           Description:
                Registers an ad hoc service.  This method will loop until the
                the application is killed.

            Args
                servicename - the name of the ad hoc service
                port        - the port to use for the ad hoc service
                interfaces  - the interfaces to register the ad hoc service on
                comments    - the service comments for the ad hoc service

            Returns
                None

            Raises
                SystemError  - if the SMF service instance can not be loaded.
                AImDNSError  - if unable to register the service OR
                               if no servicename is present.
        '''
        self._do_lookup = False

        if servicename is not None:
            self.servicename = servicename

        if self.servicename is None:
            raise ValueError(_('must specify a service to register'))

        if self.verbose:
            print _('Registering "%s"...') % self.servicename

        # get the AI SMF service instance information
        try:
            self.instance = smf.AISCF(FMRI="system/install/server")
        except SystemError:
            raise SystemError(_("error: the system does not have the "
                                "system/install/server SMF service"))

        # use the interfaces within the class if none are passed in
        if interfaces is None:
            interfaces = self.interfaces

        sdrefs = self._register_a_service(name=self.servicename,
                                          interfaces=interfaces,
                                          port=port,
                                          comments=comments)

        if sdrefs is not None:
            self.sdrefs[servicename] = sdrefs
            self._handle_events()
        else:
            raise AIMDNSError(cw(_('error: aiMDNSError: mDNS ad hoc '
                                   'registration failed for "%s" service')
                                   % self.servicename))
def validate_profile_string(profile_str, image_dir=None, resolve_entities=True,
        dtd_validation=False, warn_if_dtd_missing=False):
    ''' Given the profile contained in a string variable, validate
    Args:
        profile_str - profile in string format
        image_dir - path of service image, used to locate service_bundle
            if None, only unit test against local service_bundle(4)
        resolve_entities - if True, ask XML parser to resolve all entities
        dtd_validation - if True, validate against a DTD in the profile
        warn_if_dtd_missing - if True, raise an exception if the DTD not found
    Returns: profile as string with any inclusions
    Exceptions: etree.XMLSyntaxError
    '''
    import lxml.etree as etree
    from StringIO import StringIO

    # create an XMLParser object with settings
    parser = etree.XMLParser(
        # always read DTD for XInclude namespace xi in service_bundle(4)
        load_dtd=True,
        resolve_entities=resolve_entities,
        dtd_validation=False
        )
    root = etree.parse(StringIO(profile_str), parser)
    if not resolve_entities:  # just check basic XML, no inclusions
        return profile_str
    # validate against DTD if provided
    if dtd_validation and \
            (root.docinfo.externalDTD is not None or
                    root.docinfo.internalDTD is not None):
        # check for service_bundle(4)
        if root.docinfo.system_url is not None and \
                root.docinfo.system_url.find('/service_bundle.dtd.') == -1:
            print >> sys.stderr, _(
                "Warning:  DOCTYPE %s specified instead of service_bundle(4). "
                "The file might not be a profile.") % root.docinfo.system_url
    if image_dir is None:  # unit testing only
        err = validate_profile_external_dtd(profile_str)
        if err:
            raise etree.XMLSyntaxError(err, '', '', '')
        return profile_str
    dtd_file = os.path.join(image_dir, 'auto_install', 'service_bundle.dtd.1')
    # if warning only on DTD missing, and DTD is indeed missing
    if root.docinfo.system_url is not None and warn_if_dtd_missing and \
        not os.path.exists(dtd_file):
        print >> sys.stderr, _(
            "Warning:  DTD %s not found.  Cannot validate completely.") % \
            dtd_file
        return etree.tostring(root)
    # parse, validating against external DTD
    err = validate_profile_external_dtd(profile_str, dtd_file)
    if err:
        raise etree.XMLSyntaxError(err, '', '', '')
    return profile_str
def send_needed_criteria(port):
    '''Replies to the old client with the needed criteria

    Args
        port - the originating port for the old client

    Returns
        None

    Raises
        None
    
    '''
    # Establish the service SQL database based upon the
    # port number for the service
    path = os.path.join(com.AI_SERVICE_DIR_PATH, str(port), 'AI.db')
    if os.path.exists(path):
        try:
            aisql = AIdb.DB(path)
            aisql.verifyDBStructure()
        except StandardError as err:
            # internal error, record the error in the server error_log
            sys.stderr.write(_('error:AI database access error\n%s\n') % err)
            # report the error to the requesting client
            print "Content-Type: text/html"    # HTML is following
            print                              # blank line, end of headers
            sys.stdout.write(_("error:AI database access error\n%s\n") % err)
            sys.exit(1)
    else:
        # not an internal error, report to the requesting client only
        print "Content-Type: text/html"    # HTML is following
        print                              # blank line, end of headers
        print _("Error:unable to determine criteria "
                "for service associated with port"), port
        return

    # build the required criteria list
    xml = lxml.etree.Element("CriteriaList")
    # old version number
    version_value = lxml.etree.Element("Version")
    version_value.attrib["Number"] = COMPATIBILITY_VERSION
    xml.append(version_value)
    # pull the required criteria from the SQL database
    for crit in AIdb.getCriteria(aisql.getQueue(), strip=True):
        tag = lxml.etree.Element("Criteria")
        tag.attrib["Name"] = crit
        xml.append(tag)
    xmlstr = lxml.etree.tostring(xml, pretty_print=True)

    # report the results
    print "Content-Length:", len(xmlstr)  # Length of XML reply
    print "Content-Type: text/xml"        # XML is following
    print                                 # blank line, end of headers
    print xmlstr
def do_alias_service(options):
    ''' Create an alias of a service

    '''
    # Ensure that the base service is a service
    if not config.is_service(options.aliasof):
        raise SystemExit(_("\nService does not exist: %s\n") % options.aliasof)

    basesvc = AIService(options.aliasof)

    # Ensure that the base service is not an alias
    if basesvc.is_alias():
        raise SystemExit(_("\nError: Cannot create alias of another alias.\n"))

    image = basesvc.image
    if options.svcname in DEFAULT_ARCH:
        if ((image.arch == 'sparc' and 'sparc' not in options.svcname) or
            (image.arch == 'i386' and 'i386' not in options.svcname)):
            raise SystemExit(cw(_("\nError: %s can not be an alias of a "
                                  "service with a different architecture.\n") %
                                  options.svcname))
 
    logging.debug("Creating alias of service %s", options.aliasof)

    print _("\nCreating %(arch)s alias: %(name)s\n") % \
            {'arch': image.arch,
             'name': options.svcname}

    logging.debug("Creating AIService aliasname %s base svc=%s, bootargs=%s",
                  options.svcname, options.aliasof, options.bootargs)
    try:
        service = AIService.create(options.svcname, image,
                                   alias=options.aliasof,
                                   bootargs=options.bootargs)
    except AIServiceError as err:
        raise SystemExit(err)

    # if recreating default-sparc alias, recreate symlinks
    if service.is_default_arch_service() and image.arch == 'sparc':
        logging.debug("Recreating default-sparc symlinks")
        service.do_default_sparc_symlinks(options.svcname)

    # Register & enable service
    # (Also enables system/install/server, as needed)
    try:
        service.enable()
    except (config.ServiceCfgError, 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
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 _register_callback(self, sdref, flags, errorcode, name,
                           regtype, domain):
        '''Method: _register_callback, private to class
           Description:
                DNS Callback for the registration process

            Args
                sdref       - service reference
                              standard argument for callback, not used
                flags       - flag to determine what action is taking place
                              standard argument for callback, not used
                errorcode   - flag to determine if a registration error
                              occurred
                name        - name of the service
                regtype     - registration type, should be _OSInstall._tcp.
                domain      - DNS domain, either local or remote

            Returns
                None

            Raises
                None
        '''
        # note: DNSService Errors are ignored here and handled elsewhere.
        if errorcode == pyb.kDNSServiceErr_NoError and \
           self.verbose:
            print _('Registered service:')
            print _('\tname    = %s') % name
            print _('\tregtype = %s') % regtype
            print _('\tdomain  = %s') % domain
Пример #24
0
def list_local_services(services, name=None):
    """
    Lists the local services for a host.  If name is not
    None then it prints only the named service.

    Args
        services = config.get_all_service_props()
        name = service name

    Returns
        None

    Raises
        None
    
    """
    sdict, width, awidth = get_local_services(services, sname=name)
    
    width = max(width, len(_('Service Name')))
    awidth = max(awidth, len(_('Alias Of')))
    fields = [[_('Service Name'), width]]
    fields.extend([[_('Alias Of'), awidth]])
    fields.extend([[_('Status'), FDICT['status']]])
    fields.extend([[_('Arch'), FDICT['arch']]])
    fields.extend([[_('Image Path'), len(_('Image Path'))]])

    do_header(fields)
    print_local_services(sdict, width, awidth)
Пример #25
0
def list_local_profiles(linst, name=None):
    """
    list the local profiles.  If name is not passed in then
    print all the local profiles.  Otherwise list the named
    service's profiles' criteria.

    Args
        inst = smf.AISCF()
        name = service name

    Returns
        None

    Raises
        None
    """
    # list -p
    if not name:
        sdict, swidth, mwidth, cwidth = \
                get_manifest_or_profile_names(linst, AIdb.PROFILES_TABLE)
        if not sdict:
            output = _('There are no profiles configured for local '
                       'services.\n')
            sys.stdout.write(output)
            return

        swidth = max(swidth, len(_('Service Name'))) + 1
        fields = [[_('Service Name'), swidth]]
        mwidth = max(mwidth, len(_('Profile')))
        fields.extend([[_('Profile'), mwidth]])

        do_header(fields)
        print_local_profiles(sdict, swidth)
    # list -p -n <service>
    else:
        sdict, mwidth, cwidth = \
            get_mfest_or_profile_criteria(name, linst, AIdb.PROFILES_TABLE)
        if not sdict:
            output = _('There are no profiles configured for local service, '
                       '"%s".\n') % name
            sys.stdout.write(output)
            return

        mwidth = max(mwidth, len(_('Profile'))) + 1
        fields = [[_('Profile'), mwidth]]
        fields.extend([[_('Criteria'), len(_('Criteria'))]])

        do_header(fields)
        print_service_profiles(sdict, mwidth, cwidth)
    def register_all(self, interfaces=None):
        '''Method: register_all
           Description:
                Registers all AI services.  This method will loop until the
                the application is killed.  It responds to SIGHUP signals,
                re-checking all registered services for additions and removals
                of a service from the AI SMF service.

            Args
                interfaces  - the interfaces to register the AI services on

            Returns
                None

            Raises
                SystemError  - if the SMF service instance can not be loaded.
                AIMDNSError  - if the Instance keys are not loaded.
        '''
        self._do_lookup = False

        if self.verbose:
            print _('Registering all Auto Install services...')

        # get the AI SMF service instance information
        try:
            self.instance = smf.AISCF(FMRI="system/install/server")
        except SystemError:
            raise SystemError(_("error: the system does not have the "
                                "system/install/server SMF service"))
        self.instance_services = config.get_all_service_names()

        # use interfaces within the class if none are passed
        if interfaces is None:
            interfaces = self.interfaces

        # iterate through each service and register it
        for servicename in self.instance_services:
            sdrefs = self._register_a_service(name=servicename,
                                              interfaces=interfaces)

            # save the service reference within the class
            if sdrefs:
                if servicename in self.sdrefs:
                    self.sdrefs[servicename].extend(sdrefs)
                else:
                    self.sdrefs[servicename] = sdrefs

        signal.signal(signal.SIGHUP, self._signal_hup)
        self._handle_events()
    def browse(self):
        '''Method: browse
        Description:
            browse all available _OSInstall._tcp services.

        Args
            None

        Returns
                True  -- if a service is found -- OR --
                False -- if a service is not found -- OR --
                sdref -- if actually in find mode

        Raises
            AImDNSError - if there are no service references available
        '''
        self.sdrefs = dict()
        self._found = False
        self._resolved = list()

        # only browse over the number of interfaces available
        self.count = len(self.interfaces)

        if self.verbose:
            print _('Browsing for services...')

        # pybonjour bug -- can not Browse on a specific interfaceIndex
        # thus only Browsing on all interfaces (0).  If and when this
        # bug gets fixed up stream then the iteration over the interfaces
        # would be appropriate.  The code should look like what is in
        # the find() method.
        # Resolve the DNS service
        sdref = pyb.DNSServiceBrowse(flags=0, regtype=common.REGTYPE,
                                     domain=common.DOMAIN,
                                     interfaceIndex=0,
                                     callBack=self._browse_callback)

        # save the service reference
        if sdref:
            self.sdrefs['browse'] = [sdref]
        else:
            raise AIMDNSError(_('error: aiMDNSError: mDNS browse failed'))

        # cause the event loop to loop only 5 times
        self._do_lookup = True
        self._handle_events()

        return self._found
def remove_client_from_config(service_name, client_id):
    '''
    Remove client entry from .config file
    Input: service name
          client_id of entry to remove
    Raises:
        ServiceCfgError if service missing .config file

    '''
    logging.log(com.XDEBUG,
                "**** START service_config.remove_client_from_config: %s "
                "%s ****", service_name, client_id)
    cfg = _read_config_file(service_name)
    if cfg is None:
        raise ServiceCfgError(_("\nMissing configuration file for "
                                "service: %s\n" % service_name))
    if CLIENTS not in cfg.sections():
        return
    clients = cfg.options(CLIENTS)
    if client_id.lower() in clients:
        cfg.remove_option(CLIENTS, client_id.lower())
    # if last client deleted, remove section
    if not cfg.options(CLIENTS):
        cfg.remove_section(CLIENTS)
    _write_config_file(service_name, cfg)
def is_client(client_id):
    '''
    Find out if client exists
    Input: clientid ('01aabbccaabbcc')
    Returns: True if client exists
             False otherwise
    Raises:
        ServiceCfgError if service missing .config file

    '''
    logging.log(com.XDEBUG, "**** START service_config.is_client: %s ****",
                client_id)
    exists = False
    all_svc_names = get_all_service_names()
    for svc in all_svc_names:
        cfg = _read_config_file(svc)
        if cfg is None:
            raise ServiceCfgError(_("\nMissing configuration file for "
                                    "service: %s\n" % svc))
        if CLIENTS not in cfg.sections():
            continue
        clients = dict(cfg.items(CLIENTS))
        # cfgparser changes client_id to lower
        if client_id.lower() in clients:
            exists = True
    logging.log(com.XDEBUG, 'client exists: %s', exists)
    return exists
def find_client(client_id):
    '''
    Get info on a particular client
    Input: clientid ('01aabbccaabbcc')
    Returns: tuple consisting of service_name of client and
             dict of client data, both None if client does not exist.
             Client data can include:
                 FILES: [list of files to remove when deleting client]
                 BOOTARGS: comma separated string of client specific boot
                           args <property>=<value>,
    Raises:
        ServiceCfgError if service missing .config file

    '''
    logging.log(com.XDEBUG, "**** START service_config.find_client: %s ****",
                client_id)
    service = None
    files = None
    for svc in get_all_service_names():
        cfg = _read_config_file(svc)
        if cfg is None:
            raise ServiceCfgError(_("\nMissing configuration file for "
                                    "service: %s\n" % svc))
        if CLIENTS not in cfg.sections():
            continue
        clients = dict(cfg.items(CLIENTS))
        # cfgparser changes client_id to lower
        if client_id.lower() in clients:
            data = clients[client_id.lower()]
            files = ast.literal_eval(data)
            service = svc
            break
    logging.log(com.XDEBUG, 'service is %s, files are %s', service, files)
    return (service, files)
Пример #31
0
class InstalladmPkgImage(InstalladmImage):
    '''Handles creation of a pkg(5)-based InstalladmImage'''

    _PKG_CLIENT_NAME = "installadm"
    DEFAULT_PKG_NAME = 'install-image/solaris-auto-install'
    ARCH_VARIANT = u'variant.arch'
    SVC_NAME_ATTR = 'com.oracle.install.service-name'
    INVALID_AI_IMAGE = _(
        "\nError:\tThe pkg image is not an Automated Installer image.\n")
    
    def __init__(self, image_path, pkg_image=None):
        super(InstalladmPkgImage, self).__init__(image_path)
        self._pkgimg = pkg_image
    
    @classmethod
    def image_create(cls, fmri_or_p5i, targetdir, arch=None, publisher=None):
        logging.debug("image_create, install from=%s", fmri_or_p5i)
        tracker = pkg.client.progress.CommandLineProgressTracker()
        root_img = pkg.client.api.ImageInterface(
            "/", PKG5_API_VERSION, tracker, None, cls._PKG_CLIENT_NAME)
        
        # In the future, handle:
        #    * SSL repos (keys/certs may need explicit flags from user)
        if publisher is not None:
            prefix = publisher[0]
            order = [prefix]
            repo = pkg.client.publisher.Repository(origins=[publisher[1]])
            pub = pkg.client.publisher.Publisher(prefix, repository=repo)
            publishers = {prefix: pub}
        else:
            publishers = dict()
            order = list()
            for pub in root_img.get_publishers(duplicate=True):
                if pub.disabled:
                    logging.debug("skipping disabled publisher '%s'",
                                  pub.prefix)
                    continue
                publishers[pub.prefix] = pub
                order.append(pub.prefix)
            
            if not publishers:
                raise ImageError(_("\nError:\tThere are no enabled "
                                 "publishers.\n"))

        if arch is None:
            arch = root_img.img.get_variants()[cls.ARCH_VARIANT]
        variants = {cls.ARCH_VARIANT: arch}
        
        props = {pkg.client.imageconfig.FLUSH_CONTENT_CACHE: True}
        pkgimg = pkg.client.api.image_create(
                        cls._PKG_CLIENT_NAME,
                        PKG5_API_VERSION,
                        targetdir,
                        pkg.client.imagetypes.IMG_USER,
                        is_zone=False,
                        progtrack=tracker,
                        props=props,
                        variants=variants
                        )

        # Add publishers to the new image, preserving the original
        # search order
        search_after = None
        for pub_prefix in order:
            add_pub = publishers[pub_prefix]
            pkgimg.add_publisher(add_pub, search_after=search_after)
            logging.debug("adding publisher '%s' after '%s'",
                          add_pub.prefix, search_after)
            search_after = pub_prefix
        
        ai_img = cls(targetdir, pkg_image=pkgimg)
        ai_img._install_package(fmri_or_p5i)
        ai_img.verify()
        ai_img._prep_ai_webserver()
        
        return ai_img
    
    @property
    def pkg_image(self):
        if self._pkgimg is None:
            tracker = pkg.client.progress.CommandLineProgressTracker()
            # installadm is non-interactive, so we don't need to track
            # the "cancel_state" like, for example, packagemanager
            cancel_state_callable = None
            self._pkgimg = pkg.client.api.ImageInterface(
                                self.path,
                                PKG5_API_VERSION,
                                tracker,
                                cancel_state_callable,
                                self._PKG_CLIENT_NAME)
        return self._pkgimg
    
    def is_pkg_based(self):
        '''Returns True if image is pkg(5) based, False otherwise'''
        try:
            self.pkg_image
        except pkg.client.api_errors.ImageNotFoundException:
            return False
        return True

    def _install_package(self, fmri_or_p5i):
        try:
            p5i_data = self.pkg_image.parse_p5i(location=fmri_or_p5i)
            
            # Returns a list of tuples; should only be one publisher with
            # one package
            if len(p5i_data) != 1:
                raise ImageError(_("\nError:\tMore than one publisher "
                                 "in p5i file.\n"))
            
            pub, pkgs = p5i_data[0]
            if len(pkgs) != 1:
                raise ImageError(_("\nError:\tMore than one package "
                                 "in p5i file.\n"))
            
            if pub and self.pkg_image.has_publisher(prefix=pub.prefix):
                img_pub = self.pkg_image.get_publisher(prefix=pub.prefix,
                                                       duplicate=True)
                for origin in pub.repository.origins:
                    if not img_pub.repository.has_origin(origin):
                        img_pub.repository.add_origin(origin)
                for mirror in pub.repository.mirrors:
                    if not img_pub.repository.has_mirror(mirror):
                        img_pub.repository.add_mirror(mirror)
                self.pkg_image.update_publisher(img_pub)
            elif pub:
                self.pkg_image.add_publisher(pub)
        except (pkg.client.api_errors.InvalidP5IFile,
                pkg.client.api_errors.RetrievalError):
            pkgs = [fmri_or_p5i]
        
        self.pkg_image.plan_install(pkgs)

        self.accept_licenses()
        self.pkg_image.prepare()
        self.pkg_image.execute_plan()
        self.pkg_image.reset()

    def accept_licenses(self):
        '''Accept licenses'''
        plan = self.pkg_image.describe()
        for pfmri, src, dest, accepted, displayed in plan.get_licenses():
            self.pkg_image.set_plan_license_status(pfmri, dest.license,
                displayed=True if dest.must_display else None,
                accepted=True if dest.must_accept else None)

    def check_fmri(self, fmri):
        '''Calls pkg.client.api.ImageInterface.parse_fmri_patterns()
           to check if fmri is valid
        Input: fmri to check
        Returns: PkgFmri object
        Raises: ValueError if there is a problem with the fmri

        '''
        for pattern, err, pfmri, matcher in \
            self.pkg_image.parse_fmri_patterns(fmri):
            if err:
                if isinstance(err, pkg.version.VersionError):
                    # For version errors, include the pattern so
                    # that the user understands why it failed.
                    print >> sys.stderr, \
                        cw(_("Illegal FMRI '%(patt)s': %(error)s" %
                           {'patt': pattern, 'error': err}))
                    raise ValueError(err)
                else:
                    # Including the pattern is redundant for other
                    # exceptions.
                    raise ValueError(err)
            return pfmri

    def check_update(self, fmri=None, publisher=None):
        '''Checks to see if any updates are available for this image.
           If so, self.pkg_image will be left "ready" to complete the
           update.

        Input: fmri - pkg to which to potentially update
               publisher - tuple (prefix, origin) to use for update. If
                           that publisher already exists in the image, its
                           origins/mirrors are reset to the passed in origin.
                           Otherwise, the new publisher is added. All other
                           publishers in the image are removed.
        Returns: True if update available; False if not

        '''
        logging.debug('check_update fmri=%s, publisher=%s', fmri, publisher)
        logging.debug("currently installed pfmri is: %s",
                      self.get_installed_pfmri())
        if fmri is not None:
            # validate fmri specified by user
            pkgfmri = self.check_fmri(fmri)
            fmri = [str(pkgfmri)]

        if publisher is not None:
            new_repo = pkg.client.publisher.Repository(origins=[publisher[1]])
            new_repo_uri = new_repo.origins[0].uri
            new_pub = pkg.client.publisher.Publisher(publisher[0],
                                                     repository=new_repo)
            if fmri:
                # ensure that user didn't specify conflicting publisher names
                if pkgfmri.publisher and pkgfmri.publisher != new_pub.prefix:
                    raise ValueError(cw(
                        _('\nError: FMRI publisher, "%(pub1)s", does not '
                          'match specified --publisher, "%(pub2)s".\n' %
                          {'pub1': pkgfmri.publisher,
                           'pub2': new_pub.prefix})))

            # Replace existing publisher(s) with that specified by user
            same_pub = None
            if self.pkg_image.has_publisher(new_pub.prefix):
                # Specified publisher already exists
                same_pub = self.pkg_image.get_publisher(new_pub.prefix,
                                                        duplicate=True)
                logging.debug('basesvc has same pub %s', same_pub.prefix)
                logging.debug('origins are:\n%s', '\n'.join(orig.uri for
                              orig in same_pub.repository.origins))
                logging.debug('replacing origins with new uri, %s',
                              new_repo_uri)
                same_pub.repository.reset_origins()
                same_pub.repository.reset_mirrors()
                same_pub.repository.add_origin(new_repo_uri)
                self.pkg_image.update_publisher(same_pub, search_first=True)
            else:
                # create a new publisher
                logging.debug('adding pub %s, origin %s',
                              new_pub.prefix, new_repo_uri)
                self.pkg_image.add_publisher(new_pub, search_first=True)

            # Remove any other publishers
            for pub in self.pkg_image.get_publishers(duplicate=True)[1:]:
                logging.debug('removing pub %s', pub.prefix)
                self.pkg_image.remove_publisher(prefix=pub.prefix)

        for plan_desc in self.pkg_image.gen_plan_update(pkgs_update=fmri):
            continue
        return (not self.pkg_image.planned_nothingtodo())

    def update(self, fmri=None, publisher=None):
        '''Check to see if update is needed for image and, if so, do the
        update.
        Input: fmri - pkg to which to potentially update
               publisher - tuple (prefix, origin) to use for update. This
                           replaces any publishers in the image.
        Returns: True if update was needed; False if not

        '''
        logging.debug('in update, fmri=%s, publisher=%s', fmri, publisher)
        update_needed = self.check_update(fmri=fmri, publisher=publisher)
        logging.debug('update_needed=%s', update_needed)
        if update_needed:
            self.accept_licenses()
            self.pkg_image.prepare()
            self.pkg_image.execute_plan()
        self.pkg_image.reset()
        return update_needed

    def get_basename(self):
        '''Get pkg service basename '''
        basename = "solarisx"
        try:
            pkg_list = self.pkg_image.get_pkg_list(
                pkg.client.api.ImageInterface.LIST_INSTALLED,
                raise_unmatched=True, return_fmris=True)

            for pfmri, summ, cats, states, attrs in pkg_list:
                manifest = self.pkg_image.get_manifest(pfmri)
                for action in manifest.gen_actions_by_type("set"):
                    for attrval in action.attrlist("name"):
                        if (attrval == self.SVC_NAME_ATTR and
                            action.attrs.get("variant.arch", self.arch) ==
                            self.arch):
                            basename = action.attrs["value"].strip()
                             
        except pkg.client.api_errors.ApiException:
            pass

        logging.debug("get_basename returning %s", basename)
        return basename

    def get_installed_pfmri(self):
        '''Get installed pkg fmri'''
        try:
            pkg_list = self.pkg_image.get_pkg_list(
                pkg.client.api.ImageInterface.LIST_INSTALLED,
                raise_unmatched=True, return_fmris=True)
            try:
                pfmri = pkg_list.next()[0]
            except StopIteration:
                raise ImageError(_("\nError:\tUnable to obtain pkg name from "
                                   "image.\n"))
        except pkg.client.api_errors.ApiException:
            raise
        return pfmri
Пример #32
0
                parser.print_help()
        sys.exit()

    else:
        # make sure we have the installadm smf service
        try:
            aismf.get_smf_instance()
        except aismf.ServicesError as err:
            raise SystemExit(err)

        # Invoke the function which implements the specified subcommand
        func = sub_cmds[sub_cmd][0]

        logging.debug("Invoking subcommand: %s %s", func.func_name,
                      sys.argv[index + 1:])
        try:
            return func(sys.argv[index + 1:])
        except VersionError as err:
            print >> sys.stderr, err
            return 4
        except Exception:
            sys.stderr.write(
                _("%s:\n"
                  "\tUnhandled error encountered:\n") % sub_cmd)
            traceback.print_exc(file=sys.stderr)
            return 1


if __name__ == '__main__':
    sys.exit(main())
Пример #33
0
def main():
    ''' installadm main

    Parse the command line arguments and invoke sub-command.

    Returns:
        The return from the invoked sub-command.
        4 if VersionError encountered

    '''
    # sub_cmds is a dictionary. The value for each subcommand key
    # is a tuple consisting of the method to call to invoke the
    # subcommand and the method to call to get usage for the subcommand.
    sub_cmds = {
        'create-service':
        (create_service.do_create_service, create_service.get_usage()),
        'delete-service':
        (delete_service.do_delete_service, delete_service.get_usage()),
        'rename-service': (rename_service.do_rename_service,
                           rename_service.get_usage()),
        'set-service': (set_service.do_set_service, set_service.get_usage()),
        'list': (ai_list.do_list, ai_list.get_usage()),
        'enable': (do_enable_service, get_enable_usage()),
        'disable': (do_disable_service, get_disable_usage()),
        'create-client': (create_client.do_create_client,
                          create_client.get_usage()),
        'create-profile': (create_profile.do_create_profile,
                           create_profile.get_create_usage()),
        'update-profile': (create_profile.do_update_profile,
                           create_profile.get_update_usage()),
        'delete-client': (delete_client.do_delete_client,
                          delete_client.get_usage()),
        'create-manifest': (publish_manifest.do_publish_manifest,
                            publish_manifest.get_create_usage()),
        'add-manifest': (
            publish_manifest.do_publish_manifest,  # alias
            publish_manifest.get_create_usage()),
        'update-manifest': (publish_manifest.do_update_manifest,
                            publish_manifest.get_update_usage()),
        'delete-manifest': (delete_manifest.do_delete_manifest,
                            delete_manifest.get_usage()),
        'delete-profile': (delete_profile.do_delete_profile,
                           delete_profile.get_usage()),
        'export': (export.do_export, export.get_usage()),
        'remove': (
            delete_manifest.do_delete_manifest,  # alias
            delete_manifest.get_usage()),
        'set-criteria':
        (set_criteria.do_set_criteria, set_criteria.get_usage()),
        'validate': (validate_profile.do_validate_profile,
                     validate_profile.get_usage()),
        'help': (None, get_help_usage())
    }

    # cmds is a list of subcommands used to dictate the order of
    # the commands listed in the usage output
    cmds = [
        "create-service",
        "delete-service",
        "rename-service",
        "set-service",
        "list",
        "enable",
        "disable",
        "create-client",
        "delete-client",
        "create-manifest",
        "update-manifest",
        "delete-manifest",
        "create-profile",
        "update-profile",
        "delete-profile",
        "export",
        "validate",
        "set-criteria",
        "help",
    ]

    usage_str = "Usage: installadm [options] <subcommand> <args> ..."
    for entry in cmds:
        usage_str += '\n' + sub_cmds[entry][1]
    parser = OptionParser(usage=usage_str)

    # add private debug option, which provides console output that might
    # be useful during development or bug fixing.
    parser.add_option("-d",
                      "--debug",
                      action="count",
                      dest="debug",
                      default=0,
                      help=SUPPRESS_HELP)

    # Find subcommand in sys.argv and save index to know which
    # options/args to pass to installadm and which options to
    # pass to subcommand
    sub_cmd = None
    index = 0
    for index, arg in enumerate(sys.argv):
        if arg in sub_cmds:
            sub_cmd = arg
            break

    # Exit if no subcommand was provided.
    if not sub_cmd:
        parser.print_help(file=sys.stderr)
        sys.exit(2)

    # Pass arguments up to subcommand to installadm parser
    # The rest of the arguments will be passed to the
    # subcommand later.
    #
    (options, args) = parser.parse_args(sys.argv[1:index])

    if args:
        parser.error(_("Unexpected argument(s): %s" % args))

    # Set up logging to the specified level of detail
    if options.debug == 0:
        options.log_level = DEFAULT_LOG_LEVEL
    elif options.debug == 1:
        options.log_level = DEBUG_LOG_LEVEL
    elif options.debug >= 2:
        options.log_level = XDEBUG
    try:
        setup_logging(options.log_level)
    except IOError, err:
        parser.error("%s '%s'" % (err.strerror, err.filename))
Пример #34
0
def get_disable_usage():
    ''' get usage for disable'''
    usage = _('disable\t<svcname>')
    return (usage)
Пример #35
0
def parse_options():
    '''Parses and validate options

    Args
        None

    Globals
        None

    Returns
        a dictionary of the valid options

            {
                'verbose':Bool,
                'interface':interface_name (i.e., iwh0),
                'comment':string (comment for the server),
                'timeout':time (length of time to wait per request),
                'service':SName (service name to find),
                'browse':Bool (browse mode),
                'register':SName (service name to register),
                'port':port (port number for the service),
            }

    Raises
        None
    '''
    desc = _("Multicast DNS (mDNS) & DNS Service Directory Automated "
             "Installations utility. "
             "Or with -f option, Find a service. "
             "Or with -b option, Browse the services. "
             "Or with -r option, Register a service. "
             "Or with -i option, Browse, Find, Register on a "
             "specific interface. "
             "Or with -c option, Comment for a mDNS record(s) being "
             "registered. "
             "Or with -t option, set the timeout for the operation. "
             "Or with -p option, set the port number for the registration. "
             "Or with -v option, Verbose output.")

    usage = _("usage: %prog [[-v][-i <interface>][-t <timeout>]]\n"
              "\t[-f <servicename>] |\n"
              "\t[-b] |\n"
              "\t[-r <servicename> -p <port>] [[-c comment]] |\n")

    parser = OptionParser(usage=usage, description=desc)

    parser.add_option('-v',
                      '--verbose',
                      dest='verbose',
                      default=False,
                      action='store_true',
                      help=_('turn on verbose mode'))

    parser.add_option('-i',
                      '--interface',
                      dest='interface',
                      default=None,
                      type='string',
                      help=_('interface to browse, find or register on'))

    parser.add_option('-c',
                      '--comment',
                      dest='comment',
                      default=None,
                      type='string',
                      help=_('comment used in service registration'))

    parser.add_option('-t',
                      '--timeout',
                      dest='timeout',
                      default=None,
                      type='int',
                      help=_('set the timeout for the operation'))

    parser.add_option('-p',
                      '--port',
                      dest='port',
                      default=None,
                      type='int',
                      help=_('set the port for the ad hoc registration'))

    parser.add_option("-f",
                      "--find",
                      dest="service",
                      default=None,
                      type="string",
                      help=_("find a named service"))

    parser.add_option("-b",
                      "--browse",
                      dest="browse",
                      default=False,
                      action="store_true",
                      help=_("browse the services"))

    parser.add_option("-r",
                      "--register",
                      dest="register",
                      default=None,
                      type="string",
                      help=_("register a service, root privileges required"))

    (loptions, args) = parser.parse_args()

    if args:
        parser.error(_('unknown argument(s): %s') % args)

    if loptions.register is not None and os.geteuid() != 0:
        parser.error(_('root privileges required with the "-r" operation.'))

    if [
            bool(loptions.browse),
            bool(loptions.register),
            bool(loptions.service)
    ].count(True) > 1:
        parser.error(
            _('"-f", "-b", and "-r" operations are mutually '
              'exclusive.'))

    if not loptions.browse and not loptions.register and not loptions.service:
        parser.error(_('must specify an operation of "-f", "-b", or "-r".'))

    if loptions.register and not loptions.port:
        parser.error(_('must specify a "port" for the "-r" operation.'))

    if not loptions.register and loptions.port:
        parser.error(_('"-p" option only valid for the "-r" operation.'))

    if not loptions.register and loptions.comment:
        parser.error(_('"-c" option only valid for the "-r" operation.'))

    return loptions
def delete_manifest_from_db(db, manifest_instance, service_name, data_loc):
    """
    Remove manifest from DB
    """
    instance = manifest_instance[1]
    # check to see that the manifest is found in the database (as entered)
    if manifest_instance[0] not in AIdb.getManNames(db.getQueue()):
        # since all manifest names have to have .xml appended try adding that
        if manifest_instance[0] + '.xml' in AIdb.getManNames(db.getQueue()):
            man_name = manifest_instance[0] + '.xml'
        else:
            raise SystemExit(
                _("Error:\tManifest %s not found in database!" %
                  manifest_instance[0]))
    else:
        man_name = manifest_instance[0]

    service = AIService(service_name)
    # Do not delete if this manifest is set up as the default.
    if man_name == service.get_default_manifest():
        raise ValueError(
            _("Error:\tCannot delete default manifest %s.") % man_name)

    # if we do not have an instance remove the entire manifest
    if instance is None:
        # remove manifest from database
        query = AIdb.DBrequest("DELETE FROM manifests WHERE name = '%s'" %
                               AIdb.sanitizeSQL(man_name),
                               commit=True)
        db.getQueue().put(query)
        query.waitAns()
        # run getResponse to handle and errors
        query.getResponse()

        # clean up file on file system
        try:
            os.remove(os.path.join(service.manifest_dir, man_name))
        except OSError:
            print >> sys.stderr, _("Warning:\tUnable to find file %s for " +
                                   "removal!") % man_name

    # we are removing a specific instance
    else:
        # check that the instance number is within bounds for that manifest
        # (0..numInstances)
        if instance > AIdb.numInstances(man_name, db.getQueue()) or \
            instance < 0:
            raise SystemExit(
                _("Error:\tManifest %s has %i instances" %
                  (man_name, AIdb.numInstances(man_name, db.getQueue()))))

        # remove instance from database
        query = ("DELETE FROM manifests WHERE name = '%s' AND "
                 "instance = '%i'") % (AIdb.sanitizeSQL(man_name), instance)
        query = AIdb.DBrequest(query, commit=True)
        db.getQueue().put(query)
        query.waitAns()
        # run getResponse to handle and errors
        query.getResponse()

        # We may need to reshuffle manifests to prevent gaps in instance
        # numbering as the DB routines expect instances to be contiguous and
        # increasing. We may have removed an instance with instances numbered
        # above thus leaving a gap.

        # get the number of instances with a larger instance
        for num in range(instance,
                         AIdb.numInstances(man_name, db.getQueue()) + 1):
            # now decrement the instance number
            query = ("UPDATE manifests SET instance = '%i' WHERE "
                     "name = '%s' ") % (num - 1, AIdb.sanitizeSQL(man_name))
            query += "AND instance = '%i'" % num
            query = AIdb.DBrequest(query, commit=True)
            db.getQueue().put(query)
            query.waitAns()
            # run getResponse to handle and errors
            query.getResponse()

        # remove file if manifest is no longer in database
        if man_name not in AIdb.getManNames(db.getQueue()):
            try:
                os.remove(os.path.join(service.manifest_dir, man_name))
            except OSError:
                print >> sys.stderr, _("Warning: Unable to find file %s for " +
                                       "removal!") % man_name
Пример #37
0
    # they exist.  We validate those separately later.
    try:
        crit = lxml.etree.parse(criteria_path)
    except lxml.etree.XMLSyntaxError, err:
        raise ValueError(_("Error: %s") % err.error_log.last_error)

    ai_sc_list = list()
    ai_sc_paths = (".//ai_manifest_file", ".//ai_embedded_manifest",
                   ".//sc_manifest_file", ".//sc_embedded_manifest")
    for path in ai_sc_paths:
        elements = crit.iterfind(path)

        for elem in elements:
            if is_dtd:
                raise ValueError(
                    _("Error:\tCriteria file should not contain "
                      "AI or SC manifest tags: %s") % criteria_path)
            ai_sc_list.append(elem)
            elem.getparent().remove(elem)

    # Verify the remaing DOM, which should only contain criteria
    root, errors = (verifyXML.verifyRelaxNGManifest(
        schema, StringIO.StringIO(lxml.etree.tostring(crit.getroot()))))
    logging.debug('criteria file passed RNG validation')

    if errors:
        raise ValueError(
            _("Error:\tFile %s failed validation:\n"
              "\tline %s: %s") % (criteria_path, errors.line, errors.message))
    try:
        verifyXML.prepValuesAndRanges(root, db, table)
    except ValueError, err:
def do_create_baseservice(options):
    '''
    This method sets up the install service by:
        - creating the target image directory from an iso or pkg
        - creating the /var/ai service structure
        - enabling tftp service or configuring wanboot
        - configuring dhcp if desired
    
    '''
    tempdir = None
    print _("\nCreating service from: %s") % options.srcimage
    if is_iso(options.srcimage):
        have_iso = True
        # get default service name, if needed
        logging.debug("Creating ISO based service")
    else:
        have_iso = False
        logging.debug("Creating pkg(5) based service")

    # If imagepath specified by user, use that.
    # If imagepath not specified  by user:
    #    a) if svcname specified by user, set up image in
    #       <default image path>/<svcname>
    #    b) if svcname not specified by user, set up image in
    #       <tmp location> and move to <default image path>/<svcname>
    #       once svcname is determined.

    # If imagepath not specified, verify that default image path is
    # ok with user
    if not options.imagepath:
        if options.svcname:
            imagepath = os.path.join(BASE_IMAGE_DIR, options.svcname)
            prompt = (_("OK to use default image path: %s? [y/N]: " %
                        imagepath))
        else:
            prompt = (_("OK to use subdir of %s to store image? [y/N]: " %
                        BASE_IMAGE_DIR))
        try:
            if not options.noprompt:
                if not com.ask_yes_or_no(prompt):
                    raise SystemExit(
                        _('\nPlease re-enter command with '
                          'desired --imagepath\n'))
        except KeyboardInterrupt:
            raise SystemExit(1)

        # If we know the svcname, we know where to put the image.
        # Otherwise, put the image into a temp directory and move
        # it to correct location when we know it later
        if options.svcname:
            options.imagepath = os.path.join(BASE_IMAGE_DIR, options.svcname)
            try:
                check_imagepath(options.imagepath)
            except ValueError as error:
                raise SystemExit(error)
        else:
            try:
                os.makedirs(BASE_IMAGE_DIR)
            except OSError as err:
                if err.errno != errno.EEXIST:
                    raise
                if not os.path.isdir(BASE_IMAGE_DIR):
                    raise SystemExit(
                        cw(
                            _('\nThe default image base '
                              'directory, %(dir)s, is not a directory. Check the '
                              'SMF setting for property %(prop)s in servce '
                              '%(svc)s.') % {
                                  'dir': BASE_IMAGE_DIR,
                                  'prop': com.BASEDIR_PROP,
                                  'svc': com.SRVINST
                              }))
            tempdir = tempfile.mkdtemp(dir=BASE_IMAGE_DIR)
            options.imagepath = tempdir
        logging.debug('Using default image path: %s', options.imagepath)

    # create the image area
    if have_iso:
        try:
            image = InstalladmIsoImage.unpack(options.srcimage,
                                              options.imagepath)
        except CalledProcessError as err:
            raise SystemExit(err.popen.stderr)
        except ImageError as err:
            print >> sys.stderr, str(err)
            shutil.rmtree(options.imagepath, ignore_errors=True)
            raise SystemExit(
                cw(
                    _('Please re-enter command and specify '
                      'a valid Automated Installer ISO file')))
    else:
        try:
            image = InstalladmPkgImage.image_create(
                options.srcimage,
                options.imagepath,
                arch=options.arch,
                publisher=options.publisher)
        except (ImageError, pkg.client.api_errors.ApiException) as err:
            print >> sys.stderr, cw(_("The specified data source, %s, "
                "for the service is not a path to an existing ISO image.") % \
                options.srcimage)
            print >> sys.stderr, cw(_("Attempting to create the service from"
                " pkg(5) package, %s, failed for the following reasons:") % \
                options.srcimage)
            if isinstance(err, pkg.client.api_errors.VersionException):
                print >> sys.stderr, cw(
                    _("The IPS API version specified, " +
                      str(err.received_version) +
                      ", is incompatible with the expected version, " +
                      str(err.expected_version) + "."))
            elif 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
            shutil.rmtree(options.imagepath, ignore_errors=True)
            raise SystemExit(err)

    # get default service name, if needed
    if not options.svcname:
        if tempdir and options.imagepath == tempdir:
            specified_path = None
        else:
            specified_path = options.imagepath
        options.svcname = get_default_service_name(specified_path,
                                                   image=image,
                                                   iso=have_iso)

    print _("\nCreating %(arch)s service: %(name)s\n") % \
            {'arch': image.arch,
             'name': options.svcname}

    # If image was created in temporary location, move to correct
    # location now that we know the svcname.
    if tempdir is not None:
        new_imagepath = os.path.join(BASE_IMAGE_DIR, options.svcname)
        try:
            check_imagepath(new_imagepath)
        except ValueError as error:
            # leave image in temp location so that service can be created
            logging.debug('unable to move image to %s: %s', new_imagepath,
                          error)
        else:
            options.imagepath = image.move(new_imagepath)
            logging.debug('image moved to %s', options.imagepath)

    set_permissions(options.imagepath)
    print _("Image path: %s\n") % options.imagepath
    try:
        if options.dhcp_ip_start:
            service = AIService.create(options.svcname,
                                       image,
                                       options.dhcp_ip_start,
                                       options.dhcp_ip_count,
                                       options.dhcp_bootserver,
                                       bootargs=options.bootargs)
        else:
            service = AIService.create(options.svcname,
                                       image,
                                       bootargs=options.bootargs)
    except AIServiceError as err:
        raise SystemExit(err)

    # Register & enable service
    # (Also enables system/install/server, as needed)
    got_services_error = False
    try:
        service.enable()
    except (config.ServiceCfgError, MountError) as err:
        raise SystemExit(err)
    except aismf.ServicesError as svc_err:
        # Don't print the error now.  It will either get printed out
        # upon exit or when services are enabled after creating the
        # alias
        got_services_error = True

    # create default-<arch> alias if this is the first aliasable
    # service of this architecture
    if should_be_default_for_arch(service):
        defaultarch = 'default-' + image.arch
        print(_("\nCreating %s alias.\n") % defaultarch)
        try:
            defaultarchsvc = AIService.create(defaultarch,
                                              image,
                                              bootargs=options.bootargs,
                                              alias=options.svcname)
        except AIServiceError as err:
            raise SystemExit(err)
        except UnsupportedAliasError as err:
            if got_services_error:
                # Print the services error string before printing the
                # unsupported alias error
                print svc_err, '\n'

            # Print the error, but have installadm exit successfully.
            # Since the user did not explicitly request this alias,
            # it's not a problem if an alias can't be made for this service
            print err
            return 0

        # For sparc, create symlinks for default sparc service
        if image.arch == 'sparc':
            logging.debug("Creating default-sparc symlinks")
            defaultarchsvc.do_default_sparc_symlinks(defaultarch)

        # Register & enable default-<arch> service
        try:
            defaultarchsvc.enable()
        except (config.ServiceCfgError, MountError) as err:
            raise SystemExit(err)
        except aismf.ServicesError as err:
            print err
    elif got_services_error:
        # Print the services start error generated when creating the service
        print svc_err
Пример #39
0
def parse_options(cmd_options=None):
    '''Parse commandline options for export command'''

    parser = OptionParser(usage=get_usage(), prog="export")
    parser.add_option('-p',
                      '--profile',
                      dest='pnames',
                      action="append",
                      default=list(),
                      help=_("Name of profile to export."))
    parser.add_option('-m',
                      '--manifest',
                      dest='mnames',
                      action="append",
                      default=list(),
                      help=_("Name of manifest to export."))
    parser.add_option('-n',
                      '--service',
                      dest='service_name',
                      default=None,
                      help=_("Name of install service."))
    parser.add_option('-o',
                      '--output',
                      dest='output_name',
                      default=None,
                      help=_("Name of output file."))

    (options, args) = parser.parse_args(cmd_options)

    if args:
        parser.error(_("Extra args given."))

    if not options.service_name:
        parser.error(_("Service name is required."))

    if not config.is_service(options.service_name):
        raise SystemExit(_("No such service: %s") % options.service_name)

    service = AIService(options.service_name)
    options.service = service

    if not len(options.mnames) and not len(options.pnames):
        parser.error(_("A manifest or profile name is required."))

    options.file_count = len(options.mnames) + len(options.pnames)

    if not options.output_name:
        options.output_name = SCREEN
        options.output_isdir = None

    else:
        # Non-stdout -o processing:
        # if output_name is an existing directory: write all files out to it.
        # if output_name is an existing file and output one file:
        #     overwrite the existing file.
        # if file exists with output_name and mult output files desired:
        #     error
        # if file or dir doesn't exist w/output name and mult files desired:
        #     create new directory with output name and write files there.
        # if file or dir doesn't exist with output name and one file desired:
        #     write the one file to that output name

        options.output_isdir = False
        if os.path.isdir(options.output_name):
            options.output_isdir = True
        elif os.path.exists(options.output_name):
            if (options.file_count > 1):
                parser.error(
                    _("-o must specify a directory when multiple "
                      "files are requested."))
        else:
            if (options.file_count > 1):
                os.mkdir(options.output_name)
                options.output_isdir = True

    return options
Пример #40
0
def list_manifests(service):
    '''Replies to the client with criteria list for a service.
       The output should be similar to installadm list.

    Args
        service - the name of the service being listed

    Returns
        None

    Raises
        None
    '''
    print 'Content-Type: text/html'  # HTML is following
    print  # blank line, end of headers
    print '<html>'
    print '<head>'
    sys.stdout.write('<title>%s %s</title>' %
                     (_('Manifest list for'), service))
    print '</head><body>'

    port = 0
    try:
        smf.AISCF(FMRI="system/install/server")
    except KeyError:
        # report the internal error to error_log and requesting client
        sys.stderr.write(
            _("error:The system does not have the "
              "system/install/server SMF service."))
        sys.stdout.write(
            _("error:The system does not have the "
              "system/install/server SMF service."))
        return
    services = config.get_all_service_names()
    if not services:
        # report the error to the requesting client only
        sys.stdout.write(_('error:no services on this server.\n'))
        return

    found = False
    if config.is_service(service):
        service_ctrl = AIService(service)
        found = True

        # assume new service setup
        path = service_ctrl.database_path
        if os.path.exists(path):
            try:
                aisql = AIdb.DB(path)
                aisql.verifyDBStructure()
            except StandardError as err:
                # report the internal error to error_log and
                # requesting client
                sys.stderr.write(
                    _('error:AI database access '
                      'error\n%s\n') % err)
                sys.stdout.write(
                    _('error:AI database access '
                      'error\n%s\n') % err)
                return

            # generate the list of criteria for the criteria table header
            criteria_header = E.TR()
            for crit in AIdb.getCriteria(aisql.getQueue(), strip=False):
                criteria_header.append(E.TH(crit))

            # generate the manifest rows for the criteria table body
            names = AIdb.getManNames(aisql.getQueue())
            table_body = E.TR()
            allcrit = AIdb.getCriteria(aisql.getQueue(), strip=False)
            colspan = str(max(len(list(allcrit)), 1))
            for manifest in names:

                # iterate through each manifest (and instance)
                for instance in range(
                        0, AIdb.numInstances(manifest, aisql.getQueue())):

                    table_body.append(E.TR())
                    # print the manifest name only once (from instance 0)
                    if instance == 0:
                        href = '../' + service + '/' + manifest
                        row = str(AIdb.numInstances(manifest,
                                                    aisql.getQueue()))
                        table_body.append(
                            E.TD(E.A(manifest, href=href, rowspan=row)))
                    else:
                        table_body.append(E.TD())

                    crit_pairs = AIdb.getManifestCriteria(manifest,
                                                          instance,
                                                          aisql.getQueue(),
                                                          onlyUsed=True,
                                                          humanOutput=True)

                    # crit_pairs is an SQLite3 row object which doesn't
                    # support iteritems(), etc.
                    for crit in crit_pairs.keys():
                        formatted_val = AIdb.formatValue(
                            crit, crit_pairs[crit])
                        # if we do not get back a valid value ensure a
                        # hyphen is printed (prevents "" from printing)
                        if formatted_val and crit_pairs[crit]:
                            table_body.append(
                                E.TD(formatted_val, align="center"))
                        else:
                            table_body.append(
                                E.TD(lxml.etree.Entity("nbsp"),
                                     align="center"))

            # print the default manifest at the end of the table,
            # which has the same colspan as the Criteria List label
            else:
                href = '../' + service + '/default.xml'
                table_body.append(
                    E.TR(
                        E.TD(E.A("Default", href=href)),
                        E.TD(lxml.etree.Entity("nbsp"),
                             colspan=colspan,
                             align="center")))
            web_page = E.HTML(
                E.HEAD(
                    E.TITLE(
                        _("Syneto StorageOS Automated "
                          "Installation Webserver"))),
                E.BODY(
                    E.H1(
                        _("Welcome to the Syneto StorageOS "
                          "Automated Installation webserver!")),
                    E.P(
                        _("Service '%s' has the following "
                          "manifests available, served to clients "
                          "matching required criteria.") % service),
                    E.TABLE(E.TR(E.TH(_("Manifest"), rowspan="2"),
                                 E.TH(_("Criteria List"), colspan=colspan)),
                            criteria_header,
                            table_body,
                            border="1",
                            align="center"),
                ))
            print lxml.etree.tostring(web_page, pretty_print=True)

    # service is not found, provide available services on host
    if not found:
        sys.stdout.write(_('Service <i>%s</i> not found.  ') % service)
        sys.stdout.write(_('Available services are:<p><ol><i>'))
        host = socket.gethostname()
        for service_name in config.get_all_service_names():
            # assume new service setup
            port = config.get_service_port(service_name)
            sys.stdout.write(
                '<a href="http://%s:%d/cgi-bin/'
                'cgi_get_manifest.py?version=%s&service=%s">%s</a><br>\n' %
                (host, port, VERSION, service_name, service_name))
        sys.stdout.write('</i></ol>%s' % _('Please select a service '
                                           'from the above list.'))

    print '</body></html>'
Пример #41
0
def send_manifest(form_data,
                  port=0,
                  servicename=None,
                  protocolversion=COMPATIBILITY_VERSION,
                  no_default=False):
    '''Replies to the client with matching service for a service.
    
    Args
        form_data   - the postData passed in from the client request
        port        - the port of the old client
        servicename - the name of the service being used
        protocolversion - the version of the AI service RE: handshake
        no_default  - boolean flag to signify whether or not we should hand
                      back the default manifest and profiles if one cannot
                      be matched based on the client criteria.

    Returns
        None
    
    Raises
        None
    
    '''
    # figure out the appropriate path for the AI database,
    # and get service name if necessary.
    # currently service information is stored in a port directory.
    # When the cherrypy webserver new service directories should be
    # separated via service-name only.  Old services will still use
    # port numbers as the separation mechanism.
    path = None
    found_servicename = None
    service = None
    port = str(port)

    if servicename:
        service = AIService(servicename)
        path = service.database_path
    else:
        for name in config.get_all_service_names():
            if config.get_service_port(name) == port:
                found_servicename = name
                service = AIService(name)
                path = service.database_path
                break

    # Check to insure that a valid path was found
    if not path or not os.path.exists(path):
        print 'Content-Type: text/html'  # HTML is following
        print  # blank line, end of headers
        if servicename:
            print '<pre><b>Error</b>:unable to find<i>', servicename + '</i>.'
        else:
            print '<pre><b>Error</b>:unable to find<i>', port + '</i>.'
        print 'Available services are:<p><ol><i>'
        hostname = socket.gethostname()
        for name in config.get_all_service_names():
            port = config.get_service_port(name)
            sys.stdout.write(
                '<a href="http://%s:%d/cgi-bin/'
                'cgi_get_manifest.py?version=%s&service=%s">%s</a><br>\n' %
                (hostname, port, VERSION, name, name))
        print '</i></ol>Please select a service from the above list.'
        return

    if found_servicename:
        servicename = found_servicename

    # load to the AI database
    aisql = AIdb.DB(path)
    aisql.verifyDBStructure()

    # convert the form data into a criteria dictionary
    criteria = dict()
    orig_data = form_data
    while form_data:
        try:
            [key_value, form_data] = form_data.split(';', 1)
        except (ValueError, NameError, TypeError, KeyError):
            key_value = form_data
            form_data = ''
        try:
            [key, value] = key_value.split('=')
            criteria[key] = value
        except (ValueError, NameError, TypeError, KeyError):
            criteria = dict()

    # Generate templating dictionary from criteria
    template_dict = dict()
    for crit in criteria:
        template_dict["AI_" + crit.upper()] = \
                AIdb.formatValue(crit, criteria[crit], units=False)

    # find the appropriate manifest
    try:
        manifest = AIdb.findManifest(criteria, aisql)
    except StandardError as err:
        print 'Content-Type: text/html'  # HTML is following
        print  # blank line, end of headers
        print '<pre><b>Error</b>:findManifest criteria<br>'
        print err, '<br>'
        print '<ol>servicename =', servicename
        print 'port        =', port
        print 'path        =', path
        print 'form_data   =', orig_data
        print 'criteria    =', criteria
        print 'servicename found by port =', found_servicename, '</ol>'
        print '</pre>'
        return

    # check if findManifest() returned a number equal to 0
    # (means we got no manifests back -- thus we serve the default if desired)
    if manifest is None and not no_default:
        manifest = service.get_default_manifest()

    # if we have a manifest to return, prepare its return
    if manifest is not None:
        try:
            # construct the fully qualified filename
            filename = os.path.abspath(
                os.path.join(service.manifest_dir, manifest))
            # open and read the manifest
            with open(filename, 'rb') as mfp:
                manifest_str = mfp.read()
            # maintain compability with older AI client
            if servicename is None or \
                    float(protocolversion) < float(PROFILES_VERSION):
                content_type = mimetypes.types_map.get('.xml', 'text/plain')
                print 'Content-Length:', len(
                    manifest_str)  # Length of the file
                print 'Content-Type:', content_type  # XML is following
                print  # blank line, end of headers
                print manifest_str
                logging.info('Manifest sent from %s.' % filename)
                return

        except OSError as err:
            print 'Content-Type: text/html'  # HTML is following
            print  # blank line, end of headers
            print '<pre>'
            # report the internal error to error_log and requesting client
            sys.stderr.write(_('error:manifest (%s) %s\n') % \
                            (str(manifest), err))
            sys.stdout.write(_('error:manifest (%s) %s\n') % \
                            (str(manifest), err))
            print '</pre>'
            return

    # get AI service image path
    service = AIService(servicename)
    image_dir = service.image.path
    # construct object to contain MIME multipart message
    outermime = MIMEMultipart()
    client_msg = list()  # accumulate message output for AI client

    # If we have a manifest, attach it to the return message
    if manifest is not None:
        # add manifest as attachment
        msg = MIMEText(manifest_str, 'xml')
        # indicate manifest using special name
        msg.add_header('Content-Disposition',
                       'attachment',
                       filename=sc.AI_MANIFEST_ATTACHMENT_NAME)
        outermime.attach(msg)  # add manifest as an attachment

    # search for any profiles matching client criteria
    # formulate database query to profiles table
    q_str = "SELECT DISTINCT name, file FROM " + \
        AIdb.PROFILES_TABLE + " WHERE "
    nvpairs = list()  # accumulate criteria values from post-data
    # for all AI client criteria
    for crit in AIdb.getCriteria(aisql.getQueue(),
                                 table=AIdb.PROFILES_TABLE,
                                 onlyUsed=False):
        if crit not in criteria:
            msgtxt = _("Warning: client criteria \"%s\" not provided in "
                       "request.  Setting value to NULL for profile lookup.") \
                       % crit
            client_msg += [msgtxt]
            logging.warn(msgtxt)
            # fetch only global profiles destined for all clients
            if AIdb.isRangeCriteria(aisql.getQueue(), crit,
                                    AIdb.PROFILES_TABLE):
                nvpairs += ["MIN" + crit + " IS NULL"]
                nvpairs += ["MAX" + crit + " IS NULL"]
            else:
                nvpairs += [crit + " IS NULL"]
            continue

        # prepare criteria value to add to query
        envval = AIdb.sanitizeSQL(criteria[crit])
        if AIdb.isRangeCriteria(aisql.getQueue(), crit, AIdb.PROFILES_TABLE):
            # If no default profiles are requested, then we mustn't allow
            # this criteria to be NULL.  It must match the client's given
            # value for this criteria.
            if no_default:
                if crit == "mac":
                    nvpairs += ["(HEX(MIN" + crit + ")<=HEX(X'" + envval + \
                        "'))"]

                    nvpairs += ["(HEX(MAX" + crit + ")>=HEX(X'" + envval + \
                        "'))"]
                else:
                    nvpairs += ["(MIN" + crit + "<='" + envval + "')"]
                    nvpairs += ["(MAX" + crit + ">='" + envval + "')"]
            else:
                if crit == "mac":
                    nvpairs += [
                        "(MIN" + crit + " IS NULL OR "
                        "HEX(MIN" + crit + ")<=HEX(X'" + envval + "'))"
                    ]
                    nvpairs += [
                        "(MAX" + crit + " IS NULL OR HEX(MAX" + crit +
                        ")>=HEX(X'" + envval + "'))"
                    ]
                else:
                    nvpairs += [
                        "(MIN" + crit + " IS NULL OR MIN" + crit + "<='" +
                        envval + "')"
                    ]
                    nvpairs += [
                        "(MAX" + crit + " IS NULL OR MAX" + crit + ">='" +
                        envval + "')"
                    ]
        else:
            # If no default profiles are requested, then we mustn't allow
            # this criteria to be NULL.  It must match the client's given
            # value for this criteria.
            #
            # Also, since this is a non-range criteria, the value stored
            # in the DB may be a whitespace separated list of single
            # values.  We use a special user-defined function in the
            # determine if the given criteria is in that textual list.
            if no_default:
                nvpairs += ["(is_in_list('" + crit + "', '" + envval + \
                    "', " + crit + ", 'None') == 1)"]
            else:
                nvpairs += ["(" + crit + " IS NULL OR is_in_list('" + crit + \
                    "', '" + envval + "', " + crit + ", 'None') == 1)"]

    if len(nvpairs) > 0:
        q_str += " AND ".join(nvpairs)

        # issue database query
        logging.info("Profile query: " + q_str)
        query = AIdb.DBrequest(q_str)
        aisql.getQueue().put(query)
        query.waitAns()
        if query.getResponse() is None or len(query.getResponse()) == 0:
            msgtxt = _("No profiles found.")
            client_msg += [msgtxt]
            logging.info(msgtxt)
        else:
            for row in query.getResponse():
                profpath = row['file']
                profname = row['name']
                if profname is None:  # should not happen
                    profname = 'unnamed'
                try:
                    if profpath is None:
                        msgtxt = "Database record error - profile path is " \
                            "empty."
                        client_msg += [msgtxt]
                        logging.error(msgtxt)
                        continue
                    msgtxt = _('Processing profile %s') % profname
                    client_msg += [msgtxt]
                    logging.info(msgtxt)
                    with open(profpath, 'r') as pfp:
                        raw_profile = pfp.read()
                    # do any template variable replacement {{AI_xxx}}
                    tmpl_profile = sc.perform_templating(
                        raw_profile, template_dict)
                    # precautionary validation of profile, logging only
                    sc.validate_profile_string(tmpl_profile,
                                               image_dir,
                                               dtd_validation=True,
                                               warn_if_dtd_missing=True)
                except IOError as err:
                    msgtxt = _("Error:  I/O error: ") + str(err)
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    continue
                except OSError:
                    msgtxt = _("Error:  OS error on profile ") + profpath
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    continue
                except KeyError:
                    msgtxt = _('Error:  could not find criteria to substitute '
                               'in template: ') + profpath
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    logging.error('Profile with template substitution error:' +
                                  raw_profile)
                    continue
                except lxml.etree.XMLSyntaxError as err:
                    # log validation error and proceed
                    msgtxt = _(
                            'Warning:  syntax error found in profile: ') \
                            + profpath
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    for error in err.error_log:
                        msgtxt = _('Error:  ') + error.message
                        client_msg += [msgtxt]
                        logging.error(msgtxt)
                    logging.info([
                        _('Profile failing validation:  ') +
                        lxml.etree.tostring(root)
                    ])
                # build MIME message and attach to outer MIME message
                msg = MIMEText(tmpl_profile, 'xml')
                # indicate in header that this is an attachment
                msg.add_header('Content-Disposition',
                               'attachment',
                               filename=profname)
                # attach this profile to the manifest and any other profiles
                outermime.attach(msg)
                msgtxt = _('Parsed and loaded profile: ') + profname
                client_msg += [msgtxt]
                logging.info(msgtxt)

    # any profiles and AI manifest have been attached to MIME message
    # specially format list of messages for display on AI client console
    if client_msg:
        outtxt = ''
        for msgtxt in client_msg:
            msgtxt = _('SC profile locator:') + msgtxt
            outtxt += str(msgtxt) + '\n'
        # add AI client console messages as single plain text attachment
        msg = MIMEText(outtxt, 'plain')  # create MIME message
        outermime.attach(msg)  # attach MIME message to response

    print outermime.as_string()  # send MIME-formatted message
Пример #42
0
if __name__ == '__main__':
    gettext.install("ai", "/usr/lib/locale")
    DEFAULT_PORT = libaimdns.getinteger_property(com.SRVINST, com.PORTPROP)
    (PARAM_VERSION, SERVICE, NO_DEFAULT, FORM_DATA) = \
        get_parameters(cgi.FieldStorage())
    print >> sys.stderr, PARAM_VERSION, SERVICE, NO_DEFAULT, FORM_DATA
    if PARAM_VERSION == COMPATIBILITY_VERSION or SERVICE is None:
        # Old client
        (REQUEST_METHOD, REQUEST_PORT) = get_environment_information()
        if REQUEST_PORT == DEFAULT_PORT:  # only new clients use default port
            HOST = socket.gethostname()
            print 'Content-Type: text/html'  # HTML is following
            print  # blank line, end of headers
            print '<pre>'
            sys.stdout.write(_('error:must supply a service name\n'))
            sys.stdout.write(_('The request should look like:\n'))
            sys.stdout.write('<ol>http://%s:%d/cgi_get_manifest.py?'
                             'version=%s&service=<i>servicename</i></ol>' %
                             (HOST, DEFAULT_PORT, VERSION))
            print '</pre>'
            sys.exit(0)
        if REQUEST_METHOD == 'GET':
            send_needed_criteria(REQUEST_PORT)
        else:
            send_manifest(FORM_DATA, port=REQUEST_PORT)
    elif FORM_DATA is None:
        # do manifest table list
        list_manifests(SERVICE)
    else:
        # do manifest criteria match
Пример #43
0
def get_usage():
    ''' get usage for delete-service'''
    return(_('delete-service [-r|--autoremove] [-y|--noprompt] <svcname>]'))
Пример #44
0
def do_update_profile(cmd_options=None):
    ''' Updates exisiting profile
    Arg: cmd_options - command line options
    Effect: update existing profile 
    Raises SystemExit if condition cannot be handled
    '''

    options = parse_options(DO_UPDATE, cmd_options)

    # verify the file
    profile_file = options.profile_file[0]
    if not os.path.exists(profile_file):
        raise SystemExit(_("Error:\tFile does not exist: %s\n") % profile_file)

    # get profile name
    if not options.profile_name:
        profile_name = os.path.basename(profile_file)
    else:
        profile_name = options.profile_name

    # get AI service image path and database name
    service = AIService(options.service_name)
    dbname = service.database_path
    image_dir = service.image.path

    # open database
    dbn = AIdb.DB(dbname, commit=True)
    dbn.verifyDBStructure()
    queue = dbn.getQueue()

    # Handle old DB versions which did not store a profile.
    if not AIdb.tableExists(queue, AIdb.PROFILES_TABLE):
        raise SystemExit(
            _("Error:\tService %s does not support profiles") %
            options.service_name)

    # check for the existence of profile
    missing_profile_error = _("Error:\tService {service} has no profile "
                              "named {profile}.")
    if not sc.is_name_in_table(profile_name, queue, AIdb.PROFILES_TABLE):
        raise SystemExit(
            missing_profile_error.format(service=options.service_name,
                                         profile=profile_name))

    # validates the profile and report the errors if found
    raw_profile = df.validate_file(profile_name,
                                   profile_file,
                                   image_dir,
                                   verbose=False)
    if not raw_profile:
        raise SystemExit(1)

    # create file from string and report failures
    tmp_profile_path = copy_profile_internally(raw_profile)
    if not tmp_profile_path:
        raise SystemExit(1)

    # get the path of profile in db
    q_str = "SELECT file FROM " + AIdb.PROFILES_TABLE + " WHERE name=" \
                + AIdb.format_value('name', profile_name)
    query = AIdb.DBrequest(q_str)
    queue.put(query)
    query.waitAns()
    response = query.getResponse()
    # database error
    if response is None:
        raise SystemExit(
            missing_profile_error.format(service=options.service_name,
                                         profile=profile_name))

    db_profile_path = response[0][0]

    # replace the file
    try:
        shutil.copyfile(tmp_profile_path, db_profile_path)
    except IOError as err:
        raise SystemExit(
            _("Error:\t writing profile %s: %s") % (profile_name, err))
    finally:
        os.unlink(tmp_profile_path)

    print >> sys.stderr, _("Profile updated successfully.")
Пример #45
0
def get_usage():
    ''' get usage for rename-service'''
    return(_('rename-service\t<svcname> <newsvcname>'))
Пример #46
0
    def verify_AI_manifest(self):
        """
        Used for verifying and loading AI manifest as defined by
            DataFiles._AIschema.
        Args: None.
        Preconditions:  Expects its is_dtd variable to be set to determine
                        how to validate the AI manifest.
        Postconditions: Sets AI_root on success to a XML DOM of the AI
                        manifest.
        Raises: IOError on file open error.
                ValueError on validation error.
        """
        schema = file(self.AI_schema, 'r')

        try:
            xml_data = file(self.manifest_path, 'r')
        except AssertionError:
            # manifest path will be unset if we're not using a separate file
            # for A/I manifest so we must emulate a file
            xml_data = StringIO.StringIO(lxml.etree.tostring(self.AI_root))

        if self.is_dtd:
            self.AI_root, errors = verifyXML.verifyDTDManifest(
                self.AI_schema, xml_data)

            if errors:
                err = '\n'.join(errors)
                raise ValueError(
                    _("Error: AI manifest failed validation:\n%s") % err)

            ai_instance = self.AI_root.find(".//ai_instance")

        else:
            self.AI_root, errors = verifyXML.verifyRelaxNGManifest(
                schema, xml_data)

            if errors:
                # catch if we are not using a manifest we can name with
                # manifest_path
                try:
                    # manifest_path is a property that may raise an
                    # AssertionError
                    man_path = self.manifest_path
                    raise ValueError(
                        _("Error:\tFile %s failed validation:"
                          "\n\t%s") %
                        (os.path.basename(man_path), errors.message))
                # manifest_path will throw an AssertionError if it does not
                # have a path use a different error message
                except AssertionError:
                    raise ValueError(
                        _("Error: AI manifest failed validation:"
                          "\n\t%s") % errors.message)

            # Replace the <ai_manifest_file> element (if one exists) with an
            # <ai_embedded_manifest> element, using content from its referenced
            # file which was just loaded into the AI_root XML DOM
            ai_manifest_file = self.criteria_root.find(".//ai_manifest_file")

            if ai_manifest_file is not None:
                new_ai = lxml.etree.Element("ai_embedded_manifest")
                # add newlines to separate ai_embedded_manifest
                # from children
                new_ai.text = "\n\t"
                new_ai.tail = "\n"
                self.AI_root.getroot().tail = "\n"
                new_ai.append(self.AI_root.getroot())

                ai_manifest_file.getparent().replace(ai_manifest_file, new_ai)

            ai_instance = self.criteria_root.find(".//ai_manifest")

        # Set/update the name inside the DOM
        ai_instance.set("name", self.manifest_name)
def get_usage():
    ''' get usage for delete-manifest'''
    return (_('delete-manifest\t-m|--manifest <manifest/script name> \n'
              '\t\t-n|--service <svcname>'))
Пример #48
0
def verifyCriteriaDict(schema, criteria_dict, db, table=AIdb.MANIFESTS_TABLE):
    """
    Used for verifying and loading criteria from a dictionary of criteria.
    Args:       schema - path to schema file for criteria manifest.
                criteria_dict - dictionary of criteria to verify, in the form
                                of { criteria: value, criteria: value, ... }
                db - database object for install service
                table - database table, distinguishing manifests from profiles
    Raises IOError:
               * if the schema does not open
           ValueError:
                * if the criteria_dict dictionary is empty
                * if the XML is invalid according to the schema
           AssertionError:
                * if a value in the dictionary is empty
    Returns:    A valid XML DOM of the criteria and all MAC and IPV4 values
                are formatted according to verifyXML.prepValuesAndRanges().
    """
    schema = open(schema, 'r')

    if not criteria_dict:
        raise ValueError("Error:\tCriteria dictionary empty: %s\n" %
                         criteria_dict)

    root = lxml.etree.Element("ai_criteria_manifest")

    for name, value_or_range in criteria_dict.iteritems():

        if value_or_range is None:
            raise AssertionError(
                _("Error: Missing value for criteria "
                  "'%s'") % name)

        crit = lxml.etree.SubElement(root, "ai_criteria")
        crit.set("name", name)

        # If criteria is a range, split on "-" and add to
        # XML DOM as a range element.
        if AIdb.isRangeCriteria(db.getQueue(), name, table):
            # Split on "-"
            range_value = value_or_range.split('-', 1)

            # If there was only a single value, means user specified
            # this range criteria as a single value, add it as a single
            # value
            if len(range_value) == 1:
                value_elem = lxml.etree.SubElement(crit, "value")
                value_elem.text = value_or_range
            else:
                range_elem = lxml.etree.SubElement(crit, "range")
                range_elem.text = " ".join(range_value)
        else:
            value_elem = lxml.etree.SubElement(crit, "value")
            value_elem.text = value_or_range

    # Verify the generated criteria DOM
    root, errors = verifyXML.verifyRelaxNGManifest(
        schema, StringIO.StringIO(lxml.etree.tostring(root)))
    if errors:
        raise ValueError(
            _("Error: Criteria failed validation:\n\t%s") % errors.message)
    try:
        verifyXML.prepValuesAndRanges(root, db, table)
    except ValueError, err:
        raise ValueError(_("Error:\tCriteria error: %s") % err)
Пример #49
0
def get_help_usage():
    ''' get usage for help'''
    usage = _('help\t[<subcommand>]')
    return (usage)
def parse_options(cmd_options=None):
    '''
    Parse and validate options
    
    Returns: An options record containing
        arch
        aliasof
        bootargs
        dhcp_ip_count
        dhcp_ip_start
        dhcp_bootserver
        noprompt
        publisher
        srcimage
        svcname
        imagepath
    
    '''
    logging.log(com.XDEBUG, '**** START installadm.create_service.'
                'parse_options ****\n')

    usage = '\n' + get_usage()
    description = _('Establishes an Automated Install network service.')
    parser = OptionParser(usage=usage,
                          prog="create-service",
                          description=description)
    parser.add_option('-b',
                      '--boot-args',
                      dest='bootargs',
                      action='append',
                      default=list(),
                      help=_('Comma separated list of <property>=<value>'
                             ' pairs to add to the x86 Grub menu entry'))
    parser.add_option('-a',
                      '--arch',
                      dest='arch',
                      default=None,
                      choices=("i386", "sparc"),
                      help=_("ARCHITECTURE (sparc or i386), desired "
                             "architecture of resulting service when creating "
                             "from a pkg."))
    parser.add_option('-d',
                      '--imagepath',
                      dest='imagepath',
                      default=None,
                      help=_("Path at which to create the net image"))
    parser.add_option('-t',
                      '--aliasof',
                      dest='aliasof',
                      default=None,
                      help=_("Service being created is alias of this serivce"))
    parser.add_option('-n',
                      '--service',
                      dest='svcname',
                      help=_('service name'))
    parser.add_option('-i',
                      '--ip-start',
                      dest='dhcp_ip_start',
                      type='string',
                      help=_('DHCP Starting IP Address'),
                      action="callback",
                      callback=check_ip_address)
    parser.add_option('-c',
                      '--ip-count',
                      dest='dhcp_ip_count',
                      type='int',
                      help=_('DHCP Count of IP Addresses'))
    parser.add_option('-B',
                      '--bootfile-server',
                      dest='dhcp_bootserver',
                      type='string',
                      help=_('DHCP Boot Server Address'),
                      action="callback",
                      callback=check_ip_address)
    parser.add_option('-s',
                      '--source',
                      dest='srcimage',
                      type='string',
                      help=_('FMRI or Auto Install ISO'))
    parser.add_option('-p',
                      '--publisher',
                      help=_(
                          "A pkg(5) publisher, in the"
                          " form '<prefix>=<uri>', from which to install the "
                          "client image"))
    parser.add_option('-y',
                      "--noprompt",
                      action="store_true",
                      dest="noprompt",
                      default=False,
                      help=_('Suppress confirmation prompts and proceed with '
                             'service creation using default values'))

    options, args = parser.parse_args(cmd_options)

    if args:
        parser.error(_('Unexpected argument(s): %s') % args)

    # if service name provided, validate it
    if options.svcname:
        try:
            com.validate_service_name(options.svcname)
        except ValueError as err:
            parser.error(err)

        # Give error if service already exists
        if config.is_service(options.svcname):
            parser.error(_('\nService already exists: %s\n') % options.svcname)

    # If creating an alias, only allow additional options -n, -b,
    # and -y
    if options.aliasof:
        if (options.dhcp_ip_start or options.dhcp_ip_count or options.imagepath
                or options.srcimage):
            parser.error(
                _('\nOnly options -n|--service, -b|--boot-args, '
                  'and -y|--noprompt\nmay be specified with '
                  '-t|--aliasof.'))
        if not options.svcname:
            parser.error(
                _('\nOption -n|--service is required with the '
                  '-t|--aliasof option'))
    else:
        name = options.svcname
        if name in DEFAULT_ARCH:
            raise SystemExit(
                _('\nDefault services must be created as '
                  'aliases. Use -t|--aliasof.\n'))

    # provide default for srcimage, now that we're done option checking
    if options.srcimage is None:
        options.srcimage = "pkg:/install-image/solaris-auto-install"

    # check dhcp related options
    if options.dhcp_ip_start or options.dhcp_ip_count:
        if com.is_multihomed():
            # don't allow DHCP setup if multihomed
            parser.error(
                cw(
                    _('\nDHCP server configuration is unavailable on '
                      'hosts with multiple network interfaces (-i and '
                      '-c options are disallowed).\n')))

        # Confirm options -i and -c are both provided
        if options.dhcp_ip_count is None:
            parser.error(
                _('\nIf -i option is provided, -c option must '
                  'also be provided\n'))
        if not options.dhcp_ip_start:
            parser.error(
                _('\nIf -c option is provided, -i option must '
                  'also be provided\n'))

        # Confirm count of ip addresses is positive
        if options.dhcp_ip_count < 1:
            parser.error(
                _('\n"-c <count_of_ipaddr>" must be greater than '
                  'zero.\n'))

    if options.dhcp_bootserver:
        # Confirm if the -B is provided, that -i/-c are also
        if options.dhcp_ip_count is None:
            parser.error(
                _('\nIf -B option is provided, -i option must '
                  'also be provided\n'))

    if is_iso(options.srcimage):
        if options.arch is not None:
            parser.error(
                _("The --arch option is invalid for ISO-based "
                  "services"))
        if options.publisher is not None:
            parser.error(
                _("The --publisher option is invalid for "
                  "ISO-based services"))

    if options.publisher:
        # Convert options.publisher from a string of form 'prefix=uri' to a
        # tuple (prefix, uri)
        publisher = options.publisher.split("=")
        if len(publisher) != 2:
            parser.error(
                _('Publisher information must match the form: '
                  '"<prefix>=<URI>"'))
        options.publisher = publisher

    # Make sure imagepath meets requirements
    if options.imagepath:
        options.imagepath = options.imagepath.strip()
    if options.imagepath:
        if not options.imagepath == '/':
            options.imagepath = options.imagepath.rstrip('/')
        try:
            check_imagepath(options.imagepath)
        except ValueError as error:
            raise SystemExit(error)

    return options
Пример #51
0
def get_enable_usage():
    ''' get usage for enable'''
    usage = _('enable\t<svcname>')
    return (usage)
Пример #52
0
class InstalladmImage(object):
    '''Represents an AI client image on the installadm server'''

    INVALID_AI_IMAGE = _("\nError:\tThe image at %(path)s is not a valid "
                         "Automated Installer image.")

    def __init__(self, image_path):
        self._path = image_path
        self._arch = None
        self._version = None

    def verify(self):
        '''
        Check that the image directory exists, appears to be a valid net
        boot image (has a solaris.zlib file), and is a valid Automated
        Installer image (has an auto_install/ai.dtd file).
        Raises: ImageError if path checks fail
        Pre-conditions: Expects self.path to return a valid image_path
        Returns: None
        '''
        # check image_path exists
        if not os.path.isdir(self.path):
            raise ImageError(
                cw(
                    _("\nError:\tThe image path (%s) is not "
                      "a directory. Please provide a "
                      "different image path.\n") % self.path))

        # check that the image_path has solaris.zlib and
        # auto_install/ai.dtd files
        if not (os.path.exists(os.path.join(self.path, "solaris.zlib"))
                and os.path.exists(
                    os.path.join(self.path, "auto_install/ai.dtd"))):
            raise ImageError(cw(self.INVALID_AI_IMAGE % {"path": self.path}))

    @property
    def version(self):
        '''Returns the AI client image version.
        
        See also the module docstring.
        
        '''
        if self._version is None:
            version = self.read_image_info().get("image_version", "0.0")
            try:
                version = float(version)
            except (ValueError, TypeError):
                version = 0.0
            self._version = version
        return self._version

    def read_image_info(self):
        '''Reads the .image_info file for this image, returning a dictionary
        of its contents. The keys are set to lower-case.
        
        '''
        image_info = dict()
        with open(os.path.join(self.path, ".image_info"), "r") as info:
            for line in info:
                key, valid, value = line.strip().partition("=")
                if valid:
                    image_info[key.lower()] = value
        return image_info

    def move(self, new_path):
        '''Move image area to new location and update webserver symlinks.
           To rename self._path, caller should ensure new_path does not exist.
           Return new image path
        '''
        self._remove_ai_webserver_symlink()
        try:
            os.makedirs(os.path.dirname(new_path))
        except OSError as err:
            if err.errno != errno.EEXIST:
                raise
        # Use shutil.move rather than os.rename to allow move across
        # filesystems.
        shutil.move(self._path, new_path)
        self._path = new_path
        self._prep_ai_webserver()
        return self._path

    @property
    def path(self):
        '''
        Returns the image path
        '''
        return self._path

    @property
    def arch(self):
        '''
        Provide the image's architecture (and caches the answer)
        Raises: AssertionError if the image does not have a /platform [sun4u,
                sun4v, i86pc, amd64]
        Pre-conditions: Expects self.path to return a valid image path
        Returns: "sparc" or "i386" as appropriate
        '''
        if self._arch is None:
            platform = os.path.join(self.path, "platform")
            for root, dirs, files in os.walk(platform):
                if "i86pc" in dirs or "amd64" in dirs:
                    self._arch = "i386"
                elif "sun4v" in dirs or "sun4u" in dirs:
                    self._arch = "sparc"
                else:
                    raise ImageError(
                        _("\nError:\tUnable to determine "
                          "architecture of image.\n"))
                break
        return self._arch

    def _remove_ai_webserver_symlink(self):
        '''Remove the ai webserver symlink for this image'''
        dest = os.path.join(com.WEBSERVER_DOCROOT, self.path.lstrip("/"))
        if os.path.islink(dest) or os.path.exists(dest):
            os.remove(dest)

        # remove empty parent directories up until com.WEBSERVER_DOCROOT
        parent = os.path.dirname(dest)
        while parent != com.WEBSERVER_DOCROOT:
            try:
                os.rmdir(parent)
                parent = os.path.dirname(parent)
            except OSError:
                # break if directory is non-empty
                break

    def _prep_ai_webserver(self):
        '''Enable the AI webserver to access the image path'''
        target_path = os.path.dirname(self.path).lstrip("/")
        try:
            os.makedirs(os.path.join(com.WEBSERVER_DOCROOT, target_path))
        except OSError as err:
            if err.errno != errno.EEXIST:
                raise

        dest = os.path.join(com.WEBSERVER_DOCROOT, self.path.lstrip("/"))

        if os.path.islink(dest) or os.path.exists(dest):
            os.remove(dest)
        os.symlink(self.path, dest)
Пример #53
0
def do_disable_service(cmd_options=None):
    ''' Disable a service

    Disable the specified service and optionally update the service's
    properties to reflect the new status.

    Input:
        List of command line options
    Return:
        None
    Raises:
        SystemExit if missing permissions, invalid service name, or
        if attempt to place smf service in maintenance fails.

    '''
    logging.debug('**** START do_disable_service ****')

    usage = '\n' + get_disable_usage()
    parser = OptionParser(usage=usage)

    (options, args) = parser.parse_args(cmd_options)

    # Check for privileges
    if os.geteuid() != 0:
        raise SystemExit(
            _("Error: Root privileges are required for "
              "this command."))

    # Check for correct number of args
    if len(args) != 1:
        if len(args) == 0:
            parser.error(_("Missing required argument, <svcname>"))
        else:
            parser.error(_("Too many arguments: %s") % args)

    svcname = args[0]

    # validate service name
    try:
        validate_service_name(svcname)
    except ValueError as err:
        raise SystemExit(err)

    if not config.is_service(svcname):
        err_msg = _("The service does not exist: %s\n") % svcname
        parser.error(err_msg)

    prop_data = config.get_service_props(svcname)

    if prop_data and config.PROP_STATUS not in prop_data:
        err_msg = _("The property, status, is missing for %s.\n") % svcname
        parser.error(err_msg)

    if prop_data[config.PROP_STATUS] == config.STATUS_OFF:
        err_msg = _("The service is not running: %s\n") % svcname
        parser.error(err_msg)

    try:
        logging.debug("Disabling install service %s", svcname)
        service = AIService(svcname)
        service.disable(force=True)
    except (config.ServiceCfgError, aismf.ServicesError, MountError) as err:
        raise SystemExit(err)
    except CalledProcessError:
        return 1
Пример #54
0
    def image_create(cls, fmri_or_p5i, targetdir, arch=None, publisher=None):
        logging.debug("image_create, install from=%s", fmri_or_p5i)
        tracker = pkg.client.progress.CommandLineProgressTracker()
        root_img = pkg.client.api.ImageInterface("/", PKG5_API_VERSION,
                                                 tracker, None,
                                                 cls._PKG_CLIENT_NAME)

        # In the future, handle:
        #    * SSL repos (keys/certs may need explicit flags from user)
        if publisher is not None:
            prefix = publisher[0]
            order = [prefix]
            repo = pkg.client.publisher.Repository(origins=[publisher[1]])
            pub = pkg.client.publisher.Publisher(prefix, repository=repo)
            publishers = {prefix: pub}
        else:
            publishers = dict()
            order = list()
            for pub in root_img.get_publishers(duplicate=True):
                if pub.disabled:
                    logging.debug("skipping disabled publisher '%s'",
                                  pub.prefix)
                    continue
                publishers[pub.prefix] = pub
                order.append(pub.prefix)

            if not publishers:
                raise ImageError(
                    _("\nError:\tThere are no enabled "
                      "publishers.\n"))

        if arch is None:
            arch = root_img.img.get_variants()[cls.ARCH_VARIANT]
        variants = {cls.ARCH_VARIANT: arch}

        props = {pkg.client.imageconfig.FLUSH_CONTENT_CACHE: True}
        pkgimg = pkg.client.api.image_create(cls._PKG_CLIENT_NAME,
                                             PKG5_API_VERSION,
                                             targetdir,
                                             pkg.client.imagetypes.IMG_USER,
                                             is_zone=False,
                                             progtrack=tracker,
                                             props=props,
                                             variants=variants)

        # Add publishers to the new image, preserving the original
        # search order
        search_after = None
        for pub_prefix in order:
            add_pub = publishers[pub_prefix]
            pkgimg.add_publisher(add_pub, search_after=search_after)
            logging.debug("adding publisher '%s' after '%s'", add_pub.prefix,
                          search_after)
            search_after = pub_prefix

        ai_img = cls(targetdir, pkg_image=pkgimg)
        ai_img._install_package(fmri_or_p5i)
        ai_img.verify()
        ai_img._prep_ai_webserver()

        return ai_img
Пример #55
0
    def check_update(self, fmri=None, publisher=None):
        '''Checks to see if any updates are available for this image.
           If so, self.pkg_image will be left "ready" to complete the
           update.

        Input: fmri - pkg to which to potentially update
               publisher - tuple (prefix, origin) to use for update. If
                           that publisher already exists in the image, its
                           origins/mirrors are reset to the passed in origin.
                           Otherwise, the new publisher is added. All other
                           publishers in the image are removed.
        Returns: True if update available; False if not

        '''
        logging.debug('check_update fmri=%s, publisher=%s', fmri, publisher)
        logging.debug("currently installed pfmri is: %s",
                      self.get_installed_pfmri())
        if fmri is not None:
            # validate fmri specified by user
            pkgfmri = self.check_fmri(fmri)
            fmri = [str(pkgfmri)]

        if publisher is not None:
            new_repo = pkg.client.publisher.Repository(origins=[publisher[1]])
            new_repo_uri = new_repo.origins[0].uri
            new_pub = pkg.client.publisher.Publisher(publisher[0],
                                                     repository=new_repo)
            if fmri:
                # ensure that user didn't specify conflicting publisher names
                if pkgfmri.publisher and pkgfmri.publisher != new_pub.prefix:
                    raise ValueError(cw(
                        _('\nError: FMRI publisher, "%(pub1)s", does not '
                          'match specified --publisher, "%(pub2)s".\n' %
                          {'pub1': pkgfmri.publisher,
                           'pub2': new_pub.prefix})))

            # Replace existing publisher(s) with that specified by user
            same_pub = None
            if self.pkg_image.has_publisher(new_pub.prefix):
                # Specified publisher already exists
                same_pub = self.pkg_image.get_publisher(new_pub.prefix,
                                                        duplicate=True)
                logging.debug('basesvc has same pub %s', same_pub.prefix)
                logging.debug('origins are:\n%s', '\n'.join(orig.uri for
                              orig in same_pub.repository.origins))
                logging.debug('replacing origins with new uri, %s',
                              new_repo_uri)
                same_pub.repository.reset_origins()
                same_pub.repository.reset_mirrors()
                same_pub.repository.add_origin(new_repo_uri)
                self.pkg_image.update_publisher(same_pub, search_first=True)
            else:
                # create a new publisher
                logging.debug('adding pub %s, origin %s',
                              new_pub.prefix, new_repo_uri)
                self.pkg_image.add_publisher(new_pub, search_first=True)

            # Remove any other publishers
            for pub in self.pkg_image.get_publishers(duplicate=True)[1:]:
                logging.debug('removing pub %s', pub.prefix)
                self.pkg_image.remove_publisher(prefix=pub.prefix)

        for plan_desc in self.pkg_image.gen_plan_update(pkgs_update=fmri):
            continue
        return (not self.pkg_image.planned_nothingtodo())
Пример #56
0
class InstalladmPkgImage(InstalladmImage):
    '''Handles creation of a pkg(5)-based InstalladmImage'''

    _PKG_CLIENT_NAME = "installadm"
    DEFAULT_PKG_NAME = 'install-image/solaris-auto-install'
    ARCH_VARIANT = u'variant.arch'
    SVC_NAME_ATTR = 'com.oracle.install.service-name'
    INVALID_AI_IMAGE = _(
        "\nError:\tThe pkg image is not an Automated Installer image.\n")

    def __init__(self, image_path, pkg_image=None):
        super(InstalladmPkgImage, self).__init__(image_path)
        self._pkgimg = pkg_image

    @classmethod
    def image_create(cls, fmri_or_p5i, targetdir, arch=None, publisher=None):
        logging.debug("image_create, install from=%s", fmri_or_p5i)
        tracker = pkg.client.progress.CommandLineProgressTracker()
        root_img = pkg.client.api.ImageInterface("/", PKG5_API_VERSION,
                                                 tracker, None,
                                                 cls._PKG_CLIENT_NAME)

        # In the future, handle:
        #    * SSL repos (keys/certs may need explicit flags from user)
        if publisher is not None:
            prefix = publisher[0]
            order = [prefix]
            repo = pkg.client.publisher.Repository(origins=[publisher[1]])
            pub = pkg.client.publisher.Publisher(prefix, repository=repo)
            publishers = {prefix: pub}
        else:
            publishers = dict()
            order = list()
            for pub in root_img.get_publishers(duplicate=True):
                if pub.disabled:
                    logging.debug("skipping disabled publisher '%s'",
                                  pub.prefix)
                    continue
                publishers[pub.prefix] = pub
                order.append(pub.prefix)

            if not publishers:
                raise ImageError(
                    _("\nError:\tThere are no enabled "
                      "publishers.\n"))

        if arch is None:
            arch = root_img.img.get_variants()[cls.ARCH_VARIANT]
        variants = {cls.ARCH_VARIANT: arch}

        props = {pkg.client.imageconfig.FLUSH_CONTENT_CACHE: True}
        pkgimg = pkg.client.api.image_create(cls._PKG_CLIENT_NAME,
                                             PKG5_API_VERSION,
                                             targetdir,
                                             pkg.client.imagetypes.IMG_USER,
                                             is_zone=False,
                                             progtrack=tracker,
                                             props=props,
                                             variants=variants)

        # Add publishers to the new image, preserving the original
        # search order
        search_after = None
        for pub_prefix in order:
            add_pub = publishers[pub_prefix]
            pkgimg.add_publisher(add_pub, search_after=search_after)
            logging.debug("adding publisher '%s' after '%s'", add_pub.prefix,
                          search_after)
            search_after = pub_prefix

        ai_img = cls(targetdir, pkg_image=pkgimg)
        ai_img._install_package(fmri_or_p5i)
        ai_img.verify()
        ai_img._prep_ai_webserver()

        return ai_img

    @property
    def pkg_image(self):
        if self._pkgimg is None:
            tracker = pkg.client.progress.CommandLineProgressTracker()
            # installadm is non-interactive, so we don't need to track
            # the "cancel_state" like, for example, packagemanager
            cancel_state_callable = None
            self._pkgimg = pkg.client.api.ImageInterface(
                self.path, PKG5_API_VERSION, tracker, cancel_state_callable,
                self._PKG_CLIENT_NAME)
        return self._pkgimg

    def _install_package(self, fmri_or_p5i):
        try:
            p5i_data = self.pkg_image.parse_p5i(location=fmri_or_p5i)

            # Returns a list of tuples; should only be one publisher with
            # one package
            if len(p5i_data) != 1:
                raise ImageError(
                    _("\nError:\tMore than one publisher "
                      "in p5i file.\n"))

            pub, pkgs = p5i_data[0]
            if len(pkgs) != 1:
                raise ImageError(
                    _("\nError:\tMore than one package "
                      "in p5i file.\n"))

            if pub and self.pkg_image.has_publisher(prefix=pub.prefix):
                img_pub = self.pkg_image.get_publisher(prefix=pub.prefix,
                                                       duplicate=True)
                for origin in pub.repository.origins:
                    if not img_pub.repository.has_origin(origin):
                        img_pub.repository.add_origin(origin)
                for mirror in pub.repository.mirrors:
                    if not img_pub.repository.has_mirror(mirror):
                        img_pub.repository.add_mirror(mirror)
                self.pkg_image.update_publisher(img_pub)
            elif pub:
                self.pkg_image.add_publisher(pub)
        except (pkg.client.api_errors.InvalidP5IFile,
                pkg.client.api_errors.RetrievalError):
            pkgs = [fmri_or_p5i]

        self.pkg_image.plan_install(pkgs)

        # accept licenses
        plan = self.pkg_image.describe()
        for pfmri, src, dest, accepted, displayed in plan.get_licenses():
            if not dest.must_accept:
                continue
            self.pkg_image.set_plan_license_status(pfmri,
                                                   dest.license,
                                                   accepted=True)

        self.pkg_image.prepare()
        self.pkg_image.execute_plan()
        self.pkg_image.reset()

    def get_basename(self):
        '''Get pkg service basename '''
        basename = "solarisx"
        try:
            pkg_list = self.pkg_image.get_pkg_list(
                pkg.client.api.ImageInterface.LIST_INSTALLED,
                raise_unmatched=True,
                return_fmris=True)

            for pfmri, summ, cats, states, attrs in pkg_list:
                manifest = self.pkg_image.get_manifest(pfmri)
                for action in manifest.gen_actions_by_type("set"):
                    for attrval in action.attrlist("name"):
                        if (attrval == self.SVC_NAME_ATTR and action.attrs.get(
                                "variant.arch", self.arch) == self.arch):
                            basename = action.attrs["value"].strip()

        except pkg.client.api_errors.ApiException:
            pass

        logging.debug("get_basename returning %s", basename)
        return basename
Пример #57
0
def get_usage():
    ''' get usage for delete-profile'''
    return _("delete-profile\t-p|--profile <profile_name> ... "
             "-n|--service <svcname>")
Пример #58
0
def do_rename_service(cmd_options=None):
    '''Rename a service.

    Note: Errors that occur during the various rename stages
    are printed, but the other stages will continue, with the hopes
    of leaving the final product as close to functional as possible

    '''
    # check that we are root
    if os.geteuid() != 0:
        raise SystemExit(_("Error: Root privileges are required for this "
                           "command.\n"))

    (svcname, newsvcname) = parse_options(cmd_options)

    # Ensure the service to rename is a valid service
    if not config.is_service(svcname):
        raise SystemExit(_("\nFailed to find service %s\n") % svcname)

    # Ensure the new name is not already a service
    if config.is_service(newsvcname):
        raise SystemExit(_("\nService or alias already exists: %s\n") %
                           newsvcname)

    # Don't allow renaming to/from the 'default-<arch>' aliases
    if svcname in DEFAULT_ARCH:
        raise SystemExit(_('\nYou may not rename the "%s" service.\n') %
                           svcname)

    if newsvcname in DEFAULT_ARCH:
        raise SystemExit(cw(_('\nYou may not rename a service to be the '
                              'default service for an architecture. To create '
                              'the default-sparc or default-i386 service '
                              'aliases, use "installadm create-service '
                              '-t|--aliasof."\n')))

    # Unmount old service
    was_mounted = False
    try:
        oldservice = AIService(svcname)
        if oldservice.mounted():
            was_mounted = True
            logging.debug("disabling %s", svcname)
            oldservice.disable(force=True)
    except (MountError, ImageError) as err:
        raise SystemExit(err)

    # remove old mountpoint
    try:
        os.rmdir(oldservice.mountpoint)
    except OSError as err:
        # Just make a note if unable to cleanup mountpoint
        logging.debug(err)

    # Remove clients whose base service has been renamed
    clients = config.get_clients(svcname)
    for clientid in clients.keys():
        clientctrl.remove_client(clientid)

    oldservice.rename(newsvcname)

    # Update aliases whose base service has been renamed
    aliases = config.get_aliased_services(svcname)
    failures = list()
    for alias in aliases:
        props = {config.PROP_ALIAS_OF: newsvcname}
        config.set_service_props(alias, props)

    # Mount the renamed service if it was mounted
    newservice = AIService(newsvcname)
    if was_mounted:
        try:
            logging.debug("enabling %s", newsvcname)
            newservice.enable()
        except (MountError, ImageError) as err:
            failures.append(err)
            print >> sys.stderr, err

    # Re-add clients whose base service has been renamed
    arch = newservice.arch
    for clientid in clients.keys():
        # strip off leading '01'
        client = clientid[2:]
        bootargs = None
        if config.BOOTARGS in clients[clientid]:
            bootargs = clients[clientid][config.BOOTARGS]
        create_client.create_new_client(arch, newservice, client,
                                        bootargs=bootargs)

    if failures:
        return 1
    else:
        return 0
Пример #59
0
def parse_options(do_create, cmd_options=None):
    """ Parse and validate options
    Args:  - do_create (True) or do_update (False) 
           - cmd_options - command line handled by OptionParser

    Returns: options
    """

    if do_create:
        usage = '\n' + get_create_usage()
    else:
        usage = '\n' + get_update_usage()

    parser = OptionParser(usage=usage)

    if do_create:
        parser.add_option("-C",
                          "--criteria-file",
                          dest="criteria_file",
                          default='',
                          help=_("Name of criteria XML file."))
        parser.add_option("-c",
                          "--criteria",
                          dest="criteria_c",
                          action="append",
                          default=list(),
                          metavar="CRITERIA",
                          help=_("Criteria: <-c criteria=value|range> ..."))

    parser.add_option("-f",
                      "--file",
                      dest="profile_file",
                      action="append",
                      default=list(),
                      help=_("Path to profile file"))
    parser.add_option("-p",
                      "--profile",
                      dest="profile_name",
                      default='',
                      help=_("Name of profile"))
    parser.add_option("-n",
                      "--service",
                      dest="service_name",
                      default="",
                      help=_("Name of install service."))

    options, args = parser.parse_args(cmd_options)

    if len(args):
        parser.error(_("Unexpected arguments: %s" % args))

    if not do_create:
        options.criteria_file = None
        options.criteria_c = None
        if len(options.profile_file) > 1:
            parser.error(_("Provide only one file name (-f)."))

    if not options.service_name:
        parser.error(_("Service name is required (-n <service name>)."))
    if not options.profile_file:
        parser.error(_("Profile file is required (-f <profile file>)."))
    if options.profile_name and len(options.profile_file) > 1:
        parser.error(
            _("If a profile name is specified (-p), only one file "
              "name may be specified (-f)."))

    if not config.is_service(options.service_name):
        raise SystemExit(_("No such service: %s") % options.service_name)

    return options
Пример #60
0
def do_create_profile(cmd_options=None):
    ''' external entry point for installadm
    Arg: cmd_options - command line options
    Effect: add profiles to database per command line
    Raises SystemExit if condition cannot be handled
    '''
    options = parse_options(DO_CREATE, cmd_options)

    # get AI service image path and database name
    service = AIService(options.service_name)
    image_dir = service.image.path
    dbname = service.database_path

    # open database
    dbn = AIdb.DB(dbname, commit=True)
    dbn.verifyDBStructure()
    queue = dbn.getQueue()
    root = None
    criteria_dict = dict()

    # Handle old DB versions which did not store a profile.
    if not AIdb.tableExists(queue, AIdb.PROFILES_TABLE):
        raise SystemExit(
            _("Error:\tService %s does not support profiles") %
            options.service_name)
    try:
        if options.criteria_file:  # extract criteria from file
            root = df.verifyCriteria(df.DataFiles.criteriaSchema,
                                     options.criteria_file, dbn,
                                     AIdb.PROFILES_TABLE)
        elif options.criteria_c:
            # if we have criteria from cmd line, convert into dictionary
            criteria_dict = pub_man.criteria_to_dict(options.criteria_c)
            root = df.verifyCriteriaDict(df.DataFiles.criteriaSchema,
                                         criteria_dict, dbn,
                                         AIdb.PROFILES_TABLE)
    except ValueError as err:
        raise SystemExit(_("Error:\tcriteria error: %s") % err)
    # Instantiate a Criteria object with the XML DOM of the criteria.
    criteria = df.Criteria(root)
    sc.validate_criteria_from_user(criteria, dbn, AIdb.PROFILES_TABLE)
    # track exit status for all profiles, assuming no errors
    has_errors = False

    # loop through each profile on command line
    for profile_file in options.profile_file:
        # take option name either from command line or from basename of profile
        if options.profile_name:
            profile_name = options.profile_name
        else:
            profile_name = os.path.basename(profile_file)
        # check for any scope violations
        if sc.is_name_in_table(profile_name, queue, AIdb.PROFILES_TABLE):
            print >> sys.stderr, \
                    _("Error:  A profile named %s is already in the database "
                      "for service %s.") % (profile_name, options.service_name)
            has_errors = True
            continue
        # open profile file specified by user on command line
        if not os.path.exists(profile_file):
            print >> sys.stderr, _("File %s does not exist") % profile_file
            has_errors = True
            continue

        # validates the profile and report errors if found
        raw_profile = df.validate_file(profile_name,
                                       profile_file,
                                       image_dir,
                                       verbose=False)
        if not raw_profile:
            has_errors = True
            continue

        # create file from profile string and report failures
        full_profile_path = copy_profile_internally(raw_profile)
        if not full_profile_path:
            has_errors = True
            continue

        # add new profile to database
        if not add_profile(criteria, profile_name, full_profile_path, queue,
                           AIdb.PROFILES_TABLE):
            os.unlink(full_profile_path)  # failure, back out internal profile
            has_errors = True
    # exit with status if any errors in any profiles
    if has_errors:
        sys.exit(1)