コード例 #1
0
ファイル: installadm.py プロジェクト: alhazred/caiman
def do_enable_service(cmd_options=None):
    ''' Enable a service

    Parse the supplied arguments then enable the specified service.

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

    '''
    logging.log(XDEBUG, '**** START do_enable_service ****')

    # check for authorization and euid
    try:
        check_auth_and_euid(SERVICE_AUTH)
    except UnauthorizedUserError as err:
        raise SystemExit(err)

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

    args = parser.parse_args(cmd_options)[1]

    # 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]

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

    # Verify that the server settings are not obviously broken.
    # These checks cannot be complete, but do check for things
    # which will definitely cause failure.
    ret = Popen([CHECK_SETUP_SCRIPT]).wait()
    if ret:
        return 1

    logging.log(XDEBUG, 'Enabling install service %s', svcname)
    try:
        service = AIService(svcname)
        service.enable()
    except (aismf.ServicesError, config.ServiceCfgError, ImageError,
            MountError) as err:
        raise SystemExit(err)
    except InvalidServiceError as err:
        raise SystemExit(
            cw(
                _("\nThis service may not be enabled until all "
                  "invalid manifests and profiles have been "
                  "corrected or removed.\n")))
コード例 #2
0
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)
コード例 #3
0
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
コード例 #4
0
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
コード例 #5
0
    def __init__(self, sname):
        '''
        Opens database for given service and sets database request queue
        '''
        self.name = sname
        try:
            self.service = AIService(sname)

        except VersionError as err:
            warn_version(err)
            raise

        path = self.service.database_path

        if os.path.exists(path):
            try:
                maisql = AIdb.DB(path)
                maisql.verifyDBStructure()
                self.aiqueue = maisql.getQueue()

            except StandardError as err:
                sys.stderr.write(
                    _('Error: AI database access error\n%s\n') % err)
                raise
        else:
            sys.stderr.write(
                _('Error: unable to locate AI database for "%s" '
                  'on server\n') % sname)
            # I can't read from service database and I should raise an error
            # for this condition.
            raise StandardError
コード例 #6
0
ファイル: client_control.py プロジェクト: alhazred/caiman
def remove_client(client_id, suppress_dhcp_msgs=False):
    ''' Remove client configuration

        If client configuration incomplete (e.g., dangling symlink),
        cleanup anyway. Optionally suppress dhcp informational messages.

     '''
    logging.debug("Removing client config for %s, suppress_dhcp_msgs=%s",
                  client_id, suppress_dhcp_msgs)

    (service, datadict) = config.find_client(client_id)
    if datadict:
        more_files = datadict.get(config.FILES, list())
    else:
        more_files = list()
    if service:
        # remove client info from .config file
        config.remove_client_from_config(service, client_id)
        if AIService(service).arch == 'i386':
            # suggest dhcp unconfiguration
            remove_client_dhcp_config(client_id, suppress_dhcp_msgs)

    # remove client specific symlinks/files
    logging.debug("Cleaning up files %s", more_files)
    _cleanup_files(client_id, more_files)
コード例 #7
0
ファイル: delete_profile.py プロジェクト: alhazred/caiman
def do_delete_profile(cmd_options=None):
    ''' external entry point for installadm
    Arg: cmd_options - command line options
    Effect: delete profiles per command line
    '''
    # check for authorization and euid
    try:
        check_auth_and_euid(PROFILE_AUTH)
    except UnauthorizedUserError as err:
        raise SystemExit(err)

    options = parse_options(cmd_options)

    # get AI service directory, database name
    service = AIService(options.service_name)
    dbname = service.database_path

    # Open the database
    aisql = AIdb.DB(dbname, commit=True)
    aisql.verifyDBStructure()

    # delete profiles per command line
    errs = delete_profiles(options.profile_name, aisql, AIdb.PROFILES_TABLE)
    if errs:
        sys.exit(1)
コード例 #8
0
ファイル: create_service.py プロジェクト: alhazred/caiman
def should_be_default_for_arch(newservice):
    '''
    Determine if newservice should be the baseservice of default-<arch>
    (i.e., first service of architecture and aliasable)

    Input: service object for newly created service
    Returns: True if default-<arch> alias should be created
             False otherwise

    '''
    if newservice.image.version < 3:
        return False
    services = config.get_all_service_names()
    make_default = True
    for service in services:
        if service == newservice.name:
            continue
        svc = AIService(service)
        try:
            props = config.get_service_props(service)
            config.verify_key_properties(service, props)
        except config.ServiceCfgError as err:
            # if service is missing keys, print info and skip it
            print >> sys.stderr, err
            continue
        if svc.arch == newservice.arch and svc.image.version >= 3:
            make_default = False
            break

    logging.debug("should_be_default_for_arch service %s, arch=%s, returns %s",
                  newservice.name, newservice.arch, make_default)
    return make_default
コード例 #9
0
ファイル: publish_manifest.py プロジェクト: alhazred/caiman
def do_update_manifest(cmd_options=None):
    '''
    Update the contents of an existing manifest.

    '''
    # check for authorization and euid
    try:
        check_auth_and_euid(MANIFEST_AUTH)
    except UnauthorizedUserError as err:
        raise SystemExit(err)

    # 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)
コード例 #10
0
def should_be_default_for_arch(newservice):
    '''
    Determine if newservice should be the baseservice of default-<arch>
    (i.e., first service of architecture and aliasable)

    Input: service object for newly created service
    Returns: True if default-<arch> alias should be created
             False otherwise

    '''
    if newservice.image.version < 3:
        return False
    services = config.get_all_service_names()
    make_default = True
    for service in services:
        if service == newservice.name:
            continue
        svc = AIService(service)
        if svc.arch == newservice.arch and svc.image.version >= 3:
            make_default = False
            break

    logging.debug("should_be_default_for_arch service %s, arch=%s, returns %s",
                  newservice.name, newservice.arch, make_default)
    return make_default
コード例 #11
0
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)
コード例 #12
0
ファイル: delete_manifest.py プロジェクト: alhazred/caiman
def parse_options(cmd_options=None):
    """
    Parse and validate options
    """

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

    parser.add_option("-m", "--manifest", dest="manifest_name",
                      default=None, help=_("Name of manifest"))
    parser.add_option("-n", "--service", dest="service_name",
                      default=None, help=_("Name of install service."))
    parser.add_option("-i", "--instance", dest="instance", default=None,
                      help=_("manifest instance to remove (internal option)"),
                      type="int", metavar="manifest instance")

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

    # check for required options
    if options.service_name is None:
        parser.error(_("Service name is required "
                       "(-n|--service <service name>)."))
    if options.manifest_name is None:
        parser.error(_("Manifest name is required "
                       "(-m|--manifest <manifest_name>)."))
    if args:
        parser.error(_("Unexpected argument(s): %s" % args))

    if not config.is_service(options.service_name):
        raise SystemExit(_("Not a valid service: %s") % options.service_name)

    options.svcdir_path = AIService(options.service_name).config_dir
    logging.debug("options = %s", options)
    return options
コード例 #13
0
def find_clients(lservices, sname=None):
    """
    find_clients() returns a dictionary that contains a list of
    dictionaries.

    The service name is the key for the main dictionary and the
    client, image path, and arch are members of the subdictionary,
    as follows:

        {
          'service1': [
                { 'ipath':<path1>, 'client':<client1>, 'arch': <arch>},
                ....
                      ],
          ....
        }

    Args
        lservices = config.get_all_service_props()
        sname - service name, if only interesetd in clients of a
                specific service

    Returns
        dictionary of a list of dictionaries

    Raises
        None

    """
    sdict = dict()
    for servicename in lservices.keys():
        if sname and sname != servicename:
            continue
        try:
            service = AIService(servicename)
        except VersionError as version_err:
            warn_version(version_err)
            continue
        arch = which_arch(service)
        image_path = [service.image.path]
        client_info = config.get_clients(servicename)
        for clientkey in client_info:
            # strip off the leading '01' and reinsert ':'s
            client = AIdb.formatValue('mac', clientkey[2:])
            tdict = {'client': client, 'ipath': image_path, 'arch': arch}
            if servicename in sdict:  # existing service name
                slist = sdict[servicename]
                slist.extend([tdict])
                sdict[servicename] = slist
            else:  # new service name key
                sdict[servicename] = [tdict]
    return sdict
コード例 #14
0
def do_delete_profile(cmd_options=None):
    ''' external entry point for installadm
    Arg: cmd_options - command line options
    Effect: delete profiles per command line
    '''
    options = parse_options(cmd_options)

    # get AI service directory, database name
    service = AIService(options.service_name)
    dbname = service.database_path

    # Open the database
    aisql = AIdb.DB(dbname, commit=True)
    aisql.verifyDBStructure()

    # delete profiles per command line
    errs = delete_profiles(options.profile_name, aisql, AIdb.PROFILES_TABLE)
    if errs:
        sys.exit(1)
コード例 #15
0
def remove_client(client_id):
    ''' Remove client configuration

        If client configuration incomplete (e.g., dangling symlink),
        cleanup anyway.

     '''
    logging.debug("Removing client config for %s", client_id)

    (service, datadict) = config.find_client(client_id)
    more_files = list()
    if service:
        # remove client info from .config file
        config.remove_client_from_config(service, client_id)
        if AIService(service).arch == 'i386':
            # suggest dhcp unconfiguration
            remove_client_dhcp_config(client_id)

    # remove client specific symlinks/files
    _cleanup_files(client_id, more_files)
コード例 #16
0
def do_validate_profile(cmd_options=None):
    ''' external entry point for installadm
    Arg: cmd_options - command line options
    Effect: validate per command line
    '''
    options = parse_options(cmd_options)
    isvalid = True
    # get AI service directory, database name
    service = AIService(options.service_name)
    image_dir = service.image.path
    dbname = service.database_path

    if options.profile_name:
        isvalid = validate_internal(options.profile_name, dbname,
                                    AIdb.PROFILES_TABLE, image_dir)
    if options.profile_path:
        # iterate through profile files on command line
        for fname in options.profile_path:
            if not df.validate_file(os.path.basename(fname), fname, image_dir):
                isvalid = False
    # return failure status if any profiles failed validation
    if not isvalid:
        sys.exit(1)
コード例 #17
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
    '''
    # check for authorization and euid
    try:
        check_auth_and_euid(PROFILE_AUTH)
    except UnauthorizedUserError as err:
        raise SystemExit(err)

    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 %(name)s is already in the "
                      "database for service %(service)s.") % \
                      {'name': profile_name, 'service': 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)
コード例 #18
0
ファイル: export.py プロジェクト: alhazred/caiman
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."))

    # based on the argument, check for authorization and euid
    try:
        if len(options.mnames):
            check_auth_and_euid(MANIFEST_AUTH)
      
        if len(options.pnames):
            check_auth_and_euid(PROFILE_AUTH)
    except UnauthorizedUserError as err:
        raise SystemExit(err)

    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
コード例 #19
0
def do_set_criteria(cmd_options=None):
    '''
    Modify the criteria associated with a manifest.

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

    options = parse_options(cmd_options)

    # Get the install service's properties.
    if not config.is_service(options.service_name):
        raise SystemExit(_("Failed to find service %s") % options.service_name)

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

    # Open the database
    dbn = AIdb.DB(database, commit=True)

    # Check to make sure that the manifest whose criteria we're
    # updating exists in the install service.
    if (options.manifest_name and
            not check_published_manifest(service, dbn, options.manifest_name)):
        raise SystemExit(1)

    # Process and validate criteria from -a, -c, or -C, and store
    # store the criteria in a Criteria object.
    try:
        if options.criteria_file:
            root = df.verifyCriteria(df.DataFiles.criteriaSchema,
                                     options.criteria_file, dbn,
                                     AIdb.MANIFESTS_TABLE)
        elif options.criteria_a:
            criteria_dict = pub_man.criteria_to_dict(options.criteria_a)
            root = df.verifyCriteriaDict(df.DataFiles.criteriaSchema,
                                         criteria_dict, dbn,
                                         AIdb.MANIFESTS_TABLE)
        elif options.criteria_c:
            criteria_dict = pub_man.criteria_to_dict(options.criteria_c)
            root = df.verifyCriteriaDict(df.DataFiles.criteriaSchema,
                                         criteria_dict, dbn,
                                         AIdb.MANIFESTS_TABLE)
        else:
            raise SystemExit("Error: Missing required criteria.")

    except (AssertionError, IOError, ValueError) as err:
        raise SystemExit(err)
    except (lxml.etree.LxmlError) as err:
        raise SystemExit(_("Error:\tmanifest error: %s") % err)

    # Instantiate a Criteria object with the XML DOM of the criteria.
    criteria = df.Criteria(root)

    if options.manifest_name:
        # Ensure the criteria we're adding/setting for this manifest doesn't
        # cause a criteria collision in the DB.
        colliding_criteria = pub_man.find_colliding_criteria(
            criteria, dbn, exclude_manifests=[options.manifest_name])
        # If we're appending criteria pass the manifest name
        if options.criteria_a:
            pub_man.find_colliding_manifests(
                criteria,
                dbn,
                colliding_criteria,
                append_manifest=options.manifest_name)
        else:
            pub_man.find_colliding_manifests(criteria,
                                             dbn,
                                             colliding_criteria,
                                             append_manifest=None)
    # validate criteria for profile
    for pname in options.profile_name:
        if not sc.is_name_in_table(pname, dbn.getQueue(), AIdb.PROFILES_TABLE):
            raise SystemExit(
                _("Error:\tservice has no profile named %s." % pname))
        # Validate profile criteria
        sc.validate_criteria_from_user(criteria, dbn, AIdb.PROFILES_TABLE)

    # all validation complete - update database

    # indicate whether criteria are added or replaced
    if options.criteria_a:
        append = True  # add new criteria
    else:
        append = False  # replace any existing criteria with new
    if options.manifest_name:
        # Update the criteria for manifest
        set_criteria(criteria, options.manifest_name, dbn,
                     AIdb.MANIFESTS_TABLE, append)
        print >> sys.stderr, _("Criteria updated for manifest %s.") % \
                options.manifest_name
    for pname in options.profile_name:
        # Update the criteria for profile
        set_criteria(criteria, pname, dbn, AIdb.PROFILES_TABLE, append)
        print >> sys.stderr, _("Criteria updated for profile %s.") % pname
コード例 #20
0
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
コード例 #21
0
def parse_options(do_create, cmd_options=None):
    """
    Parse and validate options
    Args: - do_create (True) or do_update (False)
          - Optional cmd_options, used for unit testing. Otherwise, cmd line
            options handled by OptionParser
    Returns: the DataFiles object populated and initialized
    Raises: The DataFiles initialization of manifest(s) A/I, SC, SMF looks for
            many error conditions and, when caught, are flagged to the user
            via raising SystemExit exceptions.

    """
    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",
                          dest="criteria_c",
                          action="append",
                          default=list(),
                          help=_("Criteria: "
                                 "<-c criteria=value|range> ..."),
                          metavar="CRITERIA")
        parser.add_option("-C",
                          "--criteria-file",
                          dest="criteria_file",
                          default=None,
                          help=_("Path to criteria XML file."))
        parser.add_option("-d",
                          "--default",
                          dest="set_as_default",
                          default=False,
                          action='store_true',
                          help=_("Set manifest as default "))
    parser.add_option("-f",
                      "--file",
                      dest="manifest_path",
                      default=None,
                      help=_("Path to manifest file "))
    parser.add_option("-m",
                      "--manifest",
                      dest="manifest_name",
                      default=None,
                      help=_("Name of manifest"))
    parser.add_option("-n",
                      "--service",
                      dest="service_name",
                      default=None,
                      help=_("Name of install service."))

    # Get the parsed options using parse_args().  We know we don't have
    # args, so we're just grabbing the first item of the tuple returned.
    options, args = parser.parse_args(cmd_options)

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

    if not do_create:
        options.criteria_file = None
        options.criteria_c = None
        options.set_as_default = False

    # options are:
    #    -c  criteria=<value/range> ...       (create only)
    #    -C  XML file with criteria specified (create only)
    #    -d  set manifest as default          (create only)
    #    -n  service name
    #    -f  path to manifest file
    #    -m  manifest name

    # check that we got the install service's name and an AI manifest.
    if options.manifest_path is None or options.service_name is None:
        parser.error(_("Missing one or more required options."))

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

    criteria_dict = None
    if do_create:
        # check that we aren't mixing -c and -C
        # Note: -c and -C will be accepted for create, not for update.
        if options.criteria_c and options.criteria_file:
            parser.error(_("Options used are mutually exclusive."))

        # if we have criteria from cmd line, convert into dictionary
        if options.criteria_c:
            try:
                criteria_dict = criteria_to_dict(options.criteria_c)
            except ValueError as err:
                parser.error(err)

        elif options.criteria_file:
            if not os.path.exists(options.criteria_file):
                parser.error(
                    _("Unable to find criteria file: %s") %
                    options.criteria_file)

    if not config.is_service(options.service_name):
        raise SystemExit(_("Failed to find service %s") % options.service_name)

    # Get the service's imagepath. If service is an alias, the
    # base service's imagepath is obtained.
    service = AIService(options.service_name)
    try:
        image_path = service.image.path
    except KeyError as err:
        raise SystemExit(
            _("Data for service %s is corrupt. Missing "
              "property: %s\n") % (options.service_name, err))

    service_dir = service.config_dir
    dbname = service.database_path

    try:
        files = df.DataFiles(service_dir=service_dir,
                             image_path=image_path,
                             database_path=dbname,
                             manifest_file=options.manifest_path,
                             manifest_name=options.manifest_name,
                             criteria_dict=criteria_dict,
                             criteria_file=options.criteria_file,
                             service_name=options.service_name,
                             set_as_default=options.set_as_default)
    except (AssertionError, IOError, ValueError) as err:
        raise SystemExit(err)
    except (lxml.etree.LxmlError) as err:
        raise SystemExit(_("Error:\tmanifest error: %s") % err)

    return (files)
コード例 #22
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
コード例 #23
0
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
コード例 #24
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
    '''
    # check for authorization and euid
    try:
        check_auth_and_euid(PROFILE_AUTH)
    except UnauthorizedUserError as err:
        raise SystemExit(err)

    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 writing profile %(profile)s: %(err)s") % {
                'profile': profile_name,
                'err': err
            })
    finally:
        os.unlink(tmp_profile_path)

    print >> sys.stderr, _("Profile updated successfully.")
コード例 #25
0
def print_local_manifests(sdict, smwidth, mfindent, stwidth, cwidth):
    """
    Iterates over the name dictionary printing each
    manifest or criteria within the dictionary.  The name dictionary
    is populated via get_manifest_or_profile_names().

    Args
        sdict = service manifest dictionary

            {
                'servicename1':
                    [
                        [ manifestfile1, has_criteria (boolean), {} ],
                        ...
                    ],
                ...
            }

        smwidth = the length of the widest service or manifest name

        mfindent = how many spaces will be manifest name indented

        stwidth = width of status column

        cwidth = the length of the widest criteria

    Returns
        None

    Raises
        None
    """

    tkeys = sdict.keys()
    tkeys.sort()
    smwidth += 1
    stwidth += 1
    for akey in tkeys:
        default_mfest = None
        inactive_mfests = list()
        active_mfests = list()
        try:
            default_mname = AIService(akey).get_default_manifest()
        except StandardError:
            default_mname = ""
        for manifest_item in sdict[akey]:
            # manifest_items are a list of
            # [ name, number of criteria, criteria_dictionary ]

            if manifest_item[0] == default_mname:
                default_mfest = manifest_item  # There could be max 1 default
            elif manifest_item[1]:  # has_criteria and not default
                active_mfests.append(manifest_item)
            else:
                inactive_mfests.append(manifest_item)

        print akey  # print service name on separate line

        line = ''.ljust(mfindent)  # Manifest is indented
        for manifest_i in active_mfests:
            line += manifest_i[0].ljust(smwidth - mfindent)  # Manifest
            line += ''.ljust(stwidth)  # Status is empty for active mfests
            ordered_keys = ['arch', 'mac', 'ipv4']
            keys = manifest_i[2].keys()
            keys.sort()
            for k in keys:
                if k not in ordered_keys:
                    ordered_keys.append(k)
            crit_printed = False
            for k in ordered_keys:
                if k in manifest_i[2] and manifest_i[2][k] != '':
                    line += k.ljust(cwidth) + ' = ' + manifest_i[2][k]
                    print line
                    crit_printed = True
                    line = ''.ljust(mfindent) + \
                        ''.ljust(smwidth - mfindent) + ''.ljust(stwidth)
            if not crit_printed:
                line += _("None")
                print line
            print  # Blank line after each manifest
            line = ''.ljust(mfindent)

        if default_mfest:
            line += default_mfest[0].ljust(smwidth - mfindent)  # Manifest name
            line += DEFAULT.ljust(stwidth)  # Status is Default
            # Default manifest can have ignored criteria
            ordered_keys = ['arch', 'mac', 'ipv4']
            keys = default_mfest[2].keys()
            keys.sort()
            for k in keys:
                if k not in ordered_keys:
                    ordered_keys.append(k)
            crit_printed = False
            for k in ordered_keys:
                if k in default_mfest[2] and default_mfest[2][k] != '':
                    line += '(' + IGNORED + ': ' + k.ljust(cwidth) + \
                        ' = ' + default_mfest[2][k] + ')'
                    print line
                    crit_printed = True
                    line = ''.ljust(mfindent) + \
                        ''.ljust(smwidth - mfindent) + ''.ljust(stwidth)
            if not crit_printed:
                line += _("None")
                print line
            line = ''.ljust(mfindent)
            print  # Blank line after each manifest
        for manifest_i in inactive_mfests:
            line += manifest_i[0].ljust(smwidth - mfindent)  # Manifest
            line += INACTIVE.ljust(stwidth)
            line += _("None")  # Inactive manifests have no criteria
            print line
            print  # Blank line after each manifest
            line = ''.ljust(mfindent)
コード例 #26
0
def print_service_manifests(sdict, sname, width, swidth, cwidth):
    """
    Iterates over the criteria dictionary printing each non blank
    criteria.  The manifest dictionary is populated via
    get_mfest_or_profile_criteria().

    Args
        sdict = criteria dictionary
                (same as in get_mfest_or_profile_criteria() description)

        sname = name of service

        width = widest manifest name

        swidth = width of status column

        cwidth = widest criteria name (0 if no criteria)

    Returns
        None

    Raises
        None
    """
    default_mfest = None
    inactive_mfests = list()
    active_mfests = list()

    width += 1
    swidth += 1

    mnames = sdict.keys()
    if not mnames:
        return
    mnames.sort()

    try:
        default_mname = AIService(sname).get_default_manifest()
    except StandardError:
        default_mname = ""

    ordered_keys = ['arch', 'mac', 'ipv4']
    if cwidth > 0:
        # Criteria are present.
        keys = sdict[mnames[0]][0].keys()
        keys.sort()
        for akey in keys:
            if akey not in ordered_keys:
                ordered_keys.append(akey)

    for name in mnames:
        manifest_list = [name]
        if cwidth > 0:
            for ldict in sdict[name]:
                for akey in ordered_keys:
                    if akey in ldict and ldict[akey] != '':
                        manifest_list.append(
                            akey.ljust(cwidth) + ' = ' + ldict[akey])
        if name == default_mname:
            default_mfest = manifest_list
        elif len(manifest_list) == 1:
            inactive_mfests.append(manifest_list)
        else:
            active_mfests.append(manifest_list)

    for mfest in active_mfests:
        # Active manifests have at least one criterion.
        print mfest[0].ljust(width) + ''.ljust(swidth) + mfest[1]
        for other_crit in range(2, len(mfest)):
            print ' '.ljust(width + swidth) + mfest[other_crit]
        print
    if default_mfest:
        # Since 'Default' is used in status column, it is in STATUS_WORDS
        # and so swidth accommodates it.
        first_line = default_mfest[0].ljust(width) + \
            DEFAULT.ljust(swidth)
        if len(default_mfest) > 1:
            first_line += "(" + IGNORED + ": " + default_mfest[1] + ")"
        else:
            first_line += "None"
        print first_line
        for other_crit in range(2, len(default_mfest)):
            print ''.ljust(width + swidth) + \
                "(" + IGNORED + ": " + default_mfest[other_crit] + ")"
        print
    for mfest in inactive_mfests:
        # Since 'Inactive' is used in status column, it is in STATUS_WORDS.
        # and so swidth accommodates it.
        print mfest[0].ljust(width) + INACTIVE.ljust(swidth) + \
            _("None")
        print
コード例 #27
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
コード例 #28
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
コード例 #29
0
ファイル: delete_service.py プロジェクト: alhazred/caiman
def delete_specified_service(service_name, auto_remove, noprompt):
    ''' Delete the specified Automated Install Service
    Input: service_name - service name
           auto_remove - boolean, True if dep. aliases and clients should
                         be removed, False otherwise
           noprompt - boolean, True if warning about removing
                           default-<arch> service should be suppressed
    '''
    logging.debug("delete_specified_service %s %s %s", service_name,
                  auto_remove, noprompt)

    service = AIService(service_name)

    # If the '-r' option has not been specified, look for all
    # dependent aliases and clients
    all_aliases = config.get_aliased_services(service_name, recurse=True)
    if not auto_remove:
        all_clients = config.get_clients(service_name).keys()
        for ale in all_aliases:
            all_clients.extend(config.get_clients(ale).keys())

        # if any aliases or clients are dependent on this service, exit
        if all_aliases or all_clients:
            raise SystemExit(
                cw(
                    _("\nError: The following aliases and/or "
                      "clients are dependent on this service:\n\n"
                      "%s\n\nPlease update or delete them prior "
                      "to deleting this service or rerun this "
                      "command using the -r|--autoremove option "
                      "to have them automatically removed.\n") %
                    '\n'.join(all_aliases + all_clients)))

    # Prompt user if they are deleting the default-sparc or default-i386 alias
    if not noprompt:
        sname = None
        if service_name in DEFAULT_ARCH:
            sname = service_name
        else:
            default_alias = set(DEFAULT_ARCH) & set(all_aliases)
            if default_alias:
                sname = ''.join(default_alias)
        if sname:
            arch = sname.split('default-')[1]
            _warning = """
            WARNING: The service you are deleting, or a dependent alias, is
            the alias for the default %(arch)s service. Without the '%(name)s'
            service, %(arch)s clients will fail to boot unless explicitly
            assigned to a service using the create-client command.
            """ % {
                'arch': arch,
                'name': sname
            }

            print >> sys.stderr, cw(_(_warning))
            prompt = _("Are you sure you want to delete this alias? [y/N]: ")
            if not com.ask_yes_or_no(prompt):
                raise SystemExit(1)

    # If there are dependent aliases or clients, then remove these first
    aliases = config.get_aliased_services(service_name)
    for dependent in aliases:
        logging.debug("recursively calling delete_specified_service for %s",
                      dependent)
        delete_specified_service(dependent, True, True)

    clients = config.get_clients(service_name).keys()
    for dependent in clients:
        logging.debug("calling remove_client for %s", dependent)
        clientctrl.remove_client(dependent)

    logging.debug("now deleting service %s", service_name)

    # remove DHCP bootfile configuration for this service, if set
    remove_dhcp_configuration(service)

    # stop the service first (avoid pulling files out from under programs)
    try:
        service.delete()
    except StandardError as err:
        # Bail out if the service could not be unmounted during the disable,
        # as it won't be possible to delete necessary files.
        print >> sys.stderr, _("\nService could not be deleted.")
        raise SystemExit(err)

    # if this was the last service, go to maintenance
    config.check_for_enabled_services()
コード例 #30
0
def get_local_services(services, sname=None):
    """
    Iterates over the local services on a host creating a dictionary
    with the service name as the key and status, path, architecture,
    and aliasof as the value.  If name is not None then it ensures
    that only the named service is retrieved.

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

    Returns
        a service dictionary made up of a list of dictionary of services.

        {
        service1:
          [
            {'status':on1, 'path':path1, 'arch':arch1, 'aliasof':aliasof1},
            ...
          ],
        ...
        }

        the width of the longest service name
        the width of the longest aliasof name

    Raises
        None
    """
    width = 0
    aliasofwidth = 1
    sdict = dict()
    for akey in services:
        serv = services[akey]
        servicename = akey
        # ensure that the current service has the keys we need.
        # if not, print error, but continue listing other services
        try:
            config.verify_key_properties(akey, serv)
        except config.ServiceCfgError as err:
            print >> sys.stderr, err
            continue
        try:
            service = AIService(servicename)
        except VersionError as err:
            warn_version(err)
            continue
        if config.PROP_ALIAS_OF in serv:
            image_path = service.image.path
            serv[config.PROP_IMAGE_PATH] = image_path

        info = dict()
        # if a service name is passed in then
        # ensure it matches the current name
        if not sname or sname == servicename:
            width = max(len(servicename), width)
            info['status'] = serv[config.PROP_STATUS]
            info['path'] = serv[config.PROP_IMAGE_PATH]
            info['arch'] = which_arch(service)
            if config.PROP_ALIAS_OF in serv:
                # have an alias
                aliasof = serv[config.PROP_ALIAS_OF]
            else:
                aliasof = '-'
            info['aliasof'] = aliasof
            aliasofwidth = max(len(aliasof), aliasofwidth)
            if servicename in sdict:
                slist = sdict[servicename]
                slist.extend([info])
                sdict[servicename] = slist
            else:
                sdict[servicename] = [info]

    return sdict, width, aliasofwidth
コード例 #31
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(_("OmniOS Automated "
                                 "Installation Webserver"))),
                E.BODY(
                    E.H1(
                        _("Welcome to the OmniOS "
                          "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>'
コード例 #32
0
def get_manifest_or_profile_names(services, dbtable):
    """
    Iterate through the services retrieving
    all the stored manifest or profile names.

    Args
        services = dictionary of service properties
        dbtable = database table, distinguishing manifests from profiles

    Returns
        a dictionary of service manifests or profiles within a list:

            {
                servicename1:
                    [
                        [name, has_criteria (boolean), {crit:value, ... }],
                        ... 
                    ],
                ...
            }

        the width of the longest service name (swidth)

        the width of the longest manifest name (mwidth)

        the width of the longest criteria (cwidth)

    Raises
        None
    """
    swidth = 0
    mwidth = 0
    cwidth = 0
    sdict = dict()
    for sname in sorted(services.keys()):
        try:
            service = AIService(sname)
        except VersionError as err:
            warn_version(err)
            continue

        path = service.database_path

        if os.path.exists(path):
            try:
                maisql = AIdb.DB(path)
                maisql.verifyDBStructure()
                aiqueue = maisql.getQueue()
                swidth = max(len(sname), swidth)
                if not AIdb.tableExists(aiqueue, dbtable):
                    continue
                for name in AIdb.getNames(aiqueue, dbtable):
                    mwidth = max(len(name), mwidth)
                    tdict = dict()
                    if dbtable == 'manifests':
                        instances = AIdb.numInstances(name, aiqueue)
                        for instance in range(0, instances):
                            criteria = AIdb.getTableCriteria(name,
                                                             instance,
                                                             aiqueue,
                                                             dbtable,
                                                             humanOutput=False,
                                                             onlyUsed=True)
                            has_criteria = False
                            if criteria is not None:
                                for key in criteria.keys():
                                    if criteria[key] is not None:
                                        has_criteria = True
                                        break
                                if has_criteria:
                                    # We need criteria in human readable form
                                    hrcrit = AIdb.getTableCriteria(
                                        name,
                                        instance,
                                        aiqueue,
                                        dbtable,
                                        humanOutput=True,
                                        onlyUsed=True)
                                    tdict, twidth = get_criteria_info(hrcrit)
                                    cwidth = max(twidth, cwidth)
                    else:
                        criteria = AIdb.getTableCriteria(name,
                                                         None,
                                                         aiqueue,
                                                         dbtable,
                                                         humanOutput=False,
                                                         onlyUsed=True)
                        has_criteria = False
                        if criteria is not None:
                            for key in criteria.keys():
                                if criteria[key] is not None:
                                    has_criteria = True
                                    break
                    if sname in sdict:
                        slist = sdict[sname]
                        slist.append([name, has_criteria, tdict])
                        sdict[sname] = slist
                    else:
                        sdict[sname] = [[name, has_criteria, tdict]]
            except StandardError as err:
                sys.stderr.write(
                    _('Error: AI database access error\n%s\n') % err)
                continue
        else:
            sys.stderr.write(
                _('Error: unable to locate AI database for "%s" '
                  'on server\n') % sname)
            continue

    return sdict, swidth, mwidth, cwidth
コード例 #33
0
ファイル: delete_manifest.py プロジェクト: alhazred/caiman
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 %(name)s has %(num)i "
                               "instances" % {'name': man_name, 'num':
                               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
コード例 #34
0
def get_mfest_or_profile_criteria(sname, services, dbtable):
    """
    Iterate through all the manifests or profiles for the named service (sname)
    pointed to by the SCF service.

    Args
        sname = service name
        services = config.get_all_service_props()
        dbtable = database table, distinguishing manifests from profiles
            Assumed to be one of AIdb.MANIFESTS_TABLE or AIdb.PROFILES_TABLE

    Returns
        a dictionary of the criteria for the named service within a list:

            {
                servicename1:[
                             { 'arch':arch1, 'mem':memory1, 'ipv4':ipaddress1,
                               'mac':macaddr1, 'platform':platform1,
                               'network':network1, 'cpu':cpu1, 'zonename':z1 },
                             ...
                            ]
            }

        * Note1: platform, network and cpu are currently not-implemented
                 upstream.
        * Note2: could simply use a list of dictionaries but implemented as a
                 dictionary of a list of dictionary which will allow for
                 multiple services to be listed at the same time.

        width of longest manifest or profile name

        width of longest criteria

    Raises
        None
    """
    sdict = dict()
    width = 0
    cwidth = 0
    # ensure the named service is in our service dictionary.
    lservices = services.keys()
    if sname in lservices:
        try:
            path = AIService(sname).database_path
        except VersionError as version_err:
            warn_version(version_err)
            return sdict, width, cwidth

        if os.path.exists(path):
            try:
                maisql = AIdb.DB(path)
                maisql.verifyDBStructure()
                aiqueue = maisql.getQueue()
                if dbtable == AIdb.MANIFESTS_TABLE:
                    for name in AIdb.getNames(aiqueue, dbtable):
                        sdict[name] = list()
                        instances = AIdb.numInstances(name, aiqueue)
                        for instance in range(0, instances):
                            width = max(len(name), width)
                            criteria = AIdb.getManifestCriteria(
                                name,
                                instance,
                                aiqueue,
                                humanOutput=True,
                                onlyUsed=True)
                            if criteria:
                                tdict, twidth = get_criteria_info(criteria)
                                cwidth = max(twidth, cwidth)
                                sdict[name].append(tdict)
                elif dbtable == AIdb.PROFILES_TABLE:
                    for name in AIdb.getNames(aiqueue, dbtable):
                        sdict[name] = list()
                        criteria = AIdb.getProfileCriteria(name,
                                                           aiqueue,
                                                           humanOutput=True,
                                                           onlyUsed=True)
                        width = max(len(name), width)
                        tdict, twidth = get_criteria_info(criteria)
                        cwidth = max(twidth, cwidth)

                        sdict[name].append(tdict)
                else:
                    raise ValueError("Invalid value for dbtable: %s" % dbtable)

            except StandardError as err:
                sys.stderr.write(
                    _('Error: AI database access error\n%s\n') % err)
                sys.exit(1)
        else:
            sys.stderr.write(
                _('Error: unable to locate AI database on server '
                  'for %s\n') % sname)
            sys.exit(1)

    return sdict, width, cwidth