7160818 installadm-convert should be removed
authorHarold N Shaw- Oracle <Harold.Shaw@oracle.COM>
Mon, 04 Jun 2012 20:25:35 -0700
changeset 1697 bf9084c8b30d
parent 1696 065053dd54cc
child 1698 e99cb9117b78
7160818 installadm-convert should be removed
usr/src/cmd/installadm/Makefile
usr/src/cmd/installadm/installadm-convert.py
usr/src/cmd/installadm/svc-install-server
usr/src/cmd/rbac/exec_attr.install%2Finstalladm
usr/src/pkg/manifests/install-installadm.mf
--- a/usr/src/cmd/installadm/Makefile	Mon Jun 04 19:19:12 2012 +0200
+++ b/usr/src/cmd/installadm/Makefile	Mon Jun 04 20:25:35 2012 -0700
@@ -67,7 +67,7 @@
 
 MSGFILES =      $(PYMODULES) $(PYTHON_MODULES) $(PYTHON_EXECS:%=%.py) 
 
-PYTHON_EXECS =	installadm installadm-convert
+PYTHON_EXECS =	installadm
 
 PYCMODULES=	$(PYMODULES:%.py=%.pyc)
 
@@ -100,9 +100,6 @@
 installadm:	installadm.py
 		cp installadm.py installadm
 
-installadm-convert:	installadm-convert.py
-		cp installadm-convert.py installadm-convert
-
 clean:
 	rm -f $(SCRIPTS) *.pyc $(PYTHON_EXECS) $(POFILE)
 
--- a/usr/src/cmd/installadm/installadm-convert.py	Mon Jun 04 19:19:12 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2030 +0,0 @@
-#!/usr/bin/python2.6
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
-#
-"""
-This file contains the code necessary to convert an AI server configuration
-from S11 Express to S11 FCS.  The changes made convert any existing AI
-services, clients, and DHCP configuration created before installing S11 FCS.
-"""
-import fileinput
-import glob
-import logging
-import optparse
-import os
-import re
-import shutil
-import socket
-import struct
-import sys
-import tempfile
-
-import osol_install.auto_install.AI_database as AIdb
-import osol_install.auto_install.ai_smf_service as aismf
-import osol_install.auto_install.dhcp as dhcp
-import osol_install.auto_install.installadm_common as com
-import osol_install.auto_install.service as svc
-import osol_install.auto_install.service_config as config
-import osol_install.libaiscf as smf
-
-from osol_install.auto_install import create_client
-from osol_install.auto_install.grub import AIGrubCfg as grubcfg
-from osol_install.auto_install.installadm_common import _, cli_wrap as cw
-from osol_install.auto_install.service import AIService
-from solaris_install import Popen, CalledProcessError, check_auth_and_euid, \
-    SERVICE_AUTH, SetUIDasEUID, UnauthorizedUserError
-
-VERSION = _("%prog: version 1.0")
-
-# old AI service property group keys and values
-PROP_BOOT_FILE = 'boot_file'
-PROP_DEF_MANIFEST = 'default-manifest'
-PROP_IMAGE_PATH = 'image_path'
-PROP_SERVICE_NAME = 'service_name'
-PROP_STATUS = 'status'
-PROP_TXT_RECORD = 'txt_record'
-PROP_VERSION = 'version'
-
-EXIT_SUCCESS = 0
-EXIT_FAILURE = 1
-
-DEFAULT_LOG_LEVEL = logging.WARN
-DEBUG_LOG_LEVEL = logging.DEBUG
-TFTPBOOT = '/tftpboot'
-NETBOOT = '/etc/netboot'
-SERVICE_DIR = '/var/ai/service'
-AI_SVC_FMRI = 'system/install/server:default'
-SOLARIS_DHCP_SVC_FMRI = 'network/dhcp-server:default'
-ISC_DHCP_CONFIG = '/var/ai/isc_dhcp.conf'
-NETBOOT_ERR = _("""
-Conversion continuing.  To manually complete this step:
-   Move all required non Automated Install files from /tftpboot to /etc/netboot
-   Delete /tftpboot
-   Create /tftpboot as a symlink to /etc/netboot
-""")
-DHCP_ERR = _("""
-DHCP Conversion error - conversion continuing. Some by-hand translation will be
-required. The untranslated items are tagged with '%(<element>)s' where element
-is the description of the required value.
-""")
-
-
-class ServiceConversionError(Exception):
-    """
-    Some type of error occurred while upgrading an AI service.  The exact cause
-    of the error should have been logged.
-    """
-    pass
-
-
-class SUNDHCPData:
-    """
-    Class to query Solaris DHCP server configuration material
-    """
-
-    def __init__(self):
-        """
-        No state stored and this is server wide data, so do not make an
-        instance of this class
-        """
-        raise NotImplementedError("class does not support creating an "
-                                  "instance.")
-
-    class DHCPError(Exception):
-        """
-        Class to report various DHCP related errors
-        """
-        pass
-
-    @staticmethod
-    def _unpack_addr(ipaddr):
-        """
-        Return the integer equivalent of the input ascii IP address
-        """
-        return struct.unpack('L', socket.inet_aton(ipaddr))[0]
-
-    @staticmethod
-    def _pack_addr(ipaddr):
-        """
-        Return the ascii IP address equivalent of the input value
-        """
-        return socket.inet_ntoa(struct.pack('L', ipaddr))
-
-    @staticmethod
-    def next_in_range(cur_ipaddr, last_ipaddr):
-        """
-        Return true if the ipaddr is the next in the range of IP addresses
-        whose last IP address is currently defined by last_ipaddr
-
-        This function treats addresses in the network as Class C.  The
-        first 3 octets are treated as the network bits and the last octet is
-        treated as the host bits.  That is not true but it does simplify the
-        processing. It is legal to have a range of addresses such as
-        192.168.12.240-192.168.13.32.  This is treated as 2 ranges
-        (192.168.12.240-192.168.12.254) and (192.168.13.0-192.168.13.32).
-        """
-        # Set up a regular expression to pull the last tuple from an IP
-        regexp = re.compile("^(\d{1,3}\.\d{1,3}\.\d{1,3})\.(\d{1,3})$")
-
-        # Break the IP address into two string values
-        m = regexp.match(cur_ipaddr)
-        if m is None:
-            raise SUNDHCPData.DHCPError("generate_ip_ranges: bad IP "
-                                        "format [%s]" % cur_ipaddr)
-
-        cur_network_octet = m.group(1)
-        cur_host_octet = m.group(2)
-
-        # Break the IP address into two string values;
-        m = regexp.match(last_ipaddr)
-        if m is None:
-            raise SUNDHCPData.DHCPError("generate_ip_ranges: bad IP "
-                           "format [%s]" % last_ipaddr)
-
-        last_network_octet = m.group(1)
-        last_host_octet = m.group(2)
-
-        if cur_network_octet != last_network_octet or \
-            int(cur_host_octet) != int(last_host_octet) + 1:
-
-            return False
-
-        return True
-
-    @staticmethod
-    def generate_ip_ranges(clients):
-        """
-        Generate the set of ip ranges from the list of clients
-        passed into this method.  The list of clients will be those
-        returned by the call to 'pntadm -P <network>'. The set of networks
-        is generated by calling 'pntadm -L'.  This method is called once for
-        each network.
-        The output will be a list of dictionaries of the form:
-            [{'nextserver':<IP addr>, 'loaddr':<IP addr>, 'hiaddr':<IP addr>}]
-
-        The output of this method is used to generate the ISC DHCP range
-        values in the subnet stanzas.
-        """
-        ranges = list()
-        last_addr = None
-        start_addr = None
-
-        # Get a sorted list of the ip addresses by unpacking, sorting, and
-        # then returning it to its original form
-        sorted_clients = map(SUNDHCPData._pack_addr,
-            sorted([SUNDHCPData._unpack_addr(client) for \
-            client in clients['Client IP']]))
-
-        # Create a list of dictionaries each containing a range of
-        # consecutive IP addresses in the DHCP network table and the
-        # server IP for that range
-        for client_addr in sorted_clients:
-            # if first time throught the loop - init the range start
-            if start_addr is None:
-                start_addr = client_addr
-            elif not SUNDHCPData.next_in_range(client_addr, last_addr):
-                # if not then create range list entry and reset the start
-                # value for the range
-                ranges.append({'nextserver': clients['Server IP'][0],
-                              'loaddr': start_addr, 'hiaddr': last_addr})
-                start_addr = client_addr
-
-            # This is now the last IP address in the range
-            last_addr = client_addr
-
-        # Create 'range' list entry with any remaining addresses
-        ranges.append({'nextserver': clients['Server IP'][0],
-                      'loaddr': start_addr, 'hiaddr': last_addr})
-
-        return ranges
-
-    @staticmethod
-    def subnets():
-        """
-        Return a list of networks configured on the DHCP server, if any error
-        is generated raise it as a DHCPError.
-        """
-        # first get a list of networks served
-        cmd = ["/usr/sbin/pntadm", "-L"]
-        try:
-            with SetUIDasEUID():
-                pipe = Popen.check_call(cmd, stdout=Popen.STORE,
-                                        stderr=Popen.DEVNULL)
-        except CalledProcessError as err:
-            # return a DHCPError on failure
-            raise SUNDHCPData.DHCPError(err)
-
-        # produce a list of networks like
-        # ["172.20.24.0",
-        #  "172.20.48.0"]
-        #
-        # get the list of clients, sort it and generate the IP ranges
-        subnets = dict()
-        for net in pipe.stdout.split():
-            try:
-                clients = SUNDHCPData.clients(net)
-            except SUNDHCPData.DHCPError as err:
-                sys.stderr.write(str(err) + "\n")
-                continue
-
-            # Produce a dictionary of the form:
-            #  {<subnet>:[{start_ip: <ip addr> count:<num>}...]}
-            try:
-                subnets[net] = SUNDHCPData.generate_ip_ranges(clients)
-            except SUNDHCPData.DHCPError as err:
-                sys.stderr.write(str(err) + "\n")
-                continue
-
-        return subnets
-
-    @staticmethod
-    def _get_include_vals(name, macros):
-        """
-        Expand the include keyword with it's underlying key/value pairs in
-        the macro that includes it.
-        """
-        include_expansion = dict()
-        # Expansion is done only on 'Include' keywords
-        if 'Include' in macros[name]:
-            # Add the value of the Include keyword expansion
-            include_value = macros[name]['Include']
-            # Check to make sure that the included macro actually exists
-            if include_value in macros:
-                include_expansion.update(macros[include_value])
-                # Recursively call this method to pick up any embedded Includes
-                include_expansion.update(SUNDHCPData._get_include_vals(
-                                      include_value, macros))
-
-        return include_expansion
-
-    @staticmethod
-    def _parse_macro_options(macro_vals):
-        """
-        Parse the macro values into a dictionary to be used in generating the
-        ISC configuration
-        """
-        # translation table to change keywords in the Solaris DHCP
-        # configuration tables correspond to those expected when creating the
-        # ISC DHCP stanzas
-        keyword_map = {'Subnet': 'netmask',
-                       'Router': 'routers',
-                       'Broadcst': 'broadcast',
-                       'BootFile': 'bootfile',
-                       'DNSdmain': 'domainname',
-                       'DNSserv': 'nameservers'}
-
-        keyval_dict = dict()
-        for keyval in macro_vals[1:-1].split(':'):
-            key_pair = keyval.split('=')
-            if len(key_pair) != 2:
-                continue
-
-            # Add the entry into the dict making sure to translate the
-            # key names if they are in the translation table otherwise
-            # keep the original keyword name. Remove any enclosing quotes
-            keyval_dict[keyword_map.get(key_pair[0], key_pair[0])] = \
-                key_pair[1].replace('"', '')
-
-        return keyval_dict
-
-    @staticmethod
-    def macros():
-        """
-        Returns a dictionary of macros and symbols with keys: Name, Type and
-        Value. In case of an error raises a DHCPError
-        """
-        # get a list of all server macros
-        cmd = ["/usr/sbin/dhtadm", "-P"]
-        try:
-            with SetUIDasEUID():
-                pipe = Popen.check_call(cmd, stdout=Popen.STORE,
-                                        stderr=Popen.DEVNULL)
-        except CalledProcessError as err:
-            raise SUNDHCPData.DHCPError(err)
-
-        # produce a list like:
-        # ['Name", "Type", "Value",
-        #  "==================================================",
-        #  "0100093D143663", "Macro",
-        #  ":BootSrvA=172.20.25.12:BootFile="0100093D143663":"]
-
-        # split into fields and create a dictionary with the name and
-        # keyword/value pairs
-        config_list = [ln.split(None, 2) for ln in
-                       pipe.stdout.splitlines()][2:]
-        macro_dict = dict()
-        # Create a dictionary that contains {name: {key:val, ...}}
-        for config_line in config_list:
-            if config_line[1] == 'Macro':
-                macro_dict[config_line[0]] = \
-                    SUNDHCPData._parse_macro_options(config_line[2])
-
-        # Each Solaris DHCP macro is comprised of keyword value pairs as
-        # shown above.  The following block will expand the Include keyword
-        # into its keyword value pairs
-        # {v20z-brm-02 :  {Include : Locale, Timeserv : 10.80.127.2}
-        #  Locale : {UTCoffset : -25200}}
-        # will be expanded to
-        # {v20z-brm-02 : {UTCoffset : -25200, Timeserv : 10.80.127.2} ...}
-        for mname in macro_dict:
-            # Add the expansion of the Include keywords to the macro
-            macro_dict[mname].update(
-                SUNDHCPData._get_include_vals(mname, macro_dict))
-            # If this entry contains the 'netmask' keyword then the
-            # macro name is a subnet specification.  This is needed to form
-            # the ISC DHCP subnet stanza so add it to the dictionary
-            if 'netmask' in macro_dict[mname]:
-                macro_dict[mname].update({"subnet": mname})
-        return macro_dict
-
-    @staticmethod
-    def clients(net):
-        """
-        Return a dictionary with keys 'Client ID', 'Flags', 'Client IP',
-        'Server IP', 'Lease Expiration', 'Macro', 'Comment', on error raise a
-        DHCPError
-        """
-        # iterate over the networks looking for clients
-        # keep state in the dictionary so initialize it out side the loop
-        systems = dict()
-        cmd = ["/usr/sbin/pntadm", "-P", net]
-        try:
-            with SetUIDasEUID():
-                pipe = Popen.check_call(cmd, stdout=Popen.STORE,
-                                        stderr=Popen.DEVNULL)
-        except CalledProcessError as err:
-            raise SUNDHCPData.DHCPError(err)
-
-        # use split to produce a list like:
-        # ['Client ID', 'Flags', 'Client IP', 'Server IP',
-        #  'Lease Expiration', 'Macro', 'Comment']
-        # ['']
-        # ['01001B21361F85', '00', '172.20.24.228', '172.20.25.12',
-        #  '08/21/2009', 'dhcp_macro_clay_ai_x86', '']
-        # ['0100093D1432AD', '01', '172.20.24.214', '172.20.25.12',
-        #  '08/21/2009', 'dhcp_macro_clay_ai_sparc', '']
-        # ['01002128262DD2', '00', '172.20.24.215', '172.20.25.12',
-        #  '08/21/2009', 'install', '']
-        # ['']
-
-        # split on newlines, then split on tabs (for a maximum of 7 fields)
-        systems['out'] = [ln.split("\t", 6) for
-            ln in pipe.stdout.splitlines()]
-
-        # the first line will be the headers
-        headers = systems['out'][0]
-        # strip white space
-        headers = [obj.strip() for obj in headers]
-
-        # strip headers, intermediate blank and footer blank
-        del(systems['out'][0:2])
-
-        # build a dict with each header a key to a list built of each row
-        systems['data'] = dict(zip(headers, [[row[i] for row in systems['out']]
-                               for i in range(0, len(headers))]))
-
-        # strip white space in data
-        for key in systems['data']:
-            systems['data'][key] = [obj.strip()
-                                   for obj in systems['data'][key]]
-        return systems['data']
-
-
-def find_tftp_root():
-    """
-    Uses get_exec_prop to get tftp root directory via the property
-    inetd_start/exec.
-
-    Args
-        None
-
-    Returns
-        directory name (type string) - default /tftpboot
-
-    Raises
-        None
-    """
-    return get_exec_prop().partition(" -s\ ")[2] or TFTPBOOT
-
-
-class VFSTab(com.DBBase):
-    """
-    Implements object oriented access to the /etc/vfstab file. One can query
-    fields through dictionary style key look-ups (i.e. vfstab_obj['DEVICE']) or
-    attribute access such as vfstab_obj.fields.DEVICE both return lists which
-    are indexed so that a line from the file can be reconstructed as
-    vfstab_obj[device][idx]\tvfstab_obj[fsckDevice]...
-    Otherwise, one can read and write the entire file via
-    vfstab_obj.file_obj.raw.
-
-    For accessing fields it is recommended one not use the direct strings
-    (i.e. 'device', 'fsckDev', etc.) but using the attribute,
-    etc. to allow the implementation to evolve, if necessary.
-
-    One can remove a record in the file by running:
-    del(vfstab_obj.fields.FIELD[idx])
-    """
-    # field variables
-    _DEVICE = "device to mount"
-    _FSCK_DEVICE = "device to fsck"
-    _MOUNT_POINT = "mount point"
-    _FS_TYPE = "FS type"
-    _FSCK_PASS = "fsck pass"
-    _MOUNT_AT_BOOT = "mount at boot"
-    _FS_OPTS = "mount options"
-
-    # the order of headers from the file needs to be recorded
-    _headers = [_DEVICE, _FSCK_DEVICE, _MOUNT_POINT, _FS_TYPE, _FSCK_PASS,
-        _MOUNT_AT_BOOT, _FS_OPTS]
-
-    # the attribute accessor names (all capital variables of the class with
-    # their leading underscore stripped) should be stored for building a list
-    # of field names
-    _fields = [obj.lstrip("_") for obj in locals().keys()
-        if obj.isupper() and locals()[obj] is not None]
-
-    def __init__(self, file_name="/etc/vfstab", mode="r"):
-        super(VFSTab, self).__init__(file_name=file_name, mode=mode)
-
-
-class GrubMenu(com.DBBase):
-    """
-    Class to handle opening and reading GRUB menu, see
-    http://www.gnu.org/software/grub/manual/grub.html for more on GRUB menu
-    format
-    Keys will be the grub menu entries and key "" will be the general commands
-    which begins the menu before the first title
-    """
-
-    # field variables
-    _TITLE = "title"
-
-    # the order of headers from the file needs to be recorded
-    _headers = [_TITLE]
-
-    # the attribute accessor names (all capital variables of the class with
-    # their leading underscore stripped) should be stored for building a list
-    # of field names
-    _fields = [obj.lstrip("_") for obj in locals().keys()
-        if obj.isupper() and locals()[obj] is not None]
-
-    # overload the _Result class to be a dictionary instead of the default list
-    class _Result(dict):
-        """
-        Wrap dict class to ignore the parent reference passed to the _Result
-        class (which would normally be used for updating the backing store of a
-        DBBase() instance)
-        """
-        def __init__(self, parent, key):
-            # store what we are representing (initialize a dictionary with the
-            # data to store)
-            super(GrubMenu._Result, self).__init__(
-                super(com.DBBase, parent).get(key))
-
-    def __init__(self, file_name="/boot/grub/menu.lst", mode="r"):
-        super(GrubMenu, self).__init__(file_name=file_name, mode=mode)
-
-    def _load_data(self):
-        """
-        Load each entry and the keys for each grub menu entry (such as module,
-        kernel$, splashimage, etc. lines)
-        Miscellaneous note: the module and kernel lines may have a '$' after
-        them or not, consumer beware
-        """
-        # see if the file has changed since last read
-        if self.mtime == self.file_obj.last_update:
-            return
-
-        # file has changed since last read
-        file_data = self.file_obj.read_all()
-
-        # update the file mtime to keep track
-        self.mtime = self.file_obj.last_update
-
-        # need to clear current entries
-        super(com.DBBase, self).clear()
-
-        # the menu begins with general commands. The keyword "title" must
-        # begin boot entries and they are either terminated by other title
-        # entries or the end of the file. Split on the title keyword as such
-        # (first "entry" from the split contains the general commands and is
-        # not an actual entry therefore, but prepend a "\n" in case we have no
-        # general commands)
-        entries = re.compile("\n\s*title\s*").split("\n" + file_data)
-        # check that we got a general commands section and at least one title
-        # if not, return an empty dictionary
-        if len(entries) < 2:
-            return dict()
-
-        # parse each entry splitting title and data off - expecting all title
-        # lines to be followed by at least one line of keyword data
-        # produces a list of lists: [entry title, [entry lines]]
-        entry_data = [[entry.split('\n', 1)[0], entry.split('\n')[1:]] for
-            entry in entries]
-
-        # add to self a list of [entry title, dictionary]
-        # with the dictionary containing GRUB keywords as keys and the
-        # values of those keys being the keyword arguments
-        for (title, entry) in entry_data:
-
-            # hold key/value tags in a dictionary for this entry
-            entry_dict = dict()
-
-            # iterate over all lines
-            for line in entry:
-                # skip empty lines or comments
-                if not line.strip() or line.lstrip().startswith("#"):
-                    continue
-                # some GRUB menus have things like
-                # timeout = 30 opposed to timeout 30, replace the = with a
-                # space
-                line = re.sub('^(?P<key>[^\s=]*?)=', '\g<key> ', line)
-                entry_dict.update([line.lstrip().split(None, 1)])
-
-            # add this GRUB entry to the GrubMenu object's dict
-            # key is the entry's title and entry_dict is its object
-            super(com.DBBase, self).update({title: entry_dict})
-
-    # provide the entries of the grub menu as a property
-    @property
-    def entries(self):
-        """
-        Return a list of all Grub title entries in the GRUB menu
-        """
-        # need to return all keys except "" which are the general menu commands
-        return filter(bool, self.keys())
-
-
-def which_arch(path):
-    """
-    Looks to see if the platform pointed to by path is x86 or sparc.
-    If the path does not exist then we are unable to determine the
-    architecture.
-
-    Args
-        path = directory path to examine
-
-    Returns
-        x86     if <path>/platform/i86pc exists
-        sparc   if <path>/platform/sun4v exists
-        -       if neither exists
-
-    Raises
-        None
-    """
-    platform = os.path.join(path, 'platform/i86pc')
-    for root, dirs, files in os.walk(platform):
-        if 'i86pc' in dirs or 'amd64' in dirs:
-            return 'i386'
-        elif 'sun4v' in dirs or 'sun4u' in dirs:
-            return 'sparc'
-        else:
-            return '-'
-
-
-def get_exec_prop():
-    """
-    Uses svcprop on the service svc:/network/tftp/udp6 to get
-    tftp root directory via the property inetd_start/exec.
-    The svcprop command is either (stdout):
-
-        /usr/sbin/in.tftpd -s /tftpboot\n
-
-    Or (stderr):
-
-        svcprop: Pattern 'tftp/udp6' doesn't match any entities
-
-    Args
-        None
-
-    Returns
-        directory name (type string) - default /tftpboot
-
-    Raises
-        None
-    """
-
-    cmd = ["/usr/bin/svcprop", "-p", "inetd_start/exec", "tftp/udp6"]
-    try:
-        pipe = Popen.check_call(cmd, stdout=Popen.STORE, stderr=Popen.DEVNULL)
-    except CalledProcessError:
-        sys.stderr.write(cw(_('%s: warning: unable to locate SMF service '
-                              'key property inetd_start/exec for '
-                              'tftp/udp6 service. Using default value.\n') %
-                              os.path.basename(sys.argv[0])))
-        exec_line = ''
-    else:
-        # remove the '\n' character
-        exec_line = pipe.stdout.rstrip("\n")
-
-    return exec_line
-
-
-def set_exec_prop(val):
-    """
-    Uses svccfg to set the specified inetd_start/exec property value
-
-    Args
-        prop_name - property to set
-        prop_value - value to set property to
-
-    Returns
-        None
-
-    Raises
-        None
-    """
-
-    cmd = ['/usr/sbin/svccfg', '-s', 'tftp/udp6', 'setprop',
-           'inetd_start/exec="%s"' % val]
-
-    try:
-        with SetUIDasEUID():
-            Popen.check_call(cmd, stdout=Popen.STORE,
-                             stderr=Popen.STORE, logger='',
-                             stderr_loglevel=logging.DEBUG)
-    except CalledProcessError:
-        sys.stderr.write(cw(_('%(path)s: warning: Unable to set the value of '
-                              'key property inetd/start_exec for '
-                              'tftp/udp6 service to "%(value)s"\n' %
-                              {'path': os.path.basename(sys.argv[0]),
-                               'value': val})))
-
-    cmd = ['/usr/sbin/svcadm', 'refresh', 'tftp/udp6:default']
-
-    try:
-        with SetUIDasEUID():
-            Popen.check_call(cmd, stdout=Popen.STORE,
-                             stderr=Popen.STORE, logger='',
-                             stderr_loglevel=logging.DEBUG)
-    except CalledProcessError:
-        sys.stderr.write(cw(_('%s: warning: Unable to refresh the service: '
-                              'tftp/udp6:default\nThis needs to be done '
-                              'before attempting to do client installs\n' %
-                              os.path.basename(sys.argv[0]))))
-
-
-def del_prop_group(ai_service, dry_run):
-    """
-    Remove a property group from the AI service
-    Input:
-        ai_service - An AI service property group name
-    Return:
-        None
-
-    """
-    pg_name = 'AI' + ai_service
-    print _('Delete SMF property %s') % pg_name
-    if not dry_run:
-        cmd = ['/usr/sbin/svccfg', '-s', AI_SVC_FMRI, 'delprop', pg_name]
-
-        try:
-            with SetUIDasEUID():
-                Popen.check_call(cmd, stdout=Popen.STORE,
-                                 stderr=Popen.STORE, logger='',
-                                 stderr_loglevel=logging.DEBUG)
-        except CalledProcessError as err:
-            sys.stderr.write(cw(_('%(cmd)s failed with: %(error)s')
-                                  % {'cmd': cmd, 'error': err.popen.stderr}))
-            sys.stderr.write(cw(_('%(cmd)s: warning: Unable to delete the '
-                                  'property group: %(property)s' %
-                                  {'cmd': cmd, 'property': pg_name})))
-
-
-def remove_boot_archive_from_vfstab(ai_service, service):
-    """
-    Remove boot_archive file system from vfstab
-    """
-
-    # check that we have a valid tftpboot directory and set base_dir to it
-    base_dir = find_tftp_root()
-    if not base_dir:
-        sys.stderr.write(cw(_("Unable to remove the vfstab entries for "
-                              "install boot archives without a valid "
-                              "tftp root directory.\n")))
-        return
-
-    # if we have a boot_file use it instead of serviceName for paths
-    service_name = service.get('boot_file', ai_service)
-    try:
-        service_name = service['boot_file']
-    except KeyError:
-        service_name = ai_service
-
-    # Use GRUB menu to check for boot_archive, see that it exists
-    grub_menu_prefix = "menu.lst."
-    grub_menu = grub_menu_prefix + service_name
-    if not os.path.exists(os.path.join(
-                          base_dir, grub_menu)):
-        sys.stderr.write(cw(_("Unable to find GRUB menu at %s, and thus "
-                              "unable to find boot archive.\n") % grub_menu))
-    else:
-        # check the menu.lst file for the boot archive(s) in use
-        menu_lst = GrubMenu(file_name=os.path.join(base_dir, grub_menu))
-
-        # iterate over both module and module$ of the service's grub menus
-        # looking for boot_archive
-        for boot_archive in set(menu_lst[entry].get('module') or
-                                menu_lst[entry].get('module$') for
-                                entry in menu_lst.entries):
-
-            # boot_archive will be relative to /tftpboot so will appear to
-            # be an absolute path (i.e. will have a leading slash) and will
-            # point to the RAM disk
-            boot_archive = base_dir + "/" + \
-                           boot_archive.split(os.path.sep, 2)[1]
-
-            # see if it is a mount point
-            # os.path.ismount() doesn't work for a lofs FS so use
-            # /etc/mnttab instead
-            if boot_archive in com.MNTTab().fields.MOUNT_POINT:
-                # unmount filesystem
-                try:
-                    cmd = ["/usr/sbin/umount", boot_archive]
-                    with SetUIDasEUID():
-                        Popen.check_call(cmd, stdout=Popen.STORE,
-                                         stderr=Popen.STORE, logger='',
-                                         stderr_loglevel=logging.DEBUG,
-                                         check_result=Popen.SUCCESS)
-
-                # if run_cmd errors out we should continue
-                except CalledProcessError as err:
-                    sys.stderr.write(str(err) + "\n")
-                    sys.stderr.write(cw(_("Unable to unmount boot archive %s "
-                                          "for service\n") %
-                                          os.path.join(base_dir,
-                                          boot_archive)))
-
-            # boot archive directory not a mountpoint
-            else:
-                sys.stderr.write(cw(_("Boot archive %s for service is not a "
-                                      "mountpoint.\n") % os.path.join(base_dir,
-                                      boot_archive)))
-
-            try:
-                vfstab_obj = VFSTab(mode="r+")
-            except IOError as err:
-                sys.stderr.write(str(err) + "\n")
-                return
-            # look for filesystem in /etc/vfstab
-            try:
-                # calculate index for mount point
-                idx = vfstab_obj.fields.MOUNT_POINT.index(boot_archive)
-                try:
-                    # remove line containing boot archive (updates /etc/vfstab)
-                    del(vfstab_obj.fields.MOUNT_POINT[idx])
-                except IOError as err:
-                    sys.stderr.write(str(err) + "\n")
-            # boot archive was not found in /etc/vfstab
-            except (ValueError, IndexError):
-                sys.stderr.write(cw(_("Boot archive (%(archive)s) for service "
-                                      "%(service)s not in vfstab.\n")
-                                      % {'archive': boot_archive,
-                                         'service': service_name}))
-
-
-def generate_new_service_name(services, ai_service):
-    """
-    Generate a unique service name based on ai_service
-    """
-    new_service_name = ai_service
-    iter = 1
-    while new_service_name in services:
-        new_service_name = ai_service + '_%s' % iter
-        iter = iter + 1
-
-    return new_service_name
-
-
-def find_sparc_clients(lservices, sname=None):
-    """
-    find_sparc_clients() searches /etc/netboot for all clients and
-    returns a dictionary that contains a list of dictionaries.
-
-    The service name is the key for the main dictionary and the
-    client and image path are members of the subdictionary.  The
-    dictionary will look something like:
-
-        {
-            'service1': [
-                        { 'ipath':<path1>, 'client':<client1> },
-                        ....
-                        ],
-            ....
-        }
-
-    The information is spread out across a couple of different
-    files within the server.  The Client is embedded within a
-    directory path (/etc/netboot/<IP Network>/01<client>).  The
-    Image Path is in the wanboot.conf file pointed to by the
-    Client path.  The Service Name is contained in the install.conf
-    file pointed to by the Image Path.
-
-    We first get the IP addresses for the host.  Then while only
-    using IPv4 address we iterate over the client directories within
-    /etc/netboot/<IP Network> to get the Image Path and Service Name.
-    The client and image path are then added to the named service
-    dictionary list.
-
-    Args
-        lservices =
-        sname =
-
-    Returns
-        dictionary of a list of dictionaries
-
-    Raises
-        None
-    """
-
-    INSTALLCONF = 'install.conf'
-    WANBOOTCONF = 'wanboot.conf'
-
-    def get_image_path(lpath):
-        """
-        gets the Image Path for the client pointed to by lpath.
-        The Image Path on sparc is stored in the wanboot.conf file.
-
-        Args
-            lpath = path for directory which contains wanboot.conf file
-
-        Returns
-            image path for client
-
-        Raises
-            None
-        """
-        try:
-            confpath = os.path.join(lpath, WANBOOTCONF)
-            sinfo = os.stat(confpath)
-            with open(confpath) as filepointer:
-                fstr = filepointer.read(sinfo.st_size)
-        except (OSError, IOError):
-            sys.stderr.write(cw(_("Error: while accessing wanboot.conf file "
-                                  "(%s/wanboot.conf)\n" % lpath)))
-            return
-
-        start = fstr.find('boot_file=') + len('boot_file=')
-        end = fstr[start:].find('/platform')
-
-        return fstr[start:start + end]
-
-    def get_service_name(lpath):
-        """
-        gets the Service Name for the client from the lpath pointed
-        to by the wanboot.conf file.  The Service Name is in the
-        Image Path install.conf file.
-
-        Args
-            lpath = path to directory containing install.conf file
-
-        Returns
-            install service for client
-
-        Raises
-            None
-        """
-        try:
-            confpath = os.path.join(lpath, INSTALLCONF)
-            sinfo = os.stat(confpath)
-            with open(confpath) as filepointer:
-                fstr = filepointer.read(sinfo.st_size)
-        except (OSError, IOError):
-            sys.stderr.write(_("Error: while accessing install.conf file\n"))
-            return
-
-        start = fstr.find('install_service=') + len('install_service=')
-        end = fstr[start:].find('\n')
-
-        return fstr[start:start + end]
-
-    # start of find_sparc_clients
-    if not os.path.exists(NETBOOT):
-        return dict()
-
-    sdict = dict()
-    hostname = socket.getfqdn()
-    ipaddr = socket.gethostbyname(hostname)
-    # get the Network IP path for the host
-    end = ipaddr.rfind('.')
-    compatibility_path = os.path.join(NETBOOT, ipaddr[:end] + '.0')
-
-    for path in [compatibility_path, NETBOOT]:
-        if not os.path.exists(path) or not os.path.isdir(path):
-            continue
-        for clientdir in os.listdir(path):
-            if not clientdir.startswith('01'):
-                continue
-            # strip off the 01 in the clientdir
-            client = AIdb.formatValue('mac', clientdir[2:])
-
-            # get the Image from the clientdir/wanboot.conf file
-            ipath = get_image_path(os.path.join(path, clientdir))
-
-            if not ipath or not os.path.exists(ipath):
-                continue
-
-            # get the service name from the ipath/install.conf file
-            servicename = get_service_name(ipath)
-
-            # Store the client and image path in the dictionary under the
-            # service name.  First, check to see if the service name key
-            # already exists.  If the service name key does not already exist
-            # then add it to the dictionary.  If the service name key does
-            # exist then extend the list and update the dictionary.
-            if servicename in lservices and \
-              (not sname or servicename == sname):
-                tdict = {'client': client, 'ipath': [ipath], 'arch': 'sparc'}
-                if servicename in sdict:  # existing service name key
-                    slist = sdict[servicename]
-                    slist.extend([tdict])
-                    sdict[servicename] = slist
-                else:  # new service name key
-                    sdict[servicename] = [tdict]
-
-    return sdict
-
-
-def find_x86_clients(lservices, sname=None):
-    """
-    find_x86_clients() searches TFTPDir for all clients and
-    returns a dictionary that contains a list of dictionaries.
-
-    The service name is the key for the main dictionary and the
-    client and image path are members of the subdictionary.  The
-    dictionary will look something like:
-
-        {
-            'service1': [
-                        { 'ipath':<path1>, 'client':<client1> },
-                        ....
-                        ],
-            ....
-        }
-
-    The information is contained within the menu.lst.01<client>
-    file.  Though not the best approach architecturally it is
-    the only method currently available.
-
-    We first get the TFTProot directory.  Then iterate over the
-    files within the TFTProot directory to get the client menu
-    which contains the Image Path and Service Name.  The client
-    and image path are then added to the named service dictionary
-    list.
-    """
-    def get_menu_info(path):
-        """
-        Reads TFTPboot/menu.list file pointed to by 'path' via
-        GrubMenu class in installadm_common.py.  Getting the
-        install path from the install_media field, service name
-        from install_service
-
-        Args
-            path = path for the menu.list.01<client> file.
-
-        Returns
-            a dictionary of services made up of a list of tuples of
-            port, install path
-
-        Raises
-            None
-        """
-        iaddr = 'install_svc_address='
-        iserv = 'install_service='
-        imedia = 'install_media=http://'
-
-        rdict = dict()
-        menu = GrubMenu(file_name=path)
-        try:
-            entries = menu.entries[0]
-        except IndexError:
-            sys.stderr.write(cw(_("Error: No entries found in %s\n") % path))
-            return ''
-        if entries:
-            if 'kernel$' in menu[entries]:
-                mdict = menu[entries]['kernel$']
-                start = mdict.find(iserv) + len(iserv)
-                service = mdict[start:].split(',')[0]
-                start = mdict.find(iaddr) + len(iaddr)
-                port = mdict[start:].split(',')[0].split(':')[-1]
-                start = mdict.find(imedia) + len(imedia)
-                pstart = mdict[start:].split(',')[0].find('/')
-                path = mdict[start:].split(',')[0][pstart:]
-                if service in rdict:
-                    tlist = rdict[service]
-                    tlist.extend([(port, path)])
-                    rdict[service] = tlist
-                else:
-                    rdict[service] = [(port, path)]
-
-        return rdict
-
-    # start of find_x86_clients
-    sdict = dict()
-    tftp_dir = find_tftp_root()
-    if tftp_dir and os.path.exists(tftp_dir):
-        for filenames in os.listdir(tftp_dir):
-            if filenames.find("menu.lst.01") >= 0:
-                path = os.path.join(tftp_dir, filenames)
-                pservices = get_menu_info(path)
-                tdict = {'arch': 'i386'}
-                for servicename in pservices:
-                    if servicename in lservices and \
-                      (not sname or servicename == sname):
-                        client = AIdb.formatValue('mac', filenames[11:])
-                        tdict['client'] = client
-                        # create a list of image_paths for the client
-                        ipath = list()
-                        for tup in pservices[servicename]:
-                            ipath.insert(0, tup[1])
-                        tdict['ipath'] = ipath
-                        if servicename in sdict:  # existing service name
-                            slist = sdict[servicename]
-                            slist.extend([tdict])
-                            sdict[servicename] = slist
-                        else:  # new service name key
-                            sdict[servicename] = [tdict]
-    return sdict
-
-
-def get_local_services(linst, sname=None):
-    """
-    Iterates over the local services on a host creating a dictionary
-    with the service name as the key and status, path, architecture
-    and port as the value.  If name is not None then it ensures that
-    only the named services is retrieved.
-
-    Args
-        linst = smf.AISCF()
-        name = service name
-
-    Returns
-        a service dictionary made up of a list of dictionary of services.
-
-        {
-            service1:
-              [
-                { 'status':on1, 'path':path1, 'arch':arch1, 'port':port1 },
-                ...
-              ],
-            ...
-        }
-
-    Raises
-        None
-    """
-    sdict = dict()
-    for akey in linst.services:
-        serv = smf.AIservice(linst, akey)
-        # Grab the keys since serv is not a dictionary
-        service_keys = serv.keys()
-        # ensure that the current service has the keys we need.
-        # if not then report the error and exit.
-        if not (PROP_SERVICE_NAME in service_keys and
-                PROP_STATUS in service_keys and
-                PROP_IMAGE_PATH in service_keys and
-                PROP_TXT_RECORD in service_keys):
-            sys.stderr.write(cw(_('Error: SMF service: %s key property does '
-                                  'not exist\n') % linst))
-            sys.exit(1)
-
-        servicename = serv[PROP_SERVICE_NAME]
-        info = dict()
-        # if a service name is passed in then
-        # ensure it matches the current name
-        if not sname or sname == servicename:
-            info['status'] = serv[PROP_STATUS]
-            info['path'] = serv[PROP_IMAGE_PATH]
-            info['txt'] = serv[PROP_TXT_RECORD]
-            info['port'] = serv[PROP_TXT_RECORD].split(':')[-1]
-            info['arch'] = which_arch(info['path'])
-
-            # try to add the optional elements
-            if PROP_BOOT_FILE in service_keys:
-                info['boot_file'] = serv[PROP_BOOT_FILE]
-
-            if PROP_DEF_MANIFEST in service_keys:
-                info['default-manifest'] = serv[PROP_DEF_MANIFEST]
-
-            if PROP_VERSION in service_keys:
-                info['version'] = serv[PROP_VERSION]
-
-            if servicename in sdict:
-                slist = sdict[servicename]
-                slist.extend([info])
-                sdict[servicename] = slist
-            else:
-                sdict[servicename] = [info]
-
-    return sdict
-
-
-def get_clients(lservices, sname=None):
-    """
-    Gets the clients (x86 and sparc) for the services of a local host.
-    If a service name is passed in the only get the clients for the
-    named service on the local host.
-
-    Args
-        lservices = services on a host
-        sname = a named service
-
-    Returns
-        a service dictionary of both x86 and sparc clients
-
-            {
-                'service1': [
-                              {
-                                'ipath':[path1, path2],
-                                'client':client1,
-                                'arch':arch1
-                              },
-                            ....
-                            ],
-                ....
-            }
-
-    Raises
-        None
-    """
-    def merge_dictionaries(first, second):
-        """
-        Merges two similar dictionaries and returns the resulting
-        dictionary.  The dictionaries are the same as in get_clients()
-        description.
-        """
-        rdict = dict()
-        rdict.update(first)
-        rdict.update(second)
-        for key in set(first.keys()) & set(second.keys()):
-            rdict[key] = first[key] + second[key]
-
-        return rdict
-
-    sdict = find_sparc_clients(lservices, sname=sname)
-    xdict = find_x86_clients(lservices, sname=sname)
-    botharchs = dict()
-    if sdict and xdict:
-        botharchs = merge_dictionaries(sdict, xdict)
-    elif sdict:
-        botharchs = sdict
-    elif xdict:
-        botharchs = xdict
-
-    return botharchs
-
-
-def build_option_list():
-    """
-    Parse command line arguments to ISIM conversion
-    """
-    desc = _("Utility for converting Solaris 11 Express services and clients "
-             "to Solaris 11 FCS format")
-    usage = _("usage: %prog [-h][--version]\n"
-              "       %prog [-n][--dryrun] [-d][--debug] [-p][--dhcp]\n")
-
-    parser = optparse.OptionParser(version=VERSION, description=desc,
-        usage=usage)
-
-    parser.add_option("-n", "--dryrun", dest="dryrun", default=False,
-                      action="store_true", help=_(
-                      "dry run mode. No changes made to AI configuration"))
-
-    parser.add_option("-d", "--debug", dest="debug", default=0,
-                      action="store_true", help=optparse.SUPPRESS_HELP)
-
-    parser.add_option("-p", "--dhcp", dest="dhcp", default=False,
-                      action="store_true",
-                      help=_("DHCP mode.  Create an ISC DHCP configuration "
-                             "from the current Solaris DHCP configuration"))
-
-    return parser
-
-
-def create_config(services, dryrun, ai_service):
-    """
-    For the specified service create the .config file in
-    /var/ai/service/<service name> with service_name, txt_record, and
-    image_path from the SMF properties
-    """
-
-    logging.debug("create_config\n")
-
-    config_path = os.path.join(SERVICE_DIR, ai_service, '.config')
-
-    # Create .config file in the service directory
-    config_list = list()
-    config_list.append("[service]")
-    for service in services[ai_service]:
-        config_list.append("status = %s" % service['status'])
-        config_list.append("service_name = %s" % ai_service)
-        config_list.append("version = 2")
-        config_list.append("txt_record = %s" % service['txt'])
-        config_list.append("image_path = %s" % service['path'])
-        if service['arch'] == 'i386':
-            config_list.append("global_menu = True")
-        else:
-            config_list.append("global_menu = False")
-        if 'default-manifest' in service:
-            config_list.append("default-manifest = %s" %
-                               service['default-manifest'])
-
-    # Write the .config file output to the log
-    print _("Create configuration file for service: %s") % ai_service
-    for item in config_list:
-        print "    ", item
-
-    if not dryrun:
-        config_file = open(config_path, "w")
-        for item in config_list:
-            config_file.write(item + "\n")
-        config_file.close()
-
-    # Check to see if installadm-convert has been run before by
-    # testing for the existence of install.conf.bak and install.conf
-    # existing as a sym link.  If this is the case then remove the
-    # link and copy install.conf.bak to install.conf.  This will restore
-    # the file to its original contents and allow the convert to run
-    # successfully.
-    install_conf = os.path.join(service['path'], 'install.conf')
-    install_conf_bak = os.path.join(service['path'], 'install.conf.bak')
-    if not dryrun:
-        if os.path.islink(install_conf):
-            link_path = os.readlink(install_conf)
-            if not os.path.exists(link_path) and \
-                os.path.exists(install_conf_bak):
-                os.remove(install_conf)
-                shutil.copyfile(install_conf_bak, install_conf)
-        # Create the install.conf.bak file if it doesn't already exist
-        elif os.path.exists(install_conf) and not \
-            os.path.exists(install_conf_bak):
-            shutil.copyfile(install_conf, install_conf_bak)
-
-    # If install.conf exists in the image directory then move it to
-    # the service directory, rename it system.conf, and then create
-    # a symlink back to install.conf
-    if os.path.exists(install_conf):
-        system_conf = os.path.join(SERVICE_DIR, ai_service, 'system.conf')
-        print _(("Move %(conf)s to %(sysconf)s") % {'conf': install_conf,
-                 'sysconf': system_conf})
-        if not dryrun:
-            shutil.move(install_conf, system_conf)
-            os.symlink(system_conf, install_conf)
-
-
-def move_service_directory(services, dryrun, ai_service):
-    """
-    Move the directory associated with the service from
-    /var/ai/<service name> to /var/ai/service/<service name>.
-    If /var/ai/<service name> does not exist then check for
-    /var/ai/46xxx where 46xxx is the value specified in the txt_record
-
-    - /var/ai/<service name>
-        Move the directory
-    - /var/ai/46xxx
-        Move the directory to /var/ai/service/<service name>
-        Create a relative sym link from dir to /var/ai/service/46xxx
-    Move the netboot files to the service directory
-    - sparc
-        move the wanboot.conf file from /etc/netboot/<service name> to
-        /var/ai/service/<service name> and remove the symbolic link to
-        /etc/netboot/wanboot.conf if it exists.
-    - x86
-        translate the menu.lst from I86PC* boot format to service name format
-        eg. /I86PC.Solaris-2 --> /solaris-i386-170
-        move menu.lst file from /tftpboot/menu.lst.<service name> to
-        /var/ai/service/<service name>/menu.lst
-        clean up pxegrub files
-    """
-
-    logging.debug("move_service_directory")
-
-    # Ensure that the image path directory exists
-    for service in services[ai_service]:
-        print _("Convert service: %s") % ai_service
-        ipath = service['path']
-        if not (os.path.exists(ipath) and os.path.isdir(ipath)):
-            raise ServiceConversionError(
-                _("Error: Image path directory does not exist: %s" % ipath))
-        # Make sure that the image appears valid (has a solaris.zlib file)
-        if not os.path.exists(os.path.join(ipath, "solaris.zlib")):
-            raise ServiceConversionError(
-                cw(_('Error: Image path directory does not'
-                     ' contain a valid image: %s' % ipath)))
-
-    sdpath = os.path.join('/var/ai', ai_service)
-    new_service_path = os.path.join(SERVICE_DIR, ai_service)
-    if not dryrun and not os.path.exists(SERVICE_DIR):
-        os.mkdir(SERVICE_DIR)
-    elif not os.path.isdir(SERVICE_DIR):
-        raise ServiceConversionError(
-            cw(_("Error: %s exists and is not a directory" % SERVICE_DIR)))
-
-    if os.path.exists(sdpath) and os.path.isdir(sdpath):
-        print _(("    Move %(path)s to %(newpath)s") % {'path': sdpath,
-                'newpath': new_service_path})
-        # Move the service path to the new service location
-        if not dryrun:
-            try:
-                shutil.move(sdpath, new_service_path)
-            except OSError as err:
-                raise ServiceConversionError(str(err))
-    else:
-        # grab the port number for the service and see if /var/ai/<port>
-        # exists.  If so move it to /var/ai/service/<service name>
-        for service in services[ai_service]:
-
-            portpath = os.path.join('/var/ai', service['port'])
-            if os.path.exists(portpath) and os.path.isdir(portpath):
-                # /var/ai/<port> exists so move it
-                print _(("    Move %(path)s to %(newpath)s")
-                         % {'path': portpath, 'newpath': new_service_path})
-                print _("    Create link from %(newpath)s to %(path)s") % \
-                        {'newpath': new_service_path, 'path':
-                        os.path.join(SERVICE_DIR, service['port'])}
-                if not dryrun:
-                    try:
-                        shutil.move(portpath, new_service_path)
-                        # Create a relative symlink from /var/ai/service/
-                        # <service name> to /var/ai/service/<port>
-                        os.chdir(SERVICE_DIR)
-                        os.symlink(os.path.join('./', ai_service),
-                                   os.path.join(SERVICE_DIR, service['port']))
-                    except OSError as err:
-                        raise ServiceConversionError(str(err))
-
-    if service['arch'] == 'i386':
-        print _("    Remove vfstab entry for %s if it exists" % ai_service)
-        if not dryrun:
-            try:
-                remove_boot_archive_from_vfstab(ai_service, service)
-            except OSError as err:
-                raise ServiceConversionError(str(err))
-
-        grub_path = TFTPBOOT + '/menu.lst.' + ai_service
-        if os.path.exists(grub_path) and os.path.isfile(grub_path):
-            new_grub = os.path.join(new_service_path, 'menu.lst')
-            print _(("    Move %(path)s to %(newpath)s") % {'path': grub_path,
-                     'newpath': new_grub})
-
-            if not dryrun:
-                try:
-                    shutil.move(grub_path, new_grub)
-                except OSError as err:
-                    raise ServiceConversionError(str(err))
-
-                try:
-                    print _("     Update menu.lst format")
-                    svcgrub = grubcfg(ai_service, path=ipath,
-                                      config_dir=new_service_path)
-                    old_mountpt = svcgrub.get_mountpt()
-                    svcgrub.update_svcname(old_mountpt, ai_service)
-                except IOError as err:
-                    raise ServiceConversionError(str(err))
-
-        else:
-            raise ServiceConversionError(_("Error: menu.lst for service: %s"
-                                         "was not found" % ai_service))
-
-        # if symlink from /tftpboot/<service name> to
-        # /tftpboot/PXEGRUB.* exists then delete it
-        pxegrub_path = os.path.join(TFTPBOOT, ai_service)
-        if os.path.islink(pxegrub_path):
-            print _("    Remove link %s") % pxegrub_path
-            if not dryrun:
-                try:
-                    os.remove(pxegrub_path)
-                # If error encountered continue with the conversion
-                except OSError as err:
-                    sys.stderr.write(str(err) + "\n")
-
-    else:
-        netboot_path = os.path.join(NETBOOT, ai_service, 'wanboot.conf')
-        if os.path.exists(netboot_path) and os.path.isfile(netboot_path):
-            new_netboot = os.path.join(ipath, 'wanboot.conf')
-            print _(("    Move %(path)s to %(newpath)s")
-                     % {'path': netboot_path, 'newpath': new_netboot})
-            if not dryrun:
-                try:
-                    shutil.move(netboot_path, new_netboot)
-                except OSError as err:
-                    raise ServiceConversionError(str(err))
-
-            # if symlink from /etc/netboot/wanboot.conf to
-            # /etc/netboot/<service>/wanboot.conf then delete it
-            netboot_lnk = os.path.join(NETBOOT, 'wanboot.conf')
-            if os.path.islink(netboot_lnk):
-                print _("    Remove link %s") % netboot_lnk
-                if not dryrun:
-                    try:
-                        os.remove(netboot_lnk)
-                    # If error encountered continue with the conversion
-                    except OSError as err:
-                        sys.stderr.write(str(err) + "\n")
-        else:
-            raise ServiceConversionError(cw(_("Warning: wanboot.conf file for "
-                                              "service: %s was not found" %
-                                              ai_service)))
-
-
-def get_bootargs(menupath):
-    """
-    Get the bootargs from the menu.lst that were specified by the user with
-    the '-b' option to installadm create-client
-    """
-    generated_bootargs = ['install', 'install_media', 'install_service',
-                          'install_svc_address']
-    bootargs = ''
-    for line in fileinput.input(menupath):
-        parts = line.partition(' -B')
-        if parts[1]:
-            ending = parts[2]
-            # Parse out the bootargs that are generated by installadm
-            for keyval in ending.split(','):
-                boot_pair = keyval.split('=')
-                if boot_pair[0].strip() in generated_bootargs:
-                    continue
-                bootargs += keyval.strip() + ','
-
-    return bootargs.rstrip(',')
-
-
-def convert_client(clients, dry_run, ai_service):
-    """
-    Remove the clients from the specified server and then recreate them.
-    x86 - save the bootargs from menu.lst
-        Remove /tftpboot/<clientid> and /tfpboot/menu.lst.<clientid>
-    sparc - remove the /etc/netboot/<clientid> directory
-
-    Recreate each client using 'installadm create-client'
-    """
-    logging.debug("convert_client")
-    print _("Client conversion:")
-
-    for service in clients[ai_service]:
-        # Strip : from client
-        print _("    client: %s") % service['client']
-        clientid = '01' + service['client'].replace(':', '')
-        if service['arch'] == 'i386':
-            client_menu_lst = os.path.join(TFTPBOOT, 'menu.lst.' + clientid)
-            boot_args = get_bootargs(client_menu_lst)
-
-            # remove the client specific entries in tftpboot
-            print _("    Remove %s") % client_menu_lst
-            print _(("    Remove %(tftpboot)s/%(clientid)s")
-                    % {'tftpboot': TFTPBOOT, 'clientid': clientid})
-            if not dry_run:
-                try:
-                    # /tftpboot/<clientid> is a symlink - remove it
-                    os.remove(os.path.join(TFTPBOOT, clientid))
-                    os.remove(client_menu_lst)
-                # If error encountered continue with the conversion
-                except OSError as err:
-                    sys.stderr.write(str(err) + "\n")
-
-        else:
-            boot_args = None
-            # remove the /etc/netboot client directory
-            netboot_dir = os.path.join(NETBOOT, clientid)
-            print _("    Remove %s") % netboot_dir
-            if not dry_run:
-                try:
-                    shutil.rmtree(netboot_dir)
-                # If error encountered continue with the conversion
-                except OSError as err:
-                    sys.stderr.write(str(err) + "\n")
-
-        print _("    Recreate client with ")
-        print _("        arch: %s") % service['arch']
-        print _("        service: %s") % ai_service
-        print _("        client: %s") % service['client']
-        if service['arch'] == 'i386':
-            print _("        boot_args: %s") % boot_args
-        if not dry_run:
-            mac_address = service['client'].replace(':', '')
-
-            # wrap the whole create_client to catch exceptions as they
-            # should not be thrown anywhere
-            try:
-                # recreate the client
-                create_client.create_new_client(service['arch'],
-                   AIService(ai_service), mac_address, boot_args)
-            except (OSError, aismf.ServicesError, config.ServiceCfgError,
-                    svc.MountError) as err:
-                sys.stderr.write("Client conversion failed:\n")
-                sys.stderr.write(str(err) + "\n")
-
-
-def copy_netboot_files(dry_run):
-    """
-    Move all of the non-AI files from /tftpboot to /etc/netboot
-    Make /tftpboot a symlink to /etc/netboot
-    Convert inetd_start/exec property of tftp/udp6 to /etc/netboot
-    """
-    logging.debug("copy_netboot_files")
-    copy_error = False
-    print _("Clean up pxegrub files from /tftpboot")
-    if not dry_run:
-        try:
-            for rm_file in glob.glob(os.path.join(TFTPBOOT, "rm.*")):
-                os.remove(rm_file)
-            for pxegrub_file in glob.glob(os.path.join(TFTPBOOT, "pxegrub.*")):
-                os.remove(pxegrub_file)
-            for i86pc_dir in glob.glob(os.path.join(TFTPBOOT,
-                                                    "I86PC.Solaris-*")):
-                shutil.rmtree(i86pc_dir, True)
-        except OSError as err:
-            sys.stderr.write(str(err) + "\n")
-            copy_error = True
-
-    print _("Copy non-AI files from /tftpboot to /etc/netboot")
-    print _("Change inetd_start/exec property in "
-            "service tftp/udp6 to /etc/netboot")
-    if not dry_run:
-        for netfile in os.listdir(TFTPBOOT):
-            print _("  Move %s from /tftpboot to /etc/netboot" % netfile)
-            try:
-                shutil.move(os.path.join(TFTPBOOT, netfile),
-                    os.path.join(NETBOOT, netfile))
-            except OSError as err:
-                sys.stderr.write(str(err) + "\n")
-                copy_error = True
-
-        # Remove /tftpboot and recreate it as a link to /etc/netboot
-        if not copy_error:
-            try:
-                os.rmdir(TFTPBOOT)
-                os.symlink(NETBOOT, TFTPBOOT)
-            except OSError as err:
-                sys.stderr.write(str(err) + "\n")
-                copy_error = True
-
-        if copy_error:
-            sys.stderr.write(NETBOOT_ERR)
-
-        inet_exec = get_exec_prop()
-        if inet_exec != '':
-            set_exec_prop(inet_exec.replace(TFTPBOOT, NETBOOT))
-
-
-def upgrade_svc_vers_0_1(services, dry_run, ai_service):
-    """
-    Re-register the default manifest for the input service managed in
-    pre Derived Manifest services to be compatible with Derived Manifest
-    services
-    """
-    logging.debug("upgrade_svc_vers_0_1")
-
-    # if running in dry run mode then the service hasn't been moved to
-    # /var/ai/service.  Look for the manifest in /var/ai instead.
-    if dry_run:
-        sdpath = os.path.join('/var/ai', ai_service)
-    else:
-        sdpath = os.path.join('/var/ai/service', ai_service)
-
-    print cw(_("Upgrade existing default manifests for %s to version 1") %
-            ai_service)
-
-    if os.path.exists(sdpath) and os.path.isdir(sdpath):
-        manifest_dir = sdpath
-    else:
-        # grab the port number for the service and see if /var/ai/<port>
-        # exists.  If so use it as the manifest directory
-        for service in services[ai_service]:
-
-            # if running in dry run mode then the service hasn't been moved to
-            # /var/ai/service/<port>.  Look for the manifest in
-            # /var/ai/<port> instead.
-            if dry_run:
-                portpath = os.path.join('/var/ai', service['port'])
-            else:
-                portpath = os.path.join('/var/ai/service', service['port'])
-            if os.path.exists(portpath) and os.path.isdir(portpath):
-                # The port path exists so use it as the manifest directory
-                manifest_dir = portpath
-                break
-            else:
-                raise ServiceConversionError(cw(_('Unable to find the service '
-                                                  ' directory for %s' %
-                                                  ai_service)))
-
-    default_file = os.path.join(manifest_dir, 'AI_data', 'default.xml')
-    print _("Search for default manifest %s") % default_file
-    if os.path.isfile(default_file):
-        # Upgrade the service
-        print _("Default manifest found - upgrade service")
-        if not dry_run:
-            # Create a temp file and move the manifest into it
-            temp_fd, temp_nm = tempfile.mkstemp()
-            shutil.move(default_file, temp_nm)
-            # call create-manifest with the default
-            cmd = ['/usr/sbin/installadm', 'create-manifest', '-n',
-                ai_service, '-d', '-f', temp_nm]
-
-            try:
-                Popen.check_call(cmd, stdout=Popen.STORE,
-                                 stderr=Popen.STORE, logger='',
-                                 stderr_loglevel=logging.DEBUG)
-            except CalledProcessError:
-                # Move the default manifest back into place
-                shutil.move(temp_nm, default_file)
-                sys.stderr.write(cw(_('Warning: Unable to add the default '
-                                      'manifest: %(file)s for service: '
-                                      '%(service)s\n' % {'file': default_file,
-                                      'service': ai_service})))
-                return
-
-            # delete the temporary file
-            try:
-                os.remove(temp_nm)
-            except OSError:
-                pass
-    else:
-        print cw(_(' No default manifest found for service %s' % ai_service))
-
-
-def upgrade_svc_vers_1_2(services, dry_run, ai_service):
-    """
-    Make the changes to the AI service necessary for ISIM
-    """
-
-    # Remove all of the AI property groups
-    del_prop_group(ai_service, dry_run)
-
-    move_service_directory(services, dry_run, ai_service)
-    create_config(services, dry_run, ai_service)
-
-
-def copy_image_path(service_name, service_info, dry_run):
-    """
-    Copy specified image to a unique location
-    """
-
-    new_image_path = service_info['path']
-    iter = 1
-    while True:
-        new_image_path = service_info['path'] + '_%s' % iter
-        if not os.path.exists(new_image_path):
-            break
-        iter += 1
-
-    print cw(_("Create a unique instance of image path for %(service)s "
-               "at %(imagepath)s") % {'service': service_name,
-               'imagepath': new_image_path})
-    if not dry_run:
-        # copy the image directory
-        shutil.copytree(service_info['path'], new_image_path, symlinks=True)
-        # update the service information to reflect the new image location
-        service_info['path'] = new_image_path
-
-
-def convert_image_paths(services, dry_run):
-    """
-    If multiple services use the same image path make copies for each
-    """
-
-    print 'Ensure that all image paths are unique'
-    image = dict()
-    for service_name in services:
-        for service in services[service_name]:
-            # services cannot share images
-            ipath = service['path']
-            if ipath in image:
-                # only copy if the image path exists
-                # don't throw an exception - this will get caught later
-                if not (os.path.exists(ipath) and os.path.isdir(ipath)):
-                    continue
-                copy_image_path(service_name, service, dry_run)
-
-            image[service['path']] = service_name
-
-
-def is_solaris_dhcp_host():
-    """
-    Is Solaris DHCP configured and online on this system
-    """
-
-    # Verify that the service is online
-    cmd = ['/bin/svcs', '-H', '-o', 'STATE', SOLARIS_DHCP_SVC_FMRI]
-    try:
-        pipe = Popen.check_call(cmd, stdout=Popen.STORE, stderr=Popen.DEVNULL)
-    except CalledProcessError:
-        return False
-
-    if pipe.stdout.strip() == 'online':
-        return True
-
-    return False
-
-
-def create_isc_dhcp_configuration(dhcp_config):
-    """
-    Create the configuration file that would convert the existing SUN
-    DHCP configuration to the equivalent ISC DHCP configuration
-    """
-    try:
-        subnets = SUNDHCPData.subnets()
-        macros = SUNDHCPData.macros()
-    except SUNDHCPData.DHCPError as err:
-        sys.stderr.write(str(err) + "\n")
-        return
-
-    # add the subnet ranges and the DHCP server IP to each of the subnet
-    # macros so that all of the necessary to create the subnet stanza
-    # will be available/
-    for subnet in subnets:
-        if subnet in macros:
-            macros[subnet].update({'ip_ranges': subnets[subnet]})
-            macros[subnet].update({'nextserver':
-                                   subnets[subnet][0]['nextserver']})
-
-    # Attempt to open the dhcp configuration file.  If unsuccessful then
-    # flag an error and return
-    try:
-        config_file = open(dhcp_config, "w")
-    except IOError as err:
-        sys.stderr.write(str(err) + "\n")
-        return
-
-    # Build each of the ISC DHCP stanzas
-    print cw(_("Build ISC DHCP Configuration File: %s\n" % dhcp_config))
-
-    cfgfile_base_printed = False
-    cfg_error_found = False
-    for mname in macros:
-        if 'broadcast' in macros[mname]:
-            # Build the Configuration file base
-            # Grab one of the macros and use the domain name and name servers
-            # to populate the global definitions
-            if not cfgfile_base_printed:
-                try:
-                    config_file.write(dhcp.CFGFILE_BASE % macros[mname])
-                except KeyError:
-                    if not cfg_error_found:
-                        print DHCP_ERR
-                        cfg_error_found = True
-                    # Add the macro without substitutions to the file
-                    config_file.write(dhcp.CFGFILE_BASE)
-
-                cfgfile_base_printed = True
-
-            # Build the subnet stanza
-            try:
-                config_file.write(dhcp.CFGFILE_SUBNET_HEADER_STRING %
-                              macros[mname] + "\n")
-            except KeyError:
-                    if not cfg_error_found:
-                        print DHCP_ERR
-                        cfg_error_found = True
-                    # Add the macro without substitutions to the file
-                    config_file.write(dhcp.CFGFILE_SUBNET_HEADER_STRING)
-
-            # build a range string for each subnet range found
-            for subnet_range in macros[mname]['ip_ranges']:
-                try:
-                    config_file.write(dhcp.CFGFILE_SUBNET_RANGE_STRING %
-                                      subnet_range)
-                except KeyError:
-                    if not cfg_error_found:
-                        print DHCP_ERR
-                        cfg_error_found = True
-                    # Add the macro without substitutions to the file
-                    config_file.write(dhcp.CFGFILE_SUBNET_RANGE_STRING)
-
-            try:
-                config_file.write(dhcp.CFGFILE_SUBNET_FOOTER_STRING %
-                              macros[mname] + "\n")
-            except KeyError:
-                if not cfg_error_found:
-                    print DHCP_ERR
-                    cfg_error_found = True
-                # Add the macro without substitutions to the file
-                config_file.write(dhcp.CFGFILE_SUBNET_FOOTER_STRING)
-
-            # Save off the server IP value for use in the Sparc class stanza
-            server_ip = macros[mname]['nextserver']
-
-    # Build the architecture class stanzas
-    config_file.write(dhcp.CFGFILE_PXE_CLASS_STANZA % {'bootfile':
-                      '<x86 service name>/boot/grub/pxegrub'} + "\n")
-    config_file.write(_("NOTE: Specify the desired x86 AI service name "
-                      "that will be used as the default boot service" + "\n"))
-    sparc_server_string = 'http://%s:5555/cgi-bin/wanboot-cgi' % server_ip
-    config_file.write(dhcp.CFGFILE_SPARC_CLASS_STANZA % {'bootfile':
-                      sparc_server_string} + "\n")
-
-    # Build each of the host stanzas
-    for  mname in macros:
-        if mname.startswith('01') and 'bootfile' in macros[mname]:
-            # Generate the string equivalent of the IP address from
-            # the macro name
-            raw_mac = mname[2:]
-            macros[mname].update({'macaddr': ':'.join(a + b for a, b in
-                                  zip(raw_mac[::2], raw_mac[1::2])),
-                                  'hostname': raw_mac})
-            config_file.write(dhcp.CFGFILE_HOST_STANZA % macros[mname] + "\n")
-
-    config_file.close()
-
-
-def main():
-    """
-    Server Image Management Conversion
-    Exit Codes:
-        0 - Success
-        1 - Failure
-
-    Options:
-        -n, --dryrun - List but do not apply changes
-        -p, --dhcp - Only generate the ISC configuration file
-                     for this AI server
-
-    """
-
-    parser = build_option_list()
-
-    (options, args) = parser.parse_args()
-
-    # check for authorization and euid
-    try:
-        check_auth_and_euid(SERVICE_AUTH)
-    except UnauthorizedUserError as err:
-        raise SystemExit(err)
-
-    try:
-        inst = smf.AISCF(FMRI="system/install/server")
-    except KeyError:
-        raise SystemExit(_("Error: The system does not have the "
-                           "system/install/server SMF service"))
-
-    # Set up logging to the specified level of detail
-    if options.debug == 0:
-        options.log_level = DEFAULT_LOG_LEVEL
-    elif options.debug == 1:
-        options.log_level = DEBUG_LOG_LEVEL
-    elif options.debug >= 2:
-        options.log_level = com.XDEBUG
-
-    try:
-        com.setup_logging(options.log_level)
-    except IOError as err:
-        parser.error("%s '%s'" % (err.strerror, err.filename))
-
-    logging.debug("installadm conversion options: verbosity = %s",
-                  options.log_level)
-
-    # Create an ISC DHCP configuration from the existing Solaris DHCP if
-    # the AI server has been setup as the DHCP server
-    # If the --dhcp option was specified then exit after creating the config
-    if is_solaris_dhcp_host():
-        create_isc_dhcp_configuration(ISC_DHCP_CONFIG)
-    else:
-        print _("The Solaris DHCP service is not online - DHCP "
-                "conversion bypassed")
-
-    if options.dhcp:
-        sys.exit(EXIT_SUCCESS)
-
-    service_names = inst.services.keys()
-    services = get_local_services(inst)
-
-    if not services:
-        output =  \
-            _('There are no upgradeable services configured on this server.\n')
-        sys.stdout.write(output)
-        sys.exit(EXIT_SUCCESS)
-
-    if options.dryrun:
-        print _("Dry run mode - no changes will be made to the system")
-    else:
-        _warning = """
-        WARNING: The conversion process will make changes to the file system.
-        It is advisable to make a copy of the boot environment using beadm(1M)
-        before proceeding.
-        """
-        print cw(_(_warning))
-        prompt = _('Are you sure you want to proceed [y/N]: ')
-        if not com.ask_yes_or_no(prompt):
-            raise SystemExit(1)
-
-    print _("Disable the AI SMF service")
-    # Disable the AI service
-    if not options.dryrun:
-        aismf.disable_instance()
-
-    # if /etc/netboot doesn't already exist - create it
-    if not options.dryrun:
-        if not os.path.exists(NETBOOT):
-            os.mkdir(NETBOOT)
-
-    # If multiple services use the same image path then convert them so that
-    # they each have their own copy
-    convert_image_paths(services, options.dryrun)
-
-    # Make all of the AI service conversions
-    unconverted_services = dict()
-    for service_name in services:
-        try:
-            # Upgrade the service from 1 to 2 (ISIM)
-            upgrade_svc_vers_1_2(services, options.dryrun, service_name)
-
-            # Grab the attributes for the specified service. There
-            # is only one set of attributes associated so grab the first
-            # entry in the list.
-            service_attributes = services[service_name][0]
-
-            # if is only necessary to upgrade when the default-manifest
-            # property isn't defined for the service and the service version
-            # is not 1 or greater
-            service_version = int(service_attributes.get('version', 0))
-            if service_version >= 1:
-                continue
-
-            if not 'default-manifest' in service_attributes:
-                # Upgrade the service version from 0 to 1
-                # It is necessary to call upgrade_svc_vers_0_1 after
-                # upgrade_svc_vers_1_2 because it is necessary to upgrade the
-                # service to ISIM before calling installadm to upgrade
-                # the manifests
-                upgrade_svc_vers_0_1(services, options.dryrun, service_name)
-
-        except ServiceConversionError as err:
-            print str(err)
-            # Add the service to the list of services that were not converted
-            # and keep the reason so that this list can be presented to the
-            # user at the end of the conversion
-            unconverted_services[service_name] = err
-
-    # Make all of the client conversions
-    clients = get_clients(service_names)
-    if not clients:
-        output = _('There are no clients configured on this server.\n')
-        sys.stdout.write(output)
-    else:
-        for service_name in clients:
-            # Only convert the client if the service was converted successfully
-            if not service_name in unconverted_services:
-                convert_client(clients, options.dryrun, service_name)
-
-    copy_netboot_files(options.dryrun)
-
-    # If there are existing default-i386 or default-sparc services
-    # rename them to default-<arch>_1 (or the first unused number)
-    for service_name in services:
-        if not service_name in unconverted_services:
-            if service_name == 'default-i386' or \
-                service_name == 'default-sparc':
-                new_service_name = generate_new_service_name(services,
-                                                         service_name)
-                print _(("Change service name %(name)s to %(new_name)s")
-                         % {'name': service_name,
-                            'new_name': new_service_name})
-
-                # Create a new service entry in the service dictionary
-                # and delete the old entry
-                services[new_service_name] = services[service_name]
-                del services[service_name]
-
-                if not options.dryrun:
-                    svc = AIService(service_name)
-                    svc.rename(new_service_name)
-
-    if len(unconverted_services) > 0:
-        print _("\nThe following services were not converted:\n")
-        for errored_service in unconverted_services:
-            # Print out the service and the reason that it wasn't converted
-            print cw(_("  %(errorsvc)s: %(error)s\n"
-                % {'errorsvc': errored_service, 'error':
-                str(unconverted_services[errored_service]).lstrip("Error: ")}))
-
-    # Enable the AI service
-    print _("Enable the AI SMF service")
-    if not options.dryrun:
-        try:
-            aismf.service_enable_attempt()
-        except aismf.ServicesError as err:
-            raise SystemExit(err)
-
-    sys.exit(EXIT_SUCCESS)
-
-if __name__ == "__main__":
-    main()
--- a/usr/src/cmd/installadm/svc-install-server	Mon Jun 04 19:19:12 2012 +0200
+++ b/usr/src/cmd/installadm/svc-install-server	Mon Jun 04 20:25:35 2012 -0700
@@ -132,7 +132,8 @@
 		echo "The following services are incompatible with this"
 		echo "version of $SMF_FMRI:"
 		echo $old_services
-		echo "Please run $UPGRADE_SCRIPT to upgrade them"
+		echo "Please boot Solaris 11 11/11, run $UPGRADE_SCRIPT"
+		echo "and then rerun the Solaris upgrade"
 		exit $SMF_EXIT_ERR_FATAL
 	fi
 
--- a/usr/src/cmd/rbac/exec_attr.install%2Finstalladm	Mon Jun 04 19:19:12 2012 +0200
+++ b/usr/src/cmd/rbac/exec_attr.install%2Finstalladm	Mon Jun 04 20:25:35 2012 -0700
@@ -23,4 +23,3 @@
 #
 Install Manifest Management:solaris:cmd:RO::/usr/sbin/installadm:euid=0
 Install Profile Management:solaris:cmd:RO::/usr/sbin/installadm:euid=0
-Install Service Management:solaris:cmd:RO::/usr/sbin/installadm-convert:euid=0
--- a/usr/src/pkg/manifests/install-installadm.mf	Mon Jun 04 19:19:12 2012 +0200
+++ b/usr/src/pkg/manifests/install-installadm.mf	Mon Jun 04 20:25:35 2012 -0700
@@ -208,7 +208,6 @@
     group=sys
 dir  path=usr/sbin
 file path=usr/sbin/installadm mode=0555
-file path=usr/sbin/installadm-convert mode=0555
 dir  path=usr/share group=sys
 dir  path=usr/share/auto_install group=sys
 file path=usr/share/auto_install/criteria_schema.rng group=sys