def remove_client_dhcp_config(client_id):
    '''
    If a local DHCP server is running, remove any client configuration for
    this client from its configuration. If not, inform end-user that the
    client-service binding should no longer be referenced in the DHCP
    configuration.
    '''
    server = dhcp.DHCPServer()
    if server.is_configured():
        # A local DHCP server is configured. Check for a host entry and remove
        # it if found.
        mac_address = client_id[2:]
        mac_address = AIdb.formatValue('mac', mac_address)
        if server.host_is_configured(mac_address):
            print cw(_("Removing host entry '%s' from local DHCP "
                       "configuration.") % mac_address)
            server.remove_host(mac_address)

            if server.is_online():
                try:
                    server.control('restart')
                except dhcp.DHCPServerError as err:
                    print >> sys.stderr, cw(_("Unable to restart the DHCP "
                                              "SMF service: %s" % err))
                    return
    else:
        # No local DHCP configuration, inform user that it needs to be
        # unconfigured elsewhere.
        print cw(_("No local DHCP configuration found. Unless it will be "
                   "reused, the bootfile '%s' may be removed from the DHCP "
                   "configuration\n" % client_id))
Exemple #2
0
def remove_client_dhcp_config(client_id):
    '''
    If a local DHCP server is running, remove any client configuration for
    this client from its configuration. If not, inform end-user that the
    client-service binding should no longer be referenced in the DHCP
    configuration.
    '''
    server = dhcp.DHCPServer()
    if server.is_configured():
        # A local DHCP server is configured. Check for a host entry and remove
        # it if found.
        mac_address = client_id[2:]
        mac_address = AIdb.formatValue('mac', mac_address)
        if server.host_is_configured(mac_address):
            print cw(
                _("Removing host entry '%s' from local DHCP "
                  "configuration.") % mac_address)
            server.remove_host(mac_address)

            if server.is_online():
                try:
                    server.control('restart')
                except dhcp.DHCPServerError as err:
                    print >> sys.stderr, cw(
                        _("Unable to restart the DHCP "
                          "SMF service: %s" % err))
                    return
    else:
        # No local DHCP configuration, inform user that it needs to be
        # unconfigured elsewhere.
        print cw(
            _("No local DHCP configuration found. Unless it will be "
              "reused, the bootfile '%s' may be removed from the DHCP "
              "configuration\n" % client_id))
def create_new_client(arch, service, mac_address, bootargs=None):
    '''Create a new client of a service and ensure the Automated
       Install SMF service is enabled.

       Input: arch - architecture of service ('i386' or 'sparc')
              service - The AIService to attach to
              mac_address - mac address of client
              bootargs - boot arguments to insert in client menu.lst file (x86)
       Returns: Nothing

    '''
    logging.debug("creating new client for service %s, mac %s, "
                  "arch %s, bootargs %s",
                  service.name, mac_address, arch, bootargs)
    if arch == 'i386':
        clientctrl.setup_x86_client(service, mac_address,
                                     bootargs=bootargs)
    else:
        clientctrl.setup_sparc_client(service, mac_address)

    # If the installation service this client is being created for
    # is not enabled, print warning to the user.
    if not config.is_enabled(service.name):
        logging.debug("service is disabled: %s", service.name)
        print cw(_("\nWarning: the installation service, %s, is disabled. "
                   "To enable it, use 'installadm enable %s'.") %
                   (service.name, service.name))
    def _register_a_service(self, name, interfaces=None, port=0,
                            comments=None):
        '''Method: _register_a_service, private to class

        Description:
            Register a single service on the interfaces

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

        Returns
            list_sdrefs - list of service references

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

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

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

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

            smf_port = config.get_service_port(name)
            if not smf_port:
                try:
                    smf_port = libaimdns.getinteger_property(common.SRVINST,
                                                             common.PORTPROP)
                    smf_port = str(smf_port)
                except libaimdns.aiMDNSError, err:
                    raise AIMDNSError(cw(_('error: aiMDNSError: port property '
                                           'failure (%s)') % err))
Exemple #5
0
def verify_key_properties(svcname, props):
    '''Verify key properties are present for a service

    Input:
        svcname - service name
        props - A dictionary of service properties to check
    Raises:
        ServiceCfgError if not all required service properties present

    '''
    logging.log(com.XDEBUG,
                "*** START service_config.verify_key_properties ***")
    logging.log(com.XDEBUG, "svcname %s, props=%s", svcname, props)

    # verify that all required keys present
    missing = set()
    if PROP_SERVICE_NAME not in props.keys():
        missing.add(PROP_SERVICE_NAME)
    else:
        prop_name = props[PROP_SERVICE_NAME]
        if prop_name != svcname:
            raise ServiceCfgError(
                cw(
                    _("\nError: service name '%(name)s' does "
                      "not match %(service)s property "
                      "'%(prop_name)s'\n") % {
                          'name': svcname,
                          'service': PROP_SERVICE_NAME,
                          'prop_name': prop_name
                      }))

    if PROP_STATUS not in props.keys():
        missing.add(PROP_STATUS)
    if PROP_TXT_RECORD not in props.keys():
        missing.add(PROP_TXT_RECORD)
    else:
        port = props[PROP_TXT_RECORD].partition(':')[1]
        if not port:
            raise ServiceCfgError(
                cw(
                    _("\nError: Unable to determine service "
                      "directory for service %s\n") % svcname))
    if PROP_IMAGE_PATH not in props.keys():
        if PROP_ALIAS_OF not in props.keys():
            missing.add(PROP_IMAGE_PATH + _(' or ') + PROP_ALIAS_OF)
    if missing:
        raise ServiceCfgError(
            cw(
                _('\nError: installation service key '
                  'properties missing for service %(name)s:'
                  ' %(properties)s\n') % {
                      'name': svcname,
                      'properties': ', '.join(missing)
                  }))
def print_local_services(sdict, width, awidth):
    """
    Iterates over the local service dictionary and prints out
    service name, aliasof, status, architecture, and image path.
    All fields are left justified according to FDICT[field], width,
    awidth or simply printed in the case of path.

    The service dictionary looks like:

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

    Args
        sdict = service dictionary
        width = length of longest service name
        awidth = length of longest aliasof service name

    Returns
        None

    Raises
        None
    """
    tkeys = sdict.keys()
    tkeys.sort()
    missing_image = False
    for aservice in tkeys:
        firstone = True
        for info in sdict[aservice]:
            if firstone == True:
                print aservice.ljust(width),
                firstone = False
            else:
                print ' ' * width
            print info['aliasof'].ljust(awidth),
            print info['status'].ljust(FDICT['status']),
            print info['arch'].ljust(FDICT['arch']),
            # If the architecture can't be identified, either the image is
            # missing or not accessible.
            if info['arch'] == "*":
                missing_image = True
            print info['path']

    if missing_image:
        print cw(ARCH_UNKNOWN)
    print
def print_local_services(sdict, width, awidth):
    """
    Iterates over the local service dictionary and prints out
    service name, aliasof, status, architecture, and image path.
    All fields are left justified according to FDICT[field], width,
    awidth or simply printed in the case of path.

    The service dictionary looks like:

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

    Args
        sdict = service dictionary
        width = length of longest service name
        awidth = length of longest aliasof service name

    Returns
        None

    Raises
        None
    """
    tkeys = sdict.keys()
    tkeys.sort()
    missing_image = False
    for aservice in tkeys:
        firstone = True
        for info in sdict[aservice]:
            if firstone == True:
                print aservice.ljust(width),
                firstone = False
            else:
                print ' ' * width
            print info['aliasof'].ljust(awidth),
            print info['status'].ljust(FDICT['status']),
            print info['arch'].ljust(FDICT['arch']),
            # If the architecture can't be identified, either the image is
            # missing or not accessible.
            if info['arch'] == "*":
                missing_image = True
            print info['path']

    if missing_image:
        print cw(ARCH_UNKNOWN)
    print
Exemple #8
0
def set_imagepath(options):
    '''Change the location of a service's image'''

    logging.debug("set %s imagepath to %s",
                  options.svcname, options.value)
    new_imagepath = options.value.strip()

    service = svc.AIService(options.svcname)
    if service.is_alias():
        raise SystemExit(cw(_('\nError: Can not change the imagepath of an '
                           'alias.')))

    if not os.path.isabs(new_imagepath):
        raise SystemExit(_("\nError: A full pathname is required for the "
                           "imagepath.\n"))

    if os.path.exists(new_imagepath):
        raise SystemExit(_("\nError: The imagepath already exists: %s\n") %
                         new_imagepath)

    if os.path.islink(new_imagepath):
        raise SystemExit(_("\nError: The imagepath may not be a symlink.\n"))

    new_imagepath = new_imagepath.rstrip('/')
    try:
        service.relocate_imagedir(new_imagepath)
    except (svc.MountError, aismf.ServicesError) as error:
        raise SystemExit(error)
def _set_instance(state, wait_for):
    '''Set the install/server service to a given
    state, and wait for it to finish transitioning.
    
    state - desired state, e.g. 'MAINTENANCE' or 'RESTORE'
    wait_for - Function will block until the SMF state of the install/server
        instance is one of the values passed in this list,
        e.g. ['DISABLED', 'OFFLINE']. List items should be upper case.
    
    Raises ServicesError
        if the transition doesn't complete before MAX_WAIT_TIME seconds pass.
    
    '''
    libaiscf.AISCF(FMRI=AI_SVC_FMRI).state = state.upper()

    # Wait a reasonable amount of time to confirm state change.
    wait_cnt = 0
    while libaiscf.AISCF(FMRI=AI_SVC_FMRI).state.upper() not in wait_for:
        if wait_cnt >= MAX_WAIT_TIME:
            logging.debug("Wait time exceeded on attempt to move "
                          "installadm SMF service to %s.", state.lower())

            raise ServicesError(cw(_("Error: Failed to place %(svc)s in "
                                     "%(state)s. See the output of 'svcs "
                                     "-xv %(svc)s' for more information.") %
                                    {'svc': AI_SVC_FMRI,
                                    'state': state.lower()}))
        else:
            time.sleep(1)
            wait_cnt += 1
    logging.log(com.XDEBUG, "Time to set installadm SMF service to '%s' is %i"
                " seconds", state, wait_cnt)
def _start_tftpd():
    '''Start the tftp/udp6 service, a dependency of installadm. If necessary,
    adjust the inetd_start/exec property to run tftp out of /etc/netboot.
    
    Raises ServicesError if tftp/udp6 is configured to use a different
        directory, and that directory exists and has files.
    
    '''
    getprop = [SVCPROP, '-p', INET_START_PROP, TFTP_FMRI]
    svcprop_popen = Popen.check_call(getprop, stdout=Popen.STORE,
                                     stderr=Popen.STORE)
    inet_start = svcprop_popen.stdout.strip().split()
    if inet_start[-1] != com.BOOT_DIR:
        if (os.path.exists(inet_start[-1]) and os.path.isdir(inet_start[-1])
            and os.listdir(inet_start[-1])):
            raise ServicesError(cw(_("The %(svc)s service has been configured "
                                     "to use the %(dir)s directory; "
                                     "installadm is incompatible with these "
                                     "settings. Please use svccfg to change "
                                     "the %(prop)s property of the %(svc)s "
                                     "service to migrate to the %(desired)s "
                                     "directory.")
                                      % {'svc': TFTP_FMRI,
                                         'dir': inet_start[-1],
                                         'desired': com.BOOT_DIR,
                                         'prop': INET_START_PROP}))

        setprop = [SVCCFG, '-s', TFTP_FMRI, 'setprop', 'inetd_start/exec', '=',
                   INET_START % com.BOOT_DIR]
        Popen.check_call(setprop)
        Popen.check_call([SVCADM, 'refresh', TFTP_FMRI])
    
    Popen.check_call([SVCADM, 'enable', TFTP_FMRI])
Exemple #11
0
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")))
def _start_tftpd():
    '''Start the tftp/udp6 service, a dependency of installadm. If necessary,
    adjust the inetd_start/exec property to run tftp out of /etc/netboot.
    
    Raises ServicesError if tftp/udp6 is configured to use a different
        directory, and that directory exists and has files.
    
    '''
    getprop = [SVCPROP, '-p', INET_START_PROP, TFTP_FMRI]
    svcprop_popen = Popen.check_call(getprop, stdout=Popen.STORE,
                                     stderr=Popen.STORE)
    inet_start = svcprop_popen.stdout.strip().split()
    if inet_start[-1] != com.BOOT_DIR:
        if (os.path.exists(inet_start[-1]) and os.path.isdir(inet_start[-1])
            and os.listdir(inet_start[-1])):
            raise ServicesError(cw(_("The %(svc)s service has been configured "
                                     "to use the %(dir)s directory; "
                                     "installadm is incompatible with these "
                                     "settings. Please use svccfg to change "
                                     "the %(prop)s property of the %(svc)s "
                                     "service to migrate to the %(desired)s "
                                     "directory.")
                                      % {'svc': TFTP_FMRI,
                                         'dir': inet_start[-1],
                                         'desired': com.BOOT_DIR,
                                         'prop': INET_START_PROP}))

        setprop = [SVCCFG, '-s', TFTP_FMRI, 'setprop', 'inetd_start/exec', '=',
                   INET_START % com.BOOT_DIR]
        Popen.check_call(setprop)
        Popen.check_call([SVCADM, 'refresh', TFTP_FMRI])
    
    Popen.check_call([SVCADM, 'enable', TFTP_FMRI])
def _set_instance(state, wait_for):
    '''Set the install/server service to a given
    state, and wait for it to finish transitioning.
    
    state - desired state, e.g. 'MAINTENANCE' or 'RESTORE'
    wait_for - Function will block until the SMF state of the install/server
        instance is one of the values passed in this list,
        e.g. ['DISABLED', 'OFFLINE']. List items should be upper case.
    
    Raises ServicesError
        if the transition doesn't complete before MAX_WAIT_TIME seconds pass.
    
    '''
    libaiscf.AISCF(FMRI=AI_SVC_FMRI).state = state.upper()

    # Wait a reasonable amount of time to confirm state change.
    wait_cnt = 0
    while libaiscf.AISCF(FMRI=AI_SVC_FMRI).state.upper() not in wait_for:
        if wait_cnt >= MAX_WAIT_TIME:
            logging.debug("Wait time exceeded on attempt to move "
                          "installadm SMF service to %s.", state.lower())

            raise ServicesError(cw(_("Error: Failed to place %(svc)s in "
                                     "%(state)s. See the output of 'svcs "
                                     "-xv %(svc)s' for more information.") %
                                    {'svc': AI_SVC_FMRI,
                                    'state': state.lower()}))
        else:
            time.sleep(1)
            wait_cnt += 1
    logging.log(com.XDEBUG, "Time to set installadm SMF service to '%s' is %i"
                " seconds", state, wait_cnt)
    def print_clients(width, sdict):
        """
        Iterates over a dictionary of service clients printing
        information about each one.

        Args
            width = widest service name
            sdict = service dictionary of clients
                    (same as in get_clients() description)

        Returns
            None

        Raises
            None
        """
        # sort the keys so that the service names are in alphabetic order.
        tkeys = sdict.keys()
        tkeys.sort()
        missing_image = False
        for aservice in tkeys:
            service_firstone = True
            for aclient in sdict[aservice]:
                if service_firstone == True:
                    print aservice.ljust(width),
                    service_firstone = False
                else:
                    print ' ' * width,
                print aclient['client'].ljust(FDICT['cadd']),
                print aclient['arch'].ljust(FDICT['arch']),
                # If the architecture can't be identified, either the image is
                # missing or not accessible.
                if aclient['arch'] == '*':
                    missing_image = True
                path_firstone = True
                cpaths = list()
                for cpath in aclient['ipath']:
                    if cpath not in cpaths:
                        if path_firstone == False:
                            spaces = width + FDICT['cadd'] + FDICT['arch'] + 2
                            print ' '.ljust(spaces),
                        else:
                            path_firstone = False
                        print cpath
                    cpaths.insert(0, cpath)
            if missing_image:
                print cw(ARCH_UNKNOWN)
    def print_clients(width, sdict):
        """
        Iterates over a dictionary of service clients printing
        information about each one.

        Args
            width = widest service name
            sdict = service dictionary of clients
                    (same as in get_clients() description)

        Returns
            None

        Raises
            None
        """
        # sort the keys so that the service names are in alphabetic order.
        tkeys = sdict.keys()
        tkeys.sort()
        missing_image = False
        for aservice in tkeys:
            service_firstone = True
            for aclient in sdict[aservice]:
                if service_firstone == True:
                    print aservice.ljust(width),
                    service_firstone = False
                else:
                    print ' ' * width,
                print aclient['client'].ljust(FDICT['cadd']),
                print aclient['arch'].ljust(FDICT['arch']),
                # If the architecture can't be identified, either the image is
                # missing or not accessible.
                if aclient['arch'] == '*':
                    missing_image = True
                path_firstone = True
                cpaths = list()
                for cpath in aclient['ipath']:
                    if cpath not in cpaths:
                        if path_firstone == False:
                            spaces = width + FDICT['cadd'] + FDICT['arch'] + 2
                            print ' '.ljust(spaces),
                        else:
                            path_firstone = False
                        print cpath
                    cpaths.insert(0, cpath)
            if missing_image:
                print cw(ARCH_UNKNOWN)
Exemple #16
0
def set_aliasof(options):
    '''Change a service's base service'''
    logging.debug("set alias %s's basesvc to %s", options.svcname,
                  options.value)
    basesvcname = options.value
    aliasname = options.svcname

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

    if aliasname == basesvcname:
        raise SystemExit(
            _('\nError: Alias name same as service name: %s\n') % aliasname)

    aliassvc = svc.AIService(aliasname)
    if not aliassvc.is_alias():
        raise SystemExit(
            _('\nError: Service exists, but is not an '
              'alias: %s\n') % aliasname)

    basesvc_arch = svc.AIService(basesvcname).arch
    aliassvc_arch = aliassvc.arch
    if basesvc_arch != aliassvc_arch:
        raise SystemExit(
            _("\nError: Architectures of service and alias "
              "are different.\n"))

    if aliassvc.is_aliasof(basesvcname):
        raise SystemExit(
            _("\nError: %s is already an alias of %s\n") %
            (aliasname, basesvcname))

    if svc.AIService(basesvcname).is_alias():
        raise SystemExit(_("\nError: Cannot alias to another alias.\n"))

    # Make sure we aren't creating inter dependencies
    all_aliases = config.get_aliased_services(aliasname, recurse=True)
    if basesvcname in all_aliases:
        raise SystemExit(
            cw(
                _("\nError: %s can not be made an alias of %s "
                  "because %s is dependent on %s\n") %
                (aliasname, basesvcname, basesvcname, aliasname)))
    try:
        aliassvc.update_basesvc(basesvcname)
    except (OSError, config.ServiceCfgError) as err:
        raise SystemExit(
            _("Failed to set 'aliasof' property of : %s") % aliasname)
    except svc.MultipleUnmountError as err:
        print >> sys.stderr, _("Failed to disable alias")
        raise SystemExit(err)
    except svc.MountError as err:
        print >> sys.stderr, _("Failed to enable alias")
        raise SystemExit(err)
    except svc.UnsupportedAliasError as err:
        raise SystemExit(err)
Exemple #17
0
def parse_options(cmd_options=None):
    '''Parse and validate options
    Args: Optional cmd_options, used for unit testing. Otherwise, cmd line
          options handled by OptionParser
    Returns: options object

    '''
    usage = '\n' + get_usage()
    parser = OptionParser(usage=usage)
    parser.add_option('-s', '--source', dest='srcimage', help=_('FMRI'))
    parser.add_option(
        '-p',
        '--publisher',
        help=_("A pkg(5) publisher, in "
               "the form '<prefix>=<uri>', from which to update the "
               "image. Specified publisher becomes the sole publisher "
               "of the updated image."))

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

    # Confirm service name was passed in
    if not args:
        parser.error(_("Missing required argument, <svcname>"))
    elif len(args) > 1:
        parser.error(_("Too many arguments: %s") % args)

    options.service_name = args[0]

    # ensure service exists
    if not config.is_service(options.service_name):
        raise SystemExit(
            _("\nError: The specified service does "
              "not exist: %s\n") % options.service_name)

    service = svc.AIService(options.service_name,
                            image_class=InstalladmPkgImage)
    if not service.is_alias():
        raise SystemExit(
            cw(
                _("Error: '%s' is not an alias. The target of "
                  "'update-service' must be an alias.") % service.name))

    options.service = service

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

    logging.debug("options=%s", options)
    return options
Exemple #18
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
    def register(self, servicename=None, port=0, interfaces=None,
                 comments=None):
        '''Method: register
           Description:
                Registers an ad hoc service.  This method will loop until the
                the application is killed.

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

            Returns
                None

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

        if servicename is not None:
            self.servicename = servicename

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

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

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

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

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

        if sdrefs is not None:
            self.sdrefs[servicename] = sdrefs
            self._handle_events()
        else:
            raise AIMDNSError(cw(_('error: aiMDNSError: mDNS ad hoc '
                                   'registration failed for "%s" service')
                                   % self.servicename))
def verify_key_properties(svcname, props):
    '''Verify key properties are present for a service

    Input:
        svcname - service name
        props - A dictionary of service properties to check
    Raises:
        ServiceCfgError if not all required service properties present

    '''
    logging.log(com.XDEBUG,
                "*** START service_config.verify_key_properties ***")
    logging.log(com.XDEBUG, "svcname %s, props=%s", svcname, props)

    # verify that all required keys present
    missing = set()
    if PROP_SERVICE_NAME not in props.keys():
        missing.add(PROP_SERVICE_NAME)
    else:
        prop_name = props[PROP_SERVICE_NAME]
        if prop_name != svcname:
            raise ServiceCfgError(cw(_("\nError: service name '%s' does not "
                                       "match %s property '%s'\n") % (svcname,
                                       PROP_SERVICE_NAME, prop_name)))

    if PROP_STATUS not in props.keys():
        missing.add(PROP_STATUS)
    if PROP_TXT_RECORD not in props.keys():
        missing.add(PROP_TXT_RECORD)
    else:
        port = props[PROP_TXT_RECORD].partition(':')[1]
        if not port:
            raise ServiceCfgError(cw(_("\nError: Unable to determine service "
                                       "directory for service %s\n") %
                                       svcname))
    if PROP_IMAGE_PATH not in props.keys():
        if PROP_ALIAS_OF not in props.keys():
            missing.add(PROP_IMAGE_PATH + _(' or ') + PROP_ALIAS_OF)
    if missing:
        raise ServiceCfgError(cw(_('\nError: installation service key '
                                   'properties missing for service %s: %s\n') %
                                   (svcname, ', '.join(missing))))
    def verify(self):
        '''
        Check that the image directory exists, appears to be a valid net
        boot image (has a solaris.zlib file), and is a valid Automated
        Installer image (has an auto_install/ai.dtd file).
        Raises: ImageError if path checks fail
        Pre-conditions: Expects self.path to return a valid image_path
        Returns: None
        '''
        # check image_path exists
        if not os.path.isdir(self.path):
            raise ImageError(cw(_("\nError:\tThe image path (%s) is not "
                               "a directory. Please provide a "
                               "different image path.\n") % self.path))

         # check that the image_path has solaris.zlib and
         # auto_install/ai.dtd files
        if not (os.path.exists(os.path.join(self.path, "solaris.zlib")) and
                os.path.exists(os.path.join(self.path,
                               "auto_install/ai.dtd"))):
            raise ImageError(cw(self.INVALID_AI_IMAGE % {"path": self.path}))
def do_alias_service(options):
    ''' Create an alias of a service

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

    basesvc = AIService(options.aliasof)

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

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

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

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

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

    # Register & enable service
    # (Also enables system/install/server, as needed)
    try:
        service.enable()
    except (config.ServiceCfgError, MountError) as err:
        raise SystemExit(err)
    except aismf.ServicesError as err:
        # don't error out if the service is successfully created
        # but the services fail to start - just print out the error
        # and proceed
        print err
def set_aliasof(options):
    '''Change a service's base service'''
    logging.debug("set alias %s's basesvc to %s",
                  options.svcname, options.value)
    basesvcname = options.value
    aliasname = options.svcname

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

    if aliasname == basesvcname:
        raise SystemExit(_('\nError: Alias name same as service name: %s\n') %
                         aliasname)

    aliassvc = svc.AIService(aliasname)
    if not aliassvc.is_alias():
        raise SystemExit(_('\nError: Service exists, but is not an '
                           'alias: %s\n') % aliasname)

    basesvc_arch = svc.AIService(basesvcname).arch
    aliassvc_arch = aliassvc.arch
    if basesvc_arch != aliassvc_arch:
        raise SystemExit(_("\nError: Architectures of service and alias "
                           "are different.\n"))

    if aliassvc.is_aliasof(basesvcname):
        raise SystemExit(_("\nError: %s is already an alias of %s\n") %
                         (aliasname, basesvcname))

    if svc.AIService(basesvcname).is_alias():
        raise SystemExit(_("\nError: Cannot alias to another alias.\n"))

    # Make sure we aren't creating inter dependencies
    all_aliases = config.get_aliased_services(aliasname, recurse=True)
    if basesvcname in all_aliases:
        raise SystemExit(cw(_("\nError: %s can not be made an alias of %s "
                              "because %s is dependent on %s\n") % (aliasname,
                              basesvcname, basesvcname, aliasname)))
    try:
        aliassvc.update_basesvc(basesvcname)
    except (OSError, config.ServiceCfgError) as err:
        raise SystemExit(_("Failed to set 'aliasof' property of : %s") %
                         aliasname)
    except svc.MultipleUnmountError as err:
        print >> sys.stderr, _("Failed to disable alias")
        raise SystemExit(err)
    except svc.MountError as err:
        print >> sys.stderr, _("Failed to enable alias")
        raise SystemExit(err)
    except svc.UnsupportedAliasError as err:
        raise SystemExit(err)
Exemple #24
0
def remove_dhcp_configuration(service):
    '''
    Determines if a local DHCP server is running and if this service's bootfile
    is set as the architecture's default boot service. If it is, we'll unset it
    as we're deleting the service. If the DHCP configuration isn't local,
    inform the end-user that the DHCP configuration should not reference this
    bootfile any longer.
    '''
    server = dhcp.DHCPServer()
    if server.is_configured():
        # Server is configured. Regardless of it's current state, check for
        # this bootfile in the service's architecture class. If it is set as
        # the default for this architecture, unset it.
        try:
            arch_class = dhcp.DHCPArchClass.get_arch(server, service.arch)
        except dhcp.DHCPServerError as err:
            print >> sys.stderr, cw(_("\nUnable to access DHCP configuration: "
                                      "%s\n" % err))
            return

        # Skip SPARC services, since they share a global bootfile
        if (service.arch != 'sparc' and arch_class is not None and 
            arch_class.bootfile == service.dhcp_bootfile):
            try:
                print cw(_("Removing this service's bootfile from local DHCP "
                           "configuration\n"))
                arch_class.unset_bootfile()
            except dhcp.DHCPServerError as err:
                print >> sys.stderr, cw(_("\nUnable to unset this service's "
                                          "bootfile in the DHCP "
                                          "configuration: %s\n" % err))
                return

            if server.is_online():
                try:
                    server.control('restart')
                except dhcp.DHCPServerError as err:
                    print >> sys.stderr, cw(_("\nUnable to restart the DHCP "
                                              "SMF service: %s\n" % err))
    def verify(self):
        '''
        Check that the image directory exists, appears to be a valid net
        boot image (has a solaris.zlib file), and is a valid Automated
        Installer image (has an auto_install/ai.dtd file).
        Raises: ImageError if path checks fail
        Pre-conditions: Expects self.path to return a valid image_path
        Returns: None
        '''
        # check image_path exists
        if not os.path.isdir(self.path):
            raise ImageError(
                cw(
                    _("\nError:\tThe image path (%s) is not "
                      "a directory. Please provide a "
                      "different image path.\n") % self.path))

        # check that the image_path has solaris.zlib and
        # auto_install/ai.dtd files
        if not (os.path.exists(os.path.join(self.path, "solaris.zlib"))
                and os.path.exists(
                    os.path.join(self.path, "auto_install/ai.dtd"))):
            raise ImageError(cw(self.INVALID_AI_IMAGE % {"path": self.path}))
Exemple #26
0
def set_aliasof(options):
    '''Change a service's base service'''
    logging.debug("set alias %s's basesvc to %s",
                  options.svcname, options.value)
    basesvcname = options.value
    aliasname = options.svcname

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

    if aliasname == basesvcname:
        raise SystemExit(_('\nError: Alias name same as service name: %s\n') %
                         aliasname)

    aliassvc = svc.AIService(aliasname)
    if not aliassvc.is_alias():
        raise SystemExit(_('\nError: Service exists, but is not an '
                           'alias: %s\n') % aliasname)

    basesvc_arch = svc.AIService(basesvcname).arch
    aliassvc_arch = aliassvc.arch
    if basesvc_arch != aliassvc_arch:
        raise SystemExit(_("\nError: Architectures of service and alias "
                           "are different.\n"))

    if aliassvc.is_aliasof(basesvcname):
        raise SystemExit(_("\nError: %(aliasname)s is already an alias "
                           "of %(svcname)s\n") % {'aliasname': aliasname,
                           'svcname': basesvcname})

    if svc.AIService(basesvcname).is_alias():
        raise SystemExit(_("\nError: Cannot alias to another alias.\n"))

    # Make sure we aren't creating inter dependencies
    all_aliases = config.get_aliased_services(aliasname, recurse=True)
    if basesvcname in all_aliases:
        raise SystemExit(cw(_("\nError: %(aliasname)s can not be made an "
                              "alias of %(svcname)s because %(svcname)s is "
                              "dependent on %(aliasname)s\n") %
                              {'aliasname': aliasname,
                               'svcname': basesvcname}))

    failures = do_update_basesvc(aliassvc, basesvcname)

    if failures:
        return 1
    return 0
def maintain_instance():
    ''' Move the Automated Installer SMF service to the maintenance state.

    This function is roughly analogous to smf_maintain_instance(3SCF)

    Input:
        None
    Return:
        None
    Raises:
        ServicesError if service fails to transition to
        'MAINTENANCE' within reasonable time.
        Whatever exceptions AISCF encounters

    '''
    logging.log(com.XDEBUG, '**** START ai_smf_service.maintain_instance ****')
    
    _set_instance('MAINTENANCE', ('MAINTENANCE',))
    
    sys.stderr.write(cw(_("The installadm SMF service is no longer online "
                          "because the last install service has been disabled "
                          "or deleted.\n")))
Exemple #28
0
def set_service_props(service_name, props):
    ''' Set the property values for a specified service name.

    Input:
        service_name - An AI service name
        props - A dictionary of properties to update for the specified
                   service_name.
    Raises:
        ServiceCfgError if config file does not exist for the service (to
        create it, use create_service_props())

    '''
    logging.log(
        com.XDEBUG, '**** START service_config.set_service_props '
        'service_name=%s ****', service_name)
    if not os.path.exists(_get_configfile_path(service_name)):
        raise ServiceCfgError(
            cw(
                _('\nError: attempting to set service '
                  'properties, but config file does not exist for "%s"') %
                service_name))
    _write_service_config(service_name, props)
def maintain_instance():
    ''' Move the Automated Installer SMF service to the maintenance state.

    This function is roughly analogous to smf_maintain_instance(3SCF)

    Input:
        None
    Return:
        None
    Raises:
        ServicesError if service fails to transition to
        'MAINTENANCE' within reasonable time.
        Whatever exceptions AISCF encounters

    '''
    logging.log(com.XDEBUG, '**** START ai_smf_service.maintain_instance ****')
    
    _set_instance('MAINTENANCE', ('MAINTENANCE',))
    
    sys.stderr.write(cw(_("The installadm SMF service is no longer online "
                          "because the last install service has been disabled "
                          "or deleted.\n")))
Exemple #30
0
    def check_fmri(self, fmri):
        '''Calls pkg.client.api.ImageInterface.parse_fmri_patterns()
           to check if fmri is valid
        Input: fmri to check
        Returns: PkgFmri object
        Raises: ValueError if there is a problem with the fmri

        '''
        for pattern, err, pfmri, matcher in \
            self.pkg_image.parse_fmri_patterns(fmri):
            if err:
                if isinstance(err, pkg.version.VersionError):
                    # For version errors, include the pattern so
                    # that the user understands why it failed.
                    print >> sys.stderr, \
                        cw(_("Illegal FMRI '%(patt)s': %(error)s" %
                           {'patt': pattern, 'error': err}))
                    raise ValueError(err)
                else:
                    # Including the pattern is redundant for other
                    # exceptions.
                    raise ValueError(err)
            return pfmri
Exemple #31
0
class AImDNS(object):
    ''' Class: AImDNS - base class for registering, browsing and looking up
                        AI and ad hoc mDNS records.
    '''
    # a _handle_event() loop control variable, used to restart the loop
    # after modification to the self.sdrefs variable, private
    _restart_loop = False

    # find/browse mode variables, private
    _do_lookup = False
    _found = False

    # mDNS record resolved variable, used as a stack to indicate that the
    # service has been found, private
    _resolved = list()

    def __init__(self, servicename=None, domain='local', comment=None):
        '''Method: __init__, class private
           Parameters:
                        servicename - the AI servicename
                        domain      - the domain for the registered service
                        comment     - the text comment for the service
           Raises:
               AImDNSError - when unable to retrieve setup information from
                             the host about the available interfaces or the
                             AI SMF service.
        '''
        gettext.install("ai", "/usr/lib/locale")

        # find sdref handle
        self._find = None
        self._lookup = False
        self.services = dict()
        self.servicename = servicename
        self.domain = domain
        self.txt = comment
        self.inter = None
        self.port = 0
        self.verbose = False
        self.timeout = 5
        self.done = False
        self.count = 0

        self.sdrefs = dict()

        self.interfaces = libaimdns.getifaddrs()

        self.register_initialized = False
        self.exclude = False
        self.networks = ['0.0.0.0/0']

        self.instance = None
        self.instance_services = None

    def __del__(self):
        '''Method: __del__
           Parameters:
                None

           Raises:
                None
        '''
        self.done = True
        self.clear_sdrefs()

    def _resolve_callback(self, sdref, flags, interfaceindex, errorcode,
                          fullname, hosttarget, port, txtrecord):
        '''Method: _resolve_callback, class private
        Description:
            DNS Callback for the resolve process, stories the service
            information within the self.services variable.

        Args
            sdref          - service reference,
                             standard argument for callback, not used
            flags          - flag to determine what action is taking place
                             standard argument for callback, not used
            interfaceindex - the index for the interface that the service was
                             found on
            errorcode      - flag to determine if a registration error occurred
            fullname       - name of the service, should be
                             <service>._OSInstall._tcp.local.
            hosttarget     - name of the host, should be <nodename>.local.
            port           - the service port being used
            txtrecord      - the text record associated with the service,
                             standard argument for callback

        Returns
            None

        Raises
            None
        '''
        # handle errors from within the _browse_callback
        # after the select() call
        if errorcode == pyb.kDNSServiceErr_NoError:
            self._found = True
            # get the interface name for the index
            interface = netif.if_indextoname(interfaceindex)
            # interested in the service name and the domain only
            parts = fullname.split('.')
            service = dict()
            service['flags'] = not (flags & pyb.kDNSServiceFlagsAdd)
            service['hosttarget'] = hosttarget
            service['servicename'] = parts[0]
            service['domain'] = parts[-2]
            service['port'] = port
            service['comments'] = str(pyb.TXTRecord.parse(txtrecord))[1:]
            self.services.setdefault(interface, list()).append(service)

            # update the resolve stack flag
            self._resolved.append(True)

    def _browse_callback(self, sdref, flags, interfaceindex, errorcode,
                         servicename, regtype, replydomain):
        '''Method: _browse_callback, class private
        Description:
            DNS Callback for the browse process

        Args
            sdref          - service reference,
                             standard argument for callback, not used
            flags          - flag to determine what action is taking place
                             standard argument for callback, not used
            interfaceindex - the index for the interface that the service was
                             found on
            errorcode      - flag to determine if a registration error occurred
            servicename    - name of the service
            hosttarget     - name of the host, should be <nodename>.local.
            regtype        - registration type, should be _OSInstall._tcp.
            replydomain    - DNS domain, either local or remote

        Returns
            None

        Raises
            None
        '''
        if errorcode != pyb.kDNSServiceErr_NoError:
            return  # error handled in the _handle_event() method

        if self._lookup and servicename != self.servicename:
            return

        resolve_sdref = pyb.DNSServiceResolve(0, interfaceindex, servicename,
                                              regtype, replydomain,
                                              self._resolve_callback)

        # wait for and process resolve the current request
        try:
            while not self._resolved:
                try:
                    ready = select.select([resolve_sdref], list(), list(),
                                          self.timeout)
                except select.error:
                    # purposely ignore errors.
                    continue

                if resolve_sdref not in ready[0]:
                    # not a catastrophic error for the class, therefore,
                    # simply warn that the mDNS service record needed
                    # additional time to process and do not issue an
                    # exception.
                    sys.stderr.write(
                        cw(
                            _('warning: unable to resolve "%s", '
                              'try using a longer timeout\n') % servicename))
                    break
                # process the service
                pyb.DNSServiceProcessResult(resolve_sdref)
            else:
                self._resolved.pop()
        # allow exceptions to fall through
        finally:
            # clean up when there is no exception
            resolve_sdref.close()

    def _handle_events(self):
        ''' Method: __handle_events, class private
            Description:
                Handle the event processing for the registered service
                requests.

            Args
                None

            Returns
                None

            Raises
                None
        '''
        self.done = False
        while not self.done:
            # The self.sdrefs is a dictionary of the form:
            #
            #   for the find mode:
            #       { 'find':[list of sdrefs] }
            #
            #   OR for the browse mode:
            #       { 'browse':[list of sdrefs] }
            #
            #   OR for the register mode:
            #       { <service-name>:[list of sdrefs] }
            #
            #   OR for the register all mode:
            #       { <service-name1>:[list of sdrefs],
            #         <service-name2>:[list of sdrefs],
            #         ... }
            #
            # This must be converted to a simple list of sdrefs for the
            # select() call.
            therefs = list()
            # iterate through the dictionary
            for srv in self.sdrefs:
                for refs in self.sdrefs.get(srv, list()):
                    if refs is not None:
                        therefs.append(refs)

            # loop until done or we need to redo the service reference
            # list mentioned above.  The service reference list will be
            # updated when the SMF service is refreshed which sends a
            # SIGHUP to the application in daemon mode.  This processing
            # of the SIGHUP is done in the signal_hup() method below.
            self._restart_loop = False
            count = 0
            while not self._restart_loop and not self.done:
                try:
                    # process the appropriate service reference
                    try:
                        ready = select.select(therefs, list(), list(),
                                              self.timeout)
                    except select.error:
                        continue

                    # check to ensure that the __del__ method was not called
                    # between the select and the DNS processing.
                    if self.done:
                        continue

                    for sdref in therefs:
                        if sdref in ready[0]:
                            pyb.DNSServiceProcessResult(sdref)

                    # if browse or find loop then loop only long enough to
                    # ensure that all the registered mDNS records are
                    # retrieved per interface configured
                    if self._do_lookup is True:
                        count += 1
                        if count >= self.count:
                            self.done = True

                # <CTL>-C will exit the loop, application
                # needed for command line invocation
                except KeyboardInterrupt:
                    self.done = True

    def _register_callback(self, sdref, flags, errorcode, name, regtype,
                           domain):
        '''Method: _register_callback, private to class
           Description:
                DNS Callback for the registration process

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

            Returns
                None

            Raises
                None
        '''
        # note: DNSService Errors are ignored here and handled elsewhere.
        if errorcode == pyb.kDNSServiceErr_NoError and \
           self.verbose:
            print _('Registered service:')
            print _('\tname    = %s') % name
            print _('\tregtype = %s') % regtype
            print _('\tdomain  = %s') % domain

    def _register_a_service(self,
                            name,
                            interfaces=None,
                            port=0,
                            comments=None):
        '''Method: _register_a_service, private to class

        Description:
            Register a single service on the interfaces

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

        Returns
            list_sdrefs - list of service references

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

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

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

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

            smf_port = config.get_service_port(name)
            if not smf_port:
                try:
                    smf_port = libaimdns.getinteger_property(
                        common.SRVINST, common.PORTPROP)
                    smf_port = str(smf_port)
                except libaimdns.aiMDNSError, err:
                    raise AIMDNSError(
                        cw(
                            _('error: aiMDNSError: port property '
                              'failure (%s)') % err))

        # iterate over the interfaces saving the service references
        list_sdrefs = list()
        valid_networks = common.get_valid_networks()
        for inf in interfaces:
            include_it = False
            for ip in valid_networks:
                if interfaces[inf].startswith(ip):
                    include_it = True
                    break

            if not include_it:
                continue

            if self.verbose:
                print cw(_('Registering %s on %s (%s)') % \
                           (name, inf, interfaces[inf]))

            if smf_port is not None:
                # comments are part of the service record
                commentkey = serv[config.PROP_TXT_RECORD].split('=')[0]
                commenttxt = interfaces[inf].split('/')[0] + ':' + smf_port
                text = pyb.TXTRecord({commentkey: commenttxt})
                try:
                    port = int(smf_port)
                except ValueError:
                    # not a catastrophic error, just
                    # assume the default port of 5555.
                    port = common.DEFAULT_PORT
            # processing an ad hoc registration
            elif comments is None:
                adhoc_dict = {'service': 'ad hoc registration'}
                text = pyb.TXTRecord(adhoc_dict)
            else:
                text = pyb.TXTRecord({'service': comments})

            # register the service on the appropriate interface index
            try:
                interfaceindex = netif.if_nametoindex(inf)
            except netif.NetIFError, err:
                raise AIMDNSError(err)

            sdref = pyb.DNSServiceRegister(name=name,
                                           interfaceIndex=interfaceindex,
                                           regtype=common.REGTYPE,
                                           port=port,
                                           callBack=self._register_callback,
                                           txtRecord=text)

            # DNSServiceUpdateRecord will update the default record if
            # RecordRef is None. Time-to-live (ttl) for the record is being
            # set to 10 seconds.  This value allows enough time for the
            # record to be looked up and it is short enough that when the
            # service is deleted then the mdns daemon will remove it from
            # the cache after this value expires but prior to another service
            # with the same name being created.
            pyb.DNSServiceUpdateRecord(sdRef=sdref,
                                       RecordRef=None,
                                       rdata=text,
                                       ttl=10)

            # save the registered service reference
            list_sdrefs.append(sdref)
Exemple #32
0
    def register(self,
                 servicename=None,
                 port=0,
                 interfaces=None,
                 comments=None):
        '''Method: register
           Description:
                Registers an ad hoc service.  This method will loop until the
                the application is killed.

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

            Returns
                None

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

        if servicename is not None:
            self.servicename = servicename

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

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

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

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

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

        if sdrefs is not None:
            self.sdrefs[servicename] = sdrefs
            self._handle_events()
        else:
            raise AIMDNSError(
                cw(
                    _('error: aiMDNSError: mDNS ad hoc '
                      'registration failed for "%s" service') %
                    self.servicename))
Exemple #33
0
    def _register_a_service(self,
                            name,
                            interfaces=None,
                            port=0,
                            comments=None):
        '''Method: _register_a_service, private to class

        Description:
            Register a single service on the interfaces

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

        Returns
            list_sdrefs - list of service references

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

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

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

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

            smf_port = config.get_service_port(name)
            if not smf_port:
                try:
                    smf_port = libaimdns.getinteger_property(
                        common.SRVINST, common.PORTPROP)
                    smf_port = str(smf_port)
                except libaimdns.aiMDNSError, err:
                    raise AIMDNSError(
                        cw(
                            _('error: aiMDNSError: port property '
                              'failure (%s)') % err))
Exemple #34
0
    def _browse_callback(self, sdref, flags, interfaceindex, errorcode,
                         servicename, regtype, replydomain):
        '''Method: _browse_callback, class private
        Description:
            DNS Callback for the browse process

        Args
            sdref          - service reference,
                             standard argument for callback, not used
            flags          - flag to determine what action is taking place
                             standard argument for callback, not used
            interfaceindex - the index for the interface that the service was
                             found on
            errorcode      - flag to determine if a registration error occurred
            servicename    - name of the service
            hosttarget     - name of the host, should be <nodename>.local.
            regtype        - registration type, should be _OSInstall._tcp.
            replydomain    - DNS domain, either local or remote

        Returns
            None

        Raises
            None
        '''
        if errorcode != pyb.kDNSServiceErr_NoError:
            return  # error handled in the _handle_event() method

        if self._lookup and servicename != self.servicename:
            return

        resolve_sdref = pyb.DNSServiceResolve(0, interfaceindex, servicename,
                                              regtype, replydomain,
                                              self._resolve_callback)

        # wait for and process resolve the current request
        try:
            while not self._resolved:
                try:
                    ready = select.select([resolve_sdref], list(), list(),
                                          self.timeout)
                except select.error:
                    # purposely ignore errors.
                    continue

                if resolve_sdref not in ready[0]:
                    # not a catastrophic error for the class, therefore,
                    # simply warn that the mDNS service record needed
                    # additional time to process and do not issue an
                    # exception.
                    sys.stderr.write(
                        cw(
                            _('warning: unable to resolve "%s", '
                              'try using a longer timeout\n') % servicename))
                    break
                # process the service
                pyb.DNSServiceProcessResult(resolve_sdref)
            else:
                self._resolved.pop()
        # allow exceptions to fall through
        finally:
            # clean up when there is no exception
            resolve_sdref.close()
    def _browse_callback(self, sdref, flags, interfaceindex, errorcode,
                         servicename, regtype, replydomain):
        '''Method: _browse_callback, class private
        Description:
            DNS Callback for the browse process

        Args
            sdref          - service reference,
                             standard argument for callback, not used
            flags          - flag to determine what action is taking place
                             standard argument for callback, not used
            interfaceindex - the index for the interface that the service was
                             found on
            errorcode      - flag to determine if a registration error occurred
            servicename    - name of the service
            hosttarget     - name of the host, should be <nodename>.local.
            regtype        - registration type, should be _OSInstall._tcp.
            replydomain    - DNS domain, either local or remote

        Returns
            None

        Raises
            None
        '''
        if errorcode != pyb.kDNSServiceErr_NoError:
            return  # error handled in the _handle_event() method

        if self._lookup and servicename != self.servicename:
            return

        resolve_sdref = pyb.DNSServiceResolve(0, interfaceindex,
                                              servicename, regtype,
                                              replydomain,
                                              self._resolve_callback)

        # wait for and process resolve the current request
        try:
            while not self._resolved:
                try:
                    ready = select.select([resolve_sdref], list(), list(),
                                           self.timeout)
                except select.error:
                    # purposely ignore errors.
                    continue

                if resolve_sdref not in ready[0]:
                    # not a catastrophic error for the class, therefore,
                    # simply warn that the mDNS service record needed
                    # additional time to process and do not issue an
                    # exception.
                    sys.stderr.write(cw(_('warning: unable to resolve "%s", '
                                          'try using a longer timeout\n') %
                                          servicename))
                    break
                # process the service
                pyb.DNSServiceProcessResult(resolve_sdref)
            else:
                self._resolved.pop()
        # allow exceptions to fall through
        finally:
            # clean up when there is no exception
            resolve_sdref.close()
def parse_options(cmd_options=None):
    '''
    Parse and validate options
    
    Returns: An options record containing
        arch
        aliasof
        bootargs
        dhcp_ip_count
        dhcp_ip_start
        dhcp_bootserver
        noprompt
        publisher
        srcimage
        svcname
        imagepath
    
    '''
    logging.log(com.XDEBUG, '**** START installadm.create_service.'
                'parse_options ****\n')
    
    usage = '\n' + get_usage()
    description = _('Establishes an Automated Install network service.')
    parser = OptionParser(usage=usage, prog="create-service",
                          description=description)
    parser.add_option('-b', '--boot-args', dest='bootargs', action='append',
                      default=list(),
                      help=_('Comma separated list of <property>=<value>'
                             ' pairs to add to the x86 Grub menu entry'))
    parser.add_option('-a', '--arch', dest='arch', default=None,
                      choices=("i386", "sparc"),
                      help=_("ARCHITECTURE (sparc or i386), desired "
                             "architecture of resulting service when creating "
                             "from a pkg."))
    parser.add_option('-d', '--imagepath', dest='imagepath', default=None,
                      help=_("Path at which to create the net image"))
    parser.add_option('-t', '--aliasof', dest='aliasof', default=None,
                      help=_("Service being created is alias of this serivce"))
    parser.add_option('-n', '--service', dest='svcname',
                      help=_('service name'))
    parser.add_option('-i', '--ip-start', dest='dhcp_ip_start', type='string',
                      help=_('DHCP Starting IP Address'), action="callback",
                      callback=check_ip_address)
    parser.add_option('-c', '--ip-count', dest='dhcp_ip_count',
                      type='int', help=_('DHCP Count of IP Addresses'))
    parser.add_option('-B', '--bootfile-server', dest='dhcp_bootserver',
                      type='string', help=_('DHCP Boot Server Address'),
                      action="callback", callback=check_ip_address)
    parser.add_option('-s', '--source', dest='srcimage',
                      type='string',
                      help=_('FMRI or Auto Install ISO'))
    parser.add_option('-p', '--publisher', help=_("A pkg(5) publisher, in the"
                      " form '<prefix>=<uri>', from which to install the "
                      "client image"))
    parser.add_option('-y', "--noprompt", action="store_true",
                      dest="noprompt", default=False,
                      help=_('Suppress confirmation prompts and proceed with '
                      'service creation using default values'))
    
    options, args = parser.parse_args(cmd_options)
    
    if args:
        parser.error(_('Unexpected argument(s): %s') % args)
    
    # if service name provided, validate it
    if options.svcname:
        try:
            com.validate_service_name(options.svcname)
        except ValueError as err:
            parser.error(err)
        
        # Give error if service already exists
        if config.is_service(options.svcname):
            parser.error(_('\nService already exists: %s\n') % options.svcname)
    
    # If creating an alias, only allow additional options -n, -b,
    # and -y
    if options.aliasof:
        if (options.dhcp_ip_start or options.dhcp_ip_count or
            options.imagepath or options.srcimage):
            parser.error(_('\nOnly options -n|--service, -b|--boot-args, '
                           'and -y|--noprompt\nmay be specified with '
                           '-t|--aliasof.'))
        if not options.svcname:
            parser.error(_('\nOption -n|--service is required with the '
                            '-t|--aliasof option'))
    else:
        name = options.svcname
        if name in DEFAULT_ARCH:
            raise SystemExit(_('\nDefault services must be created as '
                               'aliases. Use -t|--aliasof.\n'))

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

    # check dhcp related options
    if options.dhcp_ip_start or options.dhcp_ip_count:
        if com.is_multihomed():
            # don't allow DHCP setup if multihomed
            parser.error(cw(_('\nDHCP server configuration is unavailable on '
                              'hosts with multiple network interfaces (-i and '
                              '-c options are disallowed).\n')))
        
        # Confirm options -i and -c are both provided
        if options.dhcp_ip_count is None:
            parser.error(_('\nIf -i option is provided, -c option must '
                           'also be provided\n'))
        if not options.dhcp_ip_start:
            parser.error(_('\nIf -c option is provided, -i option must '
                           'also be provided\n'))
        
        # Confirm count of ip addresses is positive
        if options.dhcp_ip_count < 1:
            parser.error(_('\n"-c <count_of_ipaddr>" must be greater than '
                           'zero.\n'))

    if options.dhcp_bootserver:
        # Confirm if the -B is provided, that -i/-c are also
        if options.dhcp_ip_count is None:
            parser.error(_('\nIf -B option is provided, -i option must '
                           'also be provided\n'))
    
    if is_iso(options.srcimage):
        if options.arch is not None:
            parser.error(_("The --arch option is invalid for ISO-based "
                           "services"))
        if options.publisher is not None:
            parser.error(_("The --publisher option is invalid for "
                           "ISO-based services"))

    if options.publisher:
        # Convert options.publisher from a string of form 'prefix=uri' to a
        # tuple (prefix, uri)
        publisher = options.publisher.split("=")
        if len(publisher) != 2:
            parser.error(_('Publisher information must match the form: '
                           '"<prefix>=<URI>"'))
        options.publisher = publisher
    
    # Make sure imagepath meets requirements
    if options.imagepath:
        options.imagepath = options.imagepath.strip()
    if options.imagepath:
        if not options.imagepath == '/':
            options.imagepath = options.imagepath.rstrip('/')
        try:
            check_imagepath(options.imagepath)
        except ValueError as error:
            raise SystemExit(error)
    
    return options
Exemple #37
0
def remove_dhcp_configuration(service):
    '''
    Determines if a local DHCP server is running and if this service's bootfile
    is set as the architecture's default boot service. If it is, we'll unset it
    as we're deleting the service. If the DHCP configuration isn't local,
    inform the end-user that the DHCP configuration should not reference this
    bootfile any longer.
    '''
    logging.debug("in remove_dhcp_configuration, service=%s", service.name)

    # Skip SPARC services, since they share a global bootfile
    if service.arch == 'sparc':
        return

    server = dhcp.DHCPServer()
    if server.is_configured():
        # Server is configured. Regardless of its current state, check for
        # this bootfile in the service's architecture class. If it is set as
        # the default for this architecture, unset it.
        try:
            arch_class = dhcp.DHCPArchClass.get_arch(server, service.arch)
        except dhcp.DHCPServerError as err:
            print >> sys.stderr, cw(
                _("\nUnable to access DHCP configuration: "
                  "%s\n" % err))
            return

        if arch_class is None or arch_class.bootfile is None:
            # nothing to do
            return

        logging.debug("arch_class.bootfile is %s", arch_class.bootfile)
        if isinstance(arch_class.bootfile, list):
            # The list consists of tuples: (archval, relpath to bootfile)
            # e.g., [('00:00', '<svcname>/boot/grub/pxegrub2'),..]
            # Using the first tuple, get the service name.
            relpath = arch_class.bootfile[0][1]
        else:
            relpath = arch_class.bootfile
        parts = relpath.partition('/')
        arch_svcname = parts[0]

        if arch_svcname == service.name:
            try:
                print cw(
                    _("Removing this service's bootfile(s) from local "
                      "DHCP configuration\n"))
                arch_class.unset_bootfile()
            except dhcp.DHCPServerError as err:
                print >> sys.stderr, cw(
                    _("\nUnable to unset this service's "
                      "bootfile(s) in the DHCP "
                      "configuration: %s\n" % err))
                return

            if server.is_online():
                try:
                    server.control('restart')
                except dhcp.DHCPServerError as err:
                    print >> sys.stderr, cw(
                        _("\nUnable to restart the DHCP "
                          "SMF service: %s\n" % err))
def setup_x86_client(service, mac_address, bootargs=''):
    ''' Set up an x86 client

    Creates a relative symlink from the <svcname>'s bootfile to
        /etc/netboot/<client_id>
    Creates /etc/netboot/menu.lst.<client_id> boot configuration file
    Adds client info to AI_SERVICE_DIR_PATH/<svcname>/.config file

    Arguments:
              image_path - directory path to AI image
              mac_address - client MAC address (as formed by
                            MACAddress class, i.e., 'ABABABABABAB')
              bootargs = bootargs of client (x86)
    Returns: Nothing

    '''
    # create a client-identifier (01 + MAC ADDRESS)
    client_id = "01" + mac_address

    menulst = os.path.join(service.config_dir, grub.MENULST)
    client_menulst = _menulst_path(client_id)

    # copy service's menu.lst file to menu.lst.<client_id>
    try:
        shutil.copy(menulst, client_menulst)
    except IOError as err:
        print >> sys.stderr, cw(_("Unable to copy grub menu.lst file: %s") %
           err.strerror)
        return

    # create a symlink from the boot directory to the sevice's bootfile.
    # note this must be relative from the boot directory.
    bootfile, pxegrub_path = _pxegrub_path(client_id)
    os.symlink("./" + service.dhcp_bootfile, pxegrub_path)

    clientinfo = {config.FILES: [client_menulst, pxegrub_path]}

    # if the client specifies bootargs, use them. Otherwise, inherit
    # the bootargs specified in the service (do nothing)
    if bootargs:
        grub.update_bootargs(client_menulst, service.bootargs, bootargs)
        clientinfo[config.BOOTARGS] = bootargs

    config.add_client_info(service.name, client_id, clientinfo)

    # Configure DHCP for this client if the configuration is local, otherwise
    # suggest the configuration addition. Note we only need to do this for
    # x86-based clients, not SPARC.
    server = dhcp.DHCPServer()
    if server.is_configured():
        # We'll need the actual hardware ethernet address for the DHCP entry,
        # rather than the non-delimited string that 'mac_address' is.
        full_mac = AIdb.formatValue('mac', mac_address)
        try:
            print cw(_("Adding host entry for %s to local DHCP configuration.")
                       % full_mac)
            server.add_host(full_mac, bootfile)
        except dhcp.DHCPServerError as err:
            print cw(_("Unable to add host (%s) to DHCP configuration: %s") %
                      (full_mac, err))
            return

        if server.is_online():
            try:
                server.control('restart')
            except dhcp.DHCPServerError as err:
                print >> sys.stderr, cw(_("\nUnable to restart the DHCP SMF "
                                          "service: %s\n" % err))
                return
        else:
            print cw(_("\nLocal DHCP configuration complete, but the DHCP "
                       "server SMF service is offline. To enable the "
                       "changes made, enable: %s.\nPlease see svcadm(1M) "
                       "for further information.\n") % 
                       dhcp.DHCP_SERVER_IPV4_SVC)
    else:
        # No local DHCP, tell the user all about their boot configuration
        valid_nets = list(com.get_valid_networks())
        if valid_nets:
            server_ip = valid_nets[0]

        print _(_PXE_CLIENT_DHCP_CONFIG % (server_ip, bootfile))

        if len(valid_nets) > 1:
            print cw(_("\nNote: determined more than one IP address "
                       "configured for use with AI. Please ensure the above "
                       "'Boot server IP' is correct.\n"))
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
Exemple #40
0
def setup_x86_client(service,
                     mac_address,
                     bootargs='',
                     suppress_dhcp_msgs=False):
    ''' Set up an x86 client

    Creates relative symlink(s) in /etc/netboot::
        <client_id>.<archtype> -> ./<svcname>/<bootfile_path>
        e.g., 01223344223344.bios -> ./mysvc/boot/grub/pxegrub
    Creates /etc/netboot/<cfg_file>.<client_id> boot configuration file
    Adds client info to AI_SERVICE_DIR_PATH/<svcname>/.config file

    Arguments:
              service - the AIService to associate with client
              mac_address - client MAC address (as formed by
                            MACAddress class, i.e., 'ABABABABABAB')
              bootargs = bootargs of client (x86)
              suppress_dhcp_msgs - if True, suppresses output of DHCP
                                   configuration messages
    Returns: Nothing

    '''
    # create a client-identifier (01 + MAC ADDRESS)
    client_id = "01" + mac_address
    clientinfo = dict()

    svcgrub = grubcfg(service.name,
                      path=service.image.path,
                      config_dir=service.config_dir)

    # Call setup_client - it will return netconfig_files, config_files,
    # and tuples, e.g.:
    # netconfig_files:  ['/etc/netboot/menu.lst.01234234234234']
    # config_files:  ['/etc/netboot/menu.conf.01234234234234']
    # boot_tuples:  [('00:00', 'bios', 'mysvc/boot/grub/pxegrub2'),
    #                ('00:07', 'uefi', 'mysvc/boot/grub/grub2netx64.efi')]
    # If the client specifies bootargs, use them. Otherwise, inherit
    # the bootargs specified in the service.
    netconfig_files, config_files, boot_tuples = \
        svcgrub.setup_client(mac_address, bootargs=bootargs,
                             service_bootargs=service.bootargs)

    # update the bootargs in the service .config file
    if bootargs:
        clientinfo[config.BOOTARGS] = bootargs

    # keep track of client files to delete when client is removed
    client_files = list(netconfig_files)
    client_files.extend(config_files)

    # create symlink(s) from the boot directory to the sevice's bootfile(s).
    # note these must be relative from the boot directory.
    for arch, archtype, relpath in boot_tuples:
        # name of symlink is /etc/netboot/<clientid>.<archtype>
        bootfile_symlink = _bootfile_path(client_id, archtype)
        dotpath = './' + relpath
        logging.debug('creating symlink %s->%s', bootfile_symlink, dotpath)
        os.symlink(dotpath, bootfile_symlink)
        client_files.append(bootfile_symlink)

        # if this is archtype bios, create <clientid> symlink to
        # <clientid>.bios for backward compatibility with existing dhcp
        # servers.
        if archtype == 'bios':
            clientid_path = os.path.join(com.BOOT_DIR, client_id)
            dot_client_arch = './' + client_id + '.' + archtype
            logging.debug('creating symlink %s->%s', clientid_path,
                          dot_client_arch)
            os.symlink(dot_client_arch, clientid_path)

    logging.debug('adding client_files to .config: %s', client_files)
    clientinfo[config.FILES] = client_files
    config.add_client_info(service.name, client_id, clientinfo)

    # Configure DHCP for this client if the configuration is local, otherwise
    # suggest the configuration addition. Note we only need to do this for
    # x86-based clients, not SPARC.
    server = dhcp.DHCPServer()
    if server.is_configured():
        # We'll need the actual hardware ethernet address for the DHCP entry,
        # rather than the non-delimited string that 'mac_address' is.
        full_mac = AIdb.formatValue('mac', mac_address)
        try:
            if not suppress_dhcp_msgs:
                print cw(
                    _("Adding host entry for %s to local DHCP "
                      "configuration.") % full_mac)
            server.add_option_arch()
            server.add_host(full_mac, boot_tuples)
        except dhcp.DHCPServerError as err:
            print cw(
                _("Unable to add host (%(mac)s) to DHCP "
                  "configuration: %(error)s") % {
                      'mac': full_mac,
                      'error': err
                  })
            return

        if server.is_online():
            try:
                server.control('restart')
            except dhcp.DHCPServerError as err:
                print >> sys.stderr, cw(
                    _("\nUnable to restart the DHCP SMF "
                      "service: %s\n" % err))
                return
        elif not suppress_dhcp_msgs:
            print cw(
                _("\nLocal DHCP configuration complete, but the DHCP "
                  "server SMF service is offline. To enable the "
                  "changes made, enable: %s.\nPlease see svcadm(1M) "
                  "for further information.\n") % dhcp.DHCP_SERVER_IPV4_SVC)
    else:
        # No local DHCP, tell the user all about their boot configuration
        valid_nets = list(com.get_valid_networks())
        if valid_nets:
            server_ip = valid_nets[0]

        if not suppress_dhcp_msgs:
            boofile_text = '\n'
            for archval, archtype, relpath in boot_tuples:
                bootfilename = client_id + '.' + archtype
                boofile_text = (boofile_text + '\t' + archtype +
                                ' clients (arch ' + archval + '):  ' +
                                bootfilename + '\n')
            print _(_PXE_CLIENT_DHCP_CONFIG % (server_ip, boofile_text))

            if len(valid_nets) > 1:
                print cw(
                    _("\nNote: determined more than one IP address "
                      "configured for use with AI. Please ensure the "
                      "above 'Boot server IP' is correct.\n"))
Exemple #41
0
def setup_x86_client(service, mac_address, bootargs=''):
    ''' Set up an x86 client

    Creates a relative symlink from the <svcname>'s bootfile to
        /etc/netboot/<client_id>
    Creates /etc/netboot/menu.lst.<client_id> boot configuration file
    Adds client info to AI_SERVICE_DIR_PATH/<svcname>/.config file

    Arguments:
              image_path - directory path to AI image
              mac_address - client MAC address (as formed by
                            MACAddress class, i.e., 'ABABABABABAB')
              bootargs = bootargs of client (x86)
    Returns: Nothing

    '''
    # create a client-identifier (01 + MAC ADDRESS)
    client_id = "01" + mac_address

    menulst = os.path.join(service.config_dir, grub.MENULST)
    client_menulst = _menulst_path(client_id)

    # copy service's menu.lst file to menu.lst.<client_id>
    try:
        shutil.copy(menulst, client_menulst)
    except IOError as err:
        print >> sys.stderr, cw(
            _("Unable to copy grub menu.lst file: %s") % err.strerror)
        return

    # create a symlink from the boot directory to the sevice's bootfile.
    # note this must be relative from the boot directory.
    bootfile, pxegrub_path = _pxegrub_path(client_id)
    os.symlink("./" + service.dhcp_bootfile, pxegrub_path)

    clientinfo = {config.FILES: [client_menulst, pxegrub_path]}

    # if the client specifies bootargs, use them. Otherwise, inherit
    # the bootargs specified in the service (do nothing)
    if bootargs:
        grub.update_bootargs(client_menulst, service.bootargs, bootargs)
        clientinfo[config.BOOTARGS] = bootargs

    config.add_client_info(service.name, client_id, clientinfo)

    # Configure DHCP for this client if the configuration is local, otherwise
    # suggest the configuration addition. Note we only need to do this for
    # x86-based clients, not SPARC.
    server = dhcp.DHCPServer()
    if server.is_configured():
        # We'll need the actual hardware ethernet address for the DHCP entry,
        # rather than the non-delimited string that 'mac_address' is.
        full_mac = AIdb.formatValue('mac', mac_address)
        try:
            print cw(
                _("Adding host entry for %s to local DHCP configuration.") %
                full_mac)
            server.add_host(full_mac, bootfile)
        except dhcp.DHCPServerError as err:
            print cw(
                _("Unable to add host (%s) to DHCP configuration: %s") %
                (full_mac, err))
            return

        if server.is_online():
            try:
                server.control('restart')
            except dhcp.DHCPServerError as err:
                print >> sys.stderr, cw(
                    _("\nUnable to restart the DHCP SMF "
                      "service: %s\n" % err))
                return
        else:
            print cw(
                _("\nLocal DHCP configuration complete, but the DHCP "
                  "server SMF service is offline. To enable the "
                  "changes made, enable: %s.\nPlease see svcadm(1M) "
                  "for further information.\n") % dhcp.DHCP_SERVER_IPV4_SVC)
    else:
        # No local DHCP, tell the user all about their boot configuration
        valid_nets = list(com.get_valid_networks())
        if valid_nets:
            server_ip = valid_nets[0]

        print _(_PXE_CLIENT_DHCP_CONFIG % (server_ip, bootfile))

        if len(valid_nets) > 1:
            print cw(
                _("\nNote: determined more than one IP address "
                  "configured for use with AI. Please ensure the above "
                  "'Boot server IP' is correct.\n"))
Exemple #42
0
def do_update_service(cmd_options=None):
    '''Update a service - currently only an alias

    Copy the baseservice of an alias, update it, create a service using
    the updated image, and re-alias the alias to the new service.

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

    options = parse_options(cmd_options)

    service = options.service
    try:
        if not service.image.is_pkg_based():
            raise SystemExit(
                cw(
                    _("\nError: '%s' is aliased to an iso based service. Only "
                      "aliases of pkg(5) based services are updatable.") %
                    options.service_name))
    except pkg.client.api_errors.VersionException as err:
        print >> sys.stderr, cw(
            _("The IPS API version specified, %(specver)s,"
              " is incompatible with the expected version, %(expver)s.") % {
                  'specver': str(err.received_version),
                  'expver': str(err.expected_version)
              })
        raise SystemExit()

    fmri = [options.srcimage] if options.srcimage else None
    base_svc = svc.AIService(service.basesvc, image_class=InstalladmPkgImage)

    new_image = None
    try:
        print _("Copying image ...")
        new_image = base_svc.image.copy(prefix=base_svc.name)
        print _('Updating image ...')
        update_needed = new_image.update(fmri=fmri,
                                         publisher=options.publisher)
    except (ValueError, img.ImageError,
            pkg.client.api_errors.ApiException) as err:
        # clean up copied image
        if new_image:
            new_image.delete()
        print >> sys.stderr, cw(
            _("Attempting to update the service %s"
              " failed for the following reasons:") % service.name)
        if isinstance(err, pkg.client.api_errors.CatalogRefreshException):
            for pub, error in err.failed:
                print >> sys.stderr, "   "
                print >> sys.stderr, str(error)
            if err.errmessage:
                print >> sys.stderr, err.errmessage
        raise SystemExit(err)

    if not update_needed:
        # No update needed, remove copied image
        new_image.delete()
        print _("No updates are available.")
        return 4

    # Get the service name from the updated image metadata
    new_svcname = img.get_default_service_name(image=new_image)

    # Determine imagepath and move image there
    new_path = new_image.path
    new_imagepath = os.path.join(os.path.dirname(new_path), new_svcname)
    try:
        img.check_imagepath(new_imagepath)
    except ValueError as error:
        # Leave image in temp location rather than fail. User can
        # update imagepath later if desired.
        logging.debug('unable to move image from %s to %s: %s', new_path,
                      new_imagepath, error)
    else:
        new_path = new_image.move(new_imagepath)
        logging.debug('image moved to %s', new_path)
    finally:
        img.set_permissions(new_path)

    # create new service based on updated image
    print _("Creating new %(arch)s service: %(newsvc)s") % \
            {'arch': new_image.arch, 'newsvc': new_svcname}
    new_service = svc.AIService.create(new_svcname, new_image)

    # Register & enable service
    # (Also enables system/install/server, as needed)
    try:
        service.enable()
    except (config.ServiceCfgError, svc.MountError) as err:
        raise SystemExit(err)
    except aismf.ServicesError as err:
        # don't error out if the service is successfully created
        # but the services fail to start - just print out the error
        # and proceed
        print err

    print _("Aliasing %(alename)s to %(newsvc)s ...\n") % \
            {'alename': service.name, 'newsvc': new_svcname}
    failures = setsvc.do_update_basesvc(service, new_service.name)
    if failures:
        return 1
    return 0
Exemple #43
0
def parse_options(cmd_options=None):
    '''
    Parse and validate options
    
    Returns: An options record containing
        arch
        aliasof
        bootargs
        dhcp_ip_count
        dhcp_ip_start
        dhcp_bootserver
        noprompt
        publisher
        srcimage
        svcname
        imagepath
    
    '''
    logging.log(com.XDEBUG, '**** START installadm.create_service.'
                'parse_options ****\n')

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

    options, args = parser.parse_args(cmd_options)

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

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

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

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

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

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

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

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

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

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

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

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

    return options
Exemple #44
0
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()
Exemple #45
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
Exemple #46
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
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