7031613 Enhance system configuration tool adding name services configuration
authorWilliam Schumann <william.schumann@sun.com>
Thu, 02 Jun 2011 00:03:21 +0200
changeset 1164 65b1a3a35195
parent 1163 b46f8cd05817
child 1165 27e2217baec9
7031613 Enhance system configuration tool adding name services configuration
usr/src/cmd/system-config/Makefile
usr/src/cmd/system-config/__init__.py
usr/src/cmd/system-config/nameservice.py
usr/src/cmd/system-config/network_nic_configure.py
usr/src/cmd/system-config/profile/Makefile
usr/src/cmd/system-config/profile/__init__.py
usr/src/cmd/system-config/profile/ip_address.py
usr/src/cmd/system-config/profile/nameservice_info.py
usr/src/cmd/system-config/profile/network_info.py
usr/src/cmd/system-config/profile/test/test_nameservice_info.py
usr/src/cmd/system-config/summary.py
usr/src/cmd/system-config/test/test_nameservice.py
usr/src/cmd/text-install/summary.py
usr/src/pkg/manifests/system-install-configuration.mf
--- a/usr/src/cmd/system-config/Makefile	Wed Jun 01 09:27:41 2011 -0700
+++ b/usr/src/cmd/system-config/Makefile	Thu Jun 02 00:03:21 2011 +0200
@@ -36,6 +36,7 @@
 
 PYMODULES=	__init__.py \
 		date_time.py \
+		nameservice.py \
 		network_nic_configure.py \
 		network_nic_select.py \
 		network_type.py \
--- a/usr/src/cmd/system-config/__init__.py	Wed Jun 01 09:27:41 2011 -0700
+++ b/usr/src/cmd/system-config/__init__.py	Thu Jun 02 00:03:21 2011 +0200
@@ -109,6 +109,12 @@
 from solaris_install.sysconfig.profile import ConfigProfile, SMFConfig, \
                                               SMFInstance, SMFPropertyGroup, \
                                               SMFProperty
+from solaris_install.sysconfig.nameservice import NSChooser, NSDomain, \
+                                                  NSDNSServer, NSDNSSearch, \
+                                                  NSLDAPProfile, \
+                                                  NSLDAPProxyBindChooser, \
+                                                  NSLDAPProxyBindInfo, \
+                                                  NSNISAuto, NSNISIP
 from solaris_install.sysconfig.summary import SummaryScreen
 from solaris_install.sysconfig.timezone import TimeZone
 from solaris_install.sysconfig.users import UserScreen
@@ -172,6 +178,7 @@
     result.append(NetworkTypeScreen(main_win, True))
     result.append(NICSelect(main_win))
     result.append(NICConfigure(main_win))
+    _append_nameservice_screens(result, main_win)
     result.append(TimeZone(main_win, screen=TimeZone.REGIONS))
     result.append(TimeZone(main_win, screen=TimeZone.LOCATIONS))
     result.append(TimeZone(main_win))
@@ -197,6 +204,10 @@
         result.append(NICSelect(main_win))
         result.append(NICConfigure(main_win))
 
+    # name services
+    if configure_group(SC_GROUP_NS):
+        _append_nameservice_screens(result, main_win)
+
     # timezone
     if configure_group(SC_GROUP_LOCATION):
         result.append(TimeZone(main_win, screen=TimeZone.REGIONS))
@@ -214,6 +225,19 @@
     return result
 
 
+def _append_nameservice_screens(result, main_win):
+    ''' Initialize and append all name service screens '''
+    result.append(NSChooser(main_win))
+    result.append(NSDomain(main_win))
+    result.append(NSDNSServer(main_win))
+    result.append(NSDNSSearch(main_win))
+    result.append(NSLDAPProfile(main_win))
+    result.append(NSLDAPProxyBindChooser(main_win))
+    result.append(NSLDAPProxyBindInfo(main_win))
+    result.append(NSNISAuto(main_win))
+    result.append(NSNISIP(main_win))
+
+
 def register_checkpoint(sc_profile=SC_FILE, xslt=XSLT_FILE):
     '''Registers the GENERATE_SC_PROFILE_CHKPOINT checkpoint with the engine.
     Also adds config_profile to InstallEngine.doc.persistent'''
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/system-config/nameservice.py	Thu Jun 02 00:03:21 2011 +0200
@@ -0,0 +1,682 @@
+#!/usr/bin/python
+#
+# 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, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''
+Name service selection screens
+'''
+
+import logging
+import re
+import string
+
+from solaris_install.logger import INSTALL_LOGGER_NAME
+from solaris_install.sysconfig import _, SC_GROUP_NETWORK, configure_group
+import solaris_install.sysconfig.profile
+from solaris_install.sysconfig.profile.ip_address import IPAddress
+from solaris_install.sysconfig.profile.network_info import NetworkInfo
+from solaris_install.sysconfig.profile.nameservice_info import NameServiceInfo
+from terminalui.base_screen import BaseScreen, SkipException, UIMessage
+from terminalui.edit_field import EditField
+from terminalui.i18n import textwidth
+from terminalui.list_item import ListItem
+from terminalui.scroll_window import ScrollWindow
+from terminalui.window_area import WindowArea
+
+
+LOGGER = None
+INDENT = 2          # standard left-side indentation
+MAXIP = 16          # maximum width of IP address plus one
+MAXDNSSEARCH = 6    # number of domain names on DNS search list
+MAXDOMAINLEN = 255  # maximum possible length of a domain
+
+# pre-compile patterns used in incremental validation
+NO_WHITE_NO_SPECIAL_PATTERN = re.compile(r'^[A-Z0-9\-_]+$')
+INCREMENTAL_DOMAIN_LABEL_PATTERN = re.compile(r'^[A-Z0-9\-]{0,63}$')
+DOMAIN_LABEL_PATTERN = re.compile(r'^[A-Z0-9\-]{1,63}$')
+
+
+class NameService(BaseScreen):
+    '''Allow user to select name service '''
+    # dimensions
+    SCROLL_SIZE = 2
+    BORDER_WIDTH = (0, 3)
+    # name service choices for display to user
+    USER_CHOICE_LIST = [_('DNS'), _('LDAP'), _('NIS'), _('None')]
+    # identify name service choices for internal use
+    CHOICE_LIST = ['DNS', 'LDAP', 'NIS', None]
+
+    def __init__(self, main_win, screen=None):
+        global LOGGER
+        if LOGGER is None:
+            LOGGER = logging.getLogger(INSTALL_LOGGER_NAME + ".sysconfig")
+        super(NameService, self).__init__(main_win)
+        self.cur_nschoice_idx = self.CHOICE_LIST.index("DNS")
+        self.cur_pbchoice_idx = NameServiceInfo.LDAP_CHOICE_NO_PROXY_BIND
+        self.cur_nisnschoice_idx = NameServiceInfo.NIS_CHOICE_AUTO
+        self.intro = "DEVELOPER: define intro in subclass"
+        # screen controls
+        self.nameservice = None
+        self.domain = None
+        self.dns_server_list = []
+        self.dns_search_list = []
+        self.ldap_profile = None
+        self.ldap_ip = None
+        self.ldap_pb_dn = None
+        self.ldap_pb_psw = None
+        self.nis_ip = None
+        self.scroll_region = None
+
+    def _show(self):
+        ''' called upon display of a screen '''
+        sc_profile = solaris_install.sysconfig.profile.from_engine()
+        if sc_profile.nic.type != NetworkInfo.MANUAL and \
+                configure_group(SC_GROUP_NETWORK):
+            raise SkipException
+        if sc_profile.nameservice is None:
+            # first time, assign sysconfig values to defaults found above
+            LOGGER.debug('assigning NSV to sysconfig.profile')
+            sc_profile.nameservice = NameServiceInfo()
+            nic = sc_profile.nic
+            LOGGER.info("Found NIC:")
+            LOGGER.info(nic)
+            # if values were learned from the NIC, offer those as defaults
+            if nic.domain:
+                sc_profile.nameservice.domain = nic.domain
+            if nic.dns_address:
+                sc_profile.nameservice.dns_server = nic.dns_address
+        self.nameservice = sc_profile.nameservice
+
+    def _paint_opening(self):
+        ''' paint screen with opening paragraph and blank line
+        Returns the row (y-offset) after the blank line '''
+        self.center_win.border_size = NameService.BORDER_WIDTH
+        return 2 + self.center_win.add_paragraph(self.intro, 1)
+
+
+class NSChooser(NameService):
+
+    def __init__(self, main_win):
+        super(NSChooser, self).__init__(main_win)
+        LOGGER.debug('in NSChooser init')
+        self.header_text = _("Name service")
+        self.intro = \
+                _("Select the name service that will be used by this "
+                  "system. Select None if the desired name service is not "
+                  "listed.")
+        self.title = _("Name service")
+
+    def _show(self):
+        ''' called upon display of a screen '''
+        super(NSChooser, self)._show()
+        y_loc = self._paint_opening()
+        LOGGER.debug(self.nameservice)
+        area = WindowArea(x_loc=0, y_loc=y_loc,
+                        scrollable_lines=len(NameService.USER_CHOICE_LIST) + 1)
+        area.lines = self.win_size_y - (y_loc + 1)
+        area.columns = self.win_size_x
+        self.scroll_region = ScrollWindow(area, window=self.center_win)
+        # add the entries to the screen
+        menu_item_max_width = self.win_size_x - NameService.SCROLL_SIZE
+        for idx, nsn in enumerate(NameService.USER_CHOICE_LIST):
+            y_loc += 1
+            hilite = min(menu_item_max_width, textwidth(nsn) + 1)
+            win_area = WindowArea(1, hilite, idx, INDENT)
+            ListItem(win_area, window=self.scroll_region, text=nsn,
+                     data_obj=nsn)
+        self.main_win.do_update()
+        self.center_win.activate_object(self.scroll_region)
+        self.scroll_region.activate_object_force(self.cur_nschoice_idx,
+                                                 force_to_top=True)
+
+    def on_change_screen(self):
+        ''' called when changes submitted by user for all screens '''
+        # Save the chosen index and object when leaving the screen
+        self.cur_nschoice_idx = self.scroll_region.active_object
+        idx = self.cur_nschoice_idx
+        LOGGER.info("on_change_screen NS chosen=%s", idx)
+        self.nameservice.nameservice = NameService.CHOICE_LIST[idx]
+
+
+class NSDomain(NameService):
+
+    def __init__(self, main_win, screen=None):
+        super(NSDomain, self).__init__(main_win)
+        self.header_text = _("Domain Name")
+        self.intro = \
+                _("Specify the domain where this system resides. "
+                  "Use the domain name's exact capitalization and "
+                  "punctuation.")
+        self.title = _("Domain Name:")
+
+    def _show(self):
+        ''' show domain '''
+        super(NSDomain, self)._show()
+        if self.nameservice.nameservice is None:
+            raise SkipException
+        y_loc = self._paint_opening()
+        cols = min(MAXDOMAINLEN + 1,
+                   self.win_size_x - textwidth(self.title) - INDENT - 1)
+        self.center_win.add_text(self.title, y_loc, INDENT)
+        area = WindowArea(1, cols, y_loc, textwidth(self.title) + INDENT + 1)
+        self.domain = EditField(area, window=self.center_win,
+                                text=self.nameservice.domain,
+                                validate=incremental_validate_domain,
+                                error_win=self.main_win.error_line)
+        self.main_win.do_update()
+        self.center_win.activate_object(self.domain)
+
+    def validate(self):
+        validate_domain(self.domain.get_text(), allow_empty=False)
+
+    def on_change_screen(self):
+        self.nameservice.domain = self.domain.get_text()
+
+
+class NSDNSServer(NameService):
+
+    def __init__(self, main_win, screen=None):
+        super(NSDNSServer, self).__init__(main_win)
+        self.header_text = _("DNS Server Addresses")
+        self.intro = \
+                _("Enter the IP address of the DNS server(s). "
+                  "At least one IP address is required.")
+        self.title = _("DNS Server IP address:")
+
+    def _show(self):
+        super(NSDNSServer, self)._show()
+        # check dictionary of screens associated with name service selections
+        if self.nameservice.nameservice != 'DNS':
+            raise SkipException
+        y_loc = self._paint_opening()
+        self.dns_server_list = []
+        find_1st_nonblank = None
+        find_last_nonblank = -1
+        for i in range(NameServiceInfo.MAXDNSSERV):
+            self.center_win.add_text(self.title, y_loc, INDENT)
+            area = WindowArea(1, MAXIP, y_loc,
+                              textwidth(self.title) + INDENT + 1)
+            y_loc += 1
+            if i < len(self.nameservice.dns_server) and \
+                    self.nameservice.dns_server[i] is not None:
+                text = self.nameservice.dns_server[i]
+            else:
+                text = ''
+            # find first blank field or last non-blank field
+            if text:
+                find_last_nonblank = i
+            elif find_1st_nonblank is None:
+                find_1st_nonblank = i
+            self.dns_server_list += [EditField(area, window=self.center_win,
+                                           text=text,
+                                           validate=incremental_validate_ip,
+                                           error_win=self.main_win.error_line)]
+        self.main_win.do_update()
+        # position cursor on first blank or after last field
+        if find_1st_nonblank is None:
+            idx = min(find_last_nonblank + 1, NameServiceInfo.MAXDNSSERV - 1)
+        else:
+            idx = find_1st_nonblank
+        self.center_win.activate_object(self.dns_server_list[idx])
+
+    def validate(self):
+        found = False
+        for field in self.dns_server_list:
+            validate_ip(field.get_text())
+            if field.get_text():
+                found = True
+        if not found:
+            raise UIMessage(
+                    _("At least one name server must be specified."))
+
+    def on_change_screen(self):
+        dnsl = []
+        for i in range(NameServiceInfo.MAXDNSSERV):
+            dnsl.append(self.dns_server_list[i].get_text())
+        self.nameservice.dns_server = dnsl
+
+
+class NSDNSSearch(NameService):
+
+    def __init__(self, main_win, screen=None):
+        super(NSDNSSearch, self).__init__(main_win)
+        self.header_text = _("DNS Search List")
+        self.intro = \
+                _("Enter a list of domains to be searched when a DNS "
+                  "query is made. If no domain is entered, only the "
+                  "DNS domain chosen for this system will be searched.")
+        self.title = _("Search domain:")
+
+    def _show(self):
+        ''' show DNS search list '''
+        super(NSDNSSearch, self)._show()
+        # check dictionary of screens associated with name service selections
+        if self.nameservice.nameservice != 'DNS':
+            raise SkipException
+        y_loc = self._paint_opening()
+        cols = min(MAXDOMAINLEN + 1,
+                   self.win_size_x - textwidth(self.title) - INDENT - 1)
+        self.dns_search_list = []
+        find_1st_nonblank = None
+        find_last_nonblank = -1
+        LOGGER.info(self.nameservice.dns_search)
+        for i in range(MAXDNSSEARCH):
+            self.center_win.add_text(text=self.title, start_y=y_loc,
+                                     start_x=INDENT)
+            area = WindowArea(1, cols, y_loc,
+                              textwidth(self.title) + INDENT + 1)
+            y_loc += 1
+            if i == 0 and self.nameservice.domain:
+                # default in domain user already entered
+                text = self.nameservice.domain
+                find_last_nonblank = 0
+            elif i < len(self.nameservice.dns_search) and \
+                    self.nameservice.dns_search[i] is not None:
+                text = self.nameservice.dns_search[i]
+            else:
+                text = ''
+            # find first blank field or last non-blank field
+            if text:
+                find_last_nonblank = i
+            elif find_1st_nonblank is None:
+                find_1st_nonblank = i
+            edf = EditField(area, window=self.center_win,
+                            text=text,
+                            validate=incremental_validate_domain,
+                            error_win=self.main_win.error_line)
+            self.dns_search_list += [edf]
+        self.main_win.do_update()
+        # position cursor on first blank or after last field
+        if find_1st_nonblank is None:
+            idx = min(find_last_nonblank + 1, MAXDNSSEARCH - 1)
+        else:
+            idx = find_1st_nonblank
+        self.center_win.activate_object(self.dns_search_list[idx])
+
+    def validate(self):
+        for field in self.dns_search_list:
+            validate_domain(field.get_text())
+
+    def on_change_screen(self):
+        dnsl = []
+        for i in range(MAXDNSSEARCH):
+            dnsl.append(self.dns_search_list[i].get_text())
+        self.nameservice.dns_search = dnsl
+
+
+class NSLDAPProfile(NameService):
+
+    def __init__(self, main_win):
+        super(NSLDAPProfile, self).__init__(main_win)
+        self.header_text = _("LDAP Profile")
+        self.intro = \
+                _("Specify the name of the LDAP profile to be used to "
+                  "configure this system and the IP address of the "
+                  "server that contains the profile.")
+        self.title = _("Profile name:")
+        self.title2 = _("Profile server IP address:")
+
+    def _show(self):
+        super(NSLDAPProfile, self)._show()
+        if self.nameservice.nameservice != 'LDAP':
+            raise SkipException
+        y_loc = self._paint_opening()
+        maxtitlelen = max(textwidth(self.title), textwidth(self.title2))
+        cols = self.win_size_x - maxtitlelen - INDENT - 1
+        self.center_win.add_text(self.title.rjust(maxtitlelen), y_loc, INDENT)
+        area = WindowArea(1, cols, y_loc, maxtitlelen + INDENT + 1)
+        self.ldap_profile = EditField(area, window=self.center_win,
+                                      text=self.nameservice.ldap_profile,
+                                      error_win=self.main_win.error_line,
+                                      validate=inc_validate_nowhite_nospecial)
+        # in case of error, tell user what is being validated
+        self.ldap_profile.validate_kwargs['etext'] = _('profile name')
+        y_loc += 1
+        area = WindowArea(1, MAXIP, y_loc, maxtitlelen + INDENT + 1)
+        self.center_win.add_text(self.title2.rjust(maxtitlelen), y_loc, INDENT)
+        self.ldap_ip = EditField(area, window=self.center_win,
+                                 text=self.nameservice.ldap_ip,
+                                 validate=incremental_validate_ip,
+                                 error_win=self.main_win.error_line)
+        self.main_win.do_update()
+        self.center_win.activate_object(self.ldap_ip)
+
+    def validate(self):
+        validate_ldap_profile(self.ldap_profile.get_text())
+        validate_ip(self.ldap_ip.get_text())
+        if not self.ldap_profile.get_text():
+            raise UIMessage(_("The LDAP profile name cannot be blank."))
+        if not self.ldap_ip.get_text():
+            raise UIMessage(_("The LDAP server IP address cannot be blank."))
+
+    def on_change_screen(self):
+        self.nameservice.ldap_profile = self.ldap_profile.get_text()
+        self.nameservice.ldap_ip = self.ldap_ip.get_text()
+
+
+class NSLDAPProxyBindChooser(NameService):
+
+    def __init__(self, main_win):
+        super(NSLDAPProxyBindChooser, self).__init__(main_win)
+        self.header_text = _("LDAP Proxy")
+        self.intro = _('Does the profile specify a proxy credential level '
+                       'and an authentication method other than None?')
+
+    def _show(self):
+        super(NSLDAPProxyBindChooser, self)._show()
+        # check dictionary of screens associated with name service selections
+        if self.nameservice.nameservice != 'LDAP':
+            raise SkipException
+        y_loc = self._paint_opening()
+        ynlist = [_('No'),
+                  _('Yes')]
+        area = WindowArea(x_loc=0, y_loc=y_loc,
+                          scrollable_lines=len(ynlist) + 1)
+        area.lines = self.win_size_y - (y_loc + 1)
+        area.columns = self.win_size_x
+        self.scroll_region = ScrollWindow(area, window=self.center_win)
+        # add the entries to the screen
+        for idx, yon in enumerate(ynlist):
+            win_area = WindowArea(1, textwidth(yon) + 1, idx, INDENT)
+            ListItem(win_area, window=self.scroll_region, text=yon,
+                     data_obj=yon)
+            self.main_win.do_update()
+            self.center_win.activate_object(self.scroll_region)
+            self.scroll_region.activate_object_force(self.cur_pbchoice_idx,
+                                                     force_to_top=True)
+
+    def on_change_screen(self):
+        self.cur_pbchoice_idx = self.scroll_region.active_object
+        idx = self.cur_pbchoice_idx
+        self.nameservice.ldap_proxy_bind = idx
+
+
+class NSLDAPProxyBindInfo(NameService):
+
+    def __init__(self, main_win, screen=None):
+        super(NSLDAPProxyBindInfo, self).__init__(main_win)
+        self.header_text = _("Specify LDAP Profile Proxy Bind Information")
+        self.intro = \
+                _("Specify the LDAP proxy bind distinguished name and the "
+                  "LDAP proxy bind password.  The network administrator "
+                  "can provide this information.")
+        self.title = _("Proxy bind distinguished name:")
+        self.title2 = _("Proxy bind password:")
+
+    def _show(self):
+        super(NSLDAPProxyBindInfo, self)._show()
+        if self.nameservice.nameservice != 'LDAP':
+            raise SkipException
+        if self.nameservice.ldap_proxy_bind == \
+                NameServiceInfo.LDAP_CHOICE_NO_PROXY_BIND:
+            raise SkipException
+        y_loc = self._paint_opening()
+        maxtitlelen = max(textwidth(self.title), textwidth(self.title2))
+        y_loc += 1
+        self.center_win.add_text(self.title.rjust(maxtitlelen), y_loc, INDENT)
+        cols = self.win_size_x - maxtitlelen - INDENT - 1
+        area = WindowArea(1, cols, y_loc, maxtitlelen + INDENT + 1)
+        self.ldap_pb_dn = EditField(area, window=self.center_win,
+                                    text=self.nameservice.ldap_pb_dn,
+                                    error_win=self.main_win.error_line,
+                                    validate=inc_validate_nowhite_nospecial)
+        # in case of error, tell user what is being validated
+        self.ldap_pb_dn.validate_kwargs['etext'] = _('distinguished name')
+        y_loc += 1
+        self.center_win.add_text(self.title2.rjust(maxtitlelen), y_loc,
+                                 NameService.SCROLL_SIZE)
+        area = WindowArea(1, cols, y_loc, maxtitlelen + INDENT + 1)
+        self.ldap_pb_psw = EditField(area, window=self.center_win,
+                                     text=self.nameservice.ldap_pb_psw,
+                                     error_win=self.main_win.error_line)
+        self.main_win.do_update()
+        self.center_win.activate_object(self.ldap_pb_dn)
+
+    def validate(self):
+        validate_ldap_proxy_dn(self.ldap_pb_dn.get_text())
+        if not self.ldap_pb_dn.get_text():
+            raise UIMessage(
+                _("The LDAP proxy server distinguished name cannot be blank."))
+        validate_ldap_proxy_bind_psw(self.ldap_pb_psw.get_text())
+
+    def on_change_screen(self):
+        self.nameservice.ldap_pb_dn = self.ldap_pb_dn.get_text()
+        self.nameservice.ldap_pb_psw = self.ldap_pb_psw.get_text()
+
+
+class NSNISAuto(NameService):
+
+    def __init__(self, main_win, screen=None):
+        super(NSNISAuto, self).__init__(main_win)
+        self.header_text = _("NIS Name Server")
+        self.intro = \
+                _("Specify how to find a name server for this system.")
+        self.intro2 = \
+                _("Either let the software search for a name server, "
+                  "or specify a name server in the following screen.  ")
+        self.intro3 = \
+                _("The software can find a name server only if that "
+                  "server is on the local subnet.")
+        self.title = _("Select one option:")
+
+    def _show(self):
+        super(NSNISAuto, self)._show()
+        if self.nameservice.nameservice != 'NIS':
+            raise SkipException
+        y_loc = self._paint_opening()
+        y_loc += self.center_win.add_paragraph(self.intro2, y_loc)
+        y_loc += 1
+        ynlist = [_('Find one'),
+                  _('Specify one')]
+        area = WindowArea(x_loc=0, y_loc=y_loc,
+                          scrollable_lines=len(ynlist) + 1)
+        area.lines = self.win_size_y - (y_loc + 1)
+        area.columns = self.win_size_x
+        self.scroll_region = ScrollWindow(area, window=self.center_win)
+        y_loc += 1  # blank line
+        # add the entries to the screen
+        for idx, yon in enumerate(ynlist):
+            y_loc += 1
+            win_area = WindowArea(lines=1, columns=textwidth(yon) + 1,
+                                  y_loc=idx, x_loc=INDENT)
+            ListItem(win_area, window=self.scroll_region, text=yon,
+                     data_obj=yon)
+            self.main_win.do_update()
+        self.center_win.activate_object(self.scroll_region)
+        self.scroll_region.activate_object_force(self.cur_nisnschoice_idx,
+                                                 force_to_top=True)
+        y_loc += 1  # blank line
+        self.center_win.add_paragraph(self.intro3, y_loc)
+
+    def on_change_screen(self):
+        self.cur_nisnschoice_idx = self.scroll_region.active_object
+        idx = self.cur_nisnschoice_idx
+        self.nameservice.nis_auto = idx
+
+
+class NSNISIP(NameService):
+
+    def __init__(self, main_win):
+        super(NSNISIP, self).__init__(main_win)
+        self.header_text = _("NIS Name Server Information")
+        self.intro = \
+                _("Enter the IP address of the name server.  IP "
+                  "addresses must contain four sets of numbers separated "
+                  "by periods (for example, 129.200.9.1).")
+        self.title = _("Server's IP address:")
+
+    def _show(self):
+        super(NSNISIP, self)._show()
+        if self.nameservice.nameservice != 'NIS':
+            raise SkipException
+        if self.nameservice.nis_auto == NameServiceInfo.NIS_CHOICE_AUTO:
+            raise SkipException
+        y_loc = self._paint_opening()
+        self.center_win.add_text(self.title, y_loc, INDENT)
+        maxtitlelen = textwidth(self.title)
+        cols = self.win_size_x - maxtitlelen - INDENT - 1
+        area = WindowArea(1, cols, y_loc, maxtitlelen + INDENT + 1)
+        self.center_win.add_text(self.title, y_loc, INDENT)
+        area = WindowArea(1, MAXIP, y_loc, maxtitlelen + INDENT + 1)
+        self.nis_ip = EditField(area, window=self.center_win,
+                                text=self.nameservice.nis_ip,
+                                validate=incremental_validate_ip,
+                                error_win=self.main_win.error_line)
+        self.main_win.do_update()
+        self.center_win.activate_object(self.nis_ip)
+
+    def validate(self):
+        validate_ip(self.nis_ip.get_text())
+        if not self.nis_ip.get_text():
+            raise UIMessage(_("The NIS server IP address cannot be blank."))
+
+    def on_change_screen(self):
+        self.nameservice.nis_ip = self.nis_ip.get_text()
+
+
+def validate_ldap_profile(profile):
+    ''' given an LDAP profile string, validate it
+    Arg: profile - LDAP profile string
+    Raises: UIMessage on failure
+    '''
+    if not profile:
+        raise UIMessage(_("The LDAP profile name cannot be blank."))
+    emsg = _("Whitespace characters and quotation marks are not allowed in "
+             "LDAP profile names.")
+    for cha in profile:
+        if cha in string.whitespace or cha in "'\"":
+            raise UIMessage(emsg)
+
+
+def validate_ldap_proxy_dn(proxy_dn):
+    ''' given an LDAP proxy distinguished name string, validate it
+    Arg: profile - LDAP proxy distinguished name
+    Raises: UIMessage on failure
+    '''
+    if not proxy_dn:
+        raise UIMessage(_(
+                   "The LDAP proxy bind distinguished name may not be blank."))
+    for cha in proxy_dn:
+        if cha in string.whitespace:
+            raise UIMessage(_("The LDAP proxy bind distinguished name may not "
+                              "contain whitespace characters."))
+        if cha in "'\"":
+            raise UIMessage(_("The LDAP proxy bind distinguished name may not "
+                              "contain quotation marks."))
+
+
+def validate_ldap_proxy_bind_psw(proxy_psw):
+    ''' given an LDAP proxy bind password string, validate it
+    Arg: profile - LDAP proxy bind password
+    Raises: UIMessage on failure
+    '''
+    if not proxy_psw:
+        raise UIMessage(_("The LDAP proxy bind password may not be blank."))
+    for cha in proxy_psw:
+        if cha in string.whitespace:
+            raise UIMessage(_("The LDAP proxy bind password may not contain "
+                              "whitespace characters."))
+        if cha in "'\"":
+            raise UIMessage(_("The LDAP proxy bind password may not contain "
+                              "quotation marks."))
+
+
+def validate_ip(ip_address):
+    '''Wrap a call to IPAddress.check_address and raise a UIMessage with
+    appropriate message text
+    '''
+    if not ip_address:
+        return True
+    try:
+        IPAddress.convert_address(ip_address)
+    except ValueError:
+        raise UIMessage(_("An IP address must be of the form xxx.xxx.xxx.xxx"))
+
+
+def validate_domain(domain, allow_empty=True):
+    ''' given a domain string, validate it
+    Args: domain - network domain name
+          allow_empty - if False, and domain is blank, failure
+    Raises: UIMessage on failure
+    '''
+    if not domain:
+        if allow_empty:
+            return
+        raise UIMessage(_("The domain cannot be blank."))
+    for label in domain.split('.'):
+        if len(label) > 63:
+            raise UIMessage(
+                         _("Domain labels must have less than 64 characters."))
+        if label.startswith('-') or label.endswith('-'):
+            raise UIMessage(
+                _("Domain labels should not start or end with hyphens ('-')."))
+        global DOMAIN_LABEL_PATTERN
+        if not DOMAIN_LABEL_PATTERN.match(label.upper()):
+            raise UIMessage(_("Invalid domain"))
+
+
+def incremental_validate_ip(edit_field):
+    '''Incrementally validate the IP Address as the user enters it
+    Arg: edit_field - EditField object for validation
+    Raises: UIMessage on failure
+    '''
+    ip_address = edit_field.get_text()
+    if not ip_address:
+        return True
+    try:
+        IPAddress.incremental_check(ip_address)
+    except ValueError:
+        raise UIMessage(_("An IP address must be of the form xxx.xxx.xxx.xxx"))
+    return True
+
+
+def inc_validate_nowhite_nospecial(edit_field, etext='<empty>'):
+    '''Incrementally validate EditField as the user enters it
+    Args: edit_field - EditField object for validation
+          etext - text to paste upon error
+    Raises: UIMessage upon finding whitespace and special characters other than
+            hyphens and underscores
+    '''
+    profile = edit_field.get_text()
+    if not profile:
+        raise UIMessage(_("The %s cannot be blank.") % etext)
+    global NO_WHITE_NO_SPECIAL_PATTERN
+    if not NO_WHITE_NO_SPECIAL_PATTERN.match(profile.upper()):
+        raise UIMessage(_('Invalid character for %s.') % etext)
+
+
+def incremental_validate_domain(edit_field):
+    '''Incrementally validate EditField as a domain as the user enters it
+    Arg: edit_field - EditField object with domain to validate
+    Raises: UIMessage if invalid character typed
+    '''
+    domain = edit_field.get_text()
+    for label in domain.split('.'):
+        if len(label) > 63:
+            raise UIMessage(
+                          _("Domain labels must have less than 64 characters"))
+        if label.startswith('-'):
+            raise UIMessage(_('A domain label may not begin with a hyphen.'))
+        global INCREMENTAL_DOMAIN_LABEL_PATTERN
+        if not INCREMENTAL_DOMAIN_LABEL_PATTERN.match(label.upper()):
+            raise UIMessage(_('Invalid character for domain name.'))
--- a/usr/src/cmd/system-config/network_nic_configure.py	Wed Jun 01 09:27:41 2011 -0700
+++ b/usr/src/cmd/system-config/network_nic_configure.py	Thu Jun 02 00:03:21 2011 +0200
@@ -49,17 +49,15 @@
     
     HEADER_TEXT = _("Manually Configure: %s")
     
-    PARAGRAPH = _("Enter the configuration for the this network"
-                  " connection. All entries, except Domain, "
-                  "must contain four sets of numbers, 0 to 255,"
-                  " separated by periods.")
+    PARAGRAPH = _("Enter the configuration for this network "
+                  "connection. All entries must contain four sets of "
+                  "numbers, 0 to 255, separated by periods.")
     IP_LABEL = _("IP Address:")
     IP_DESCRIPTION = _("Must be unique for this network")
     NETMASK_LABEL = _("Netmask:")
     NETMASK_DESCRIPTION = _("Your subnet use may require a different mask")
     GATEWAY_LABEL = _("Router:")
     GATEWAY_DESCRIPTION = _("The IP address of the router on this subnet")
-    DNS_LABEL = _("DNS:")
     DNS_FOUND = _("A DNS server was found on the network")
     DNS_NOT_FOUND = _("Address of the Domain Name Server")
     DOMAIN_LABEL = _("Domain:")
@@ -87,7 +85,6 @@
         item_length = max(len(NICConfigure.IP_LABEL),
                           len(NICConfigure.NETMASK_LABEL),
                           len(NICConfigure.GATEWAY_LABEL),
-                          len(NICConfigure.DNS_LABEL),
                           len(NICConfigure.DOMAIN_LABEL))
         item_length += 1
         list_width = item_length + NICConfigure.EDIT_FIELD_LEN
@@ -97,8 +94,6 @@
         self.ip_field = None
         self.netmask_field = None
         self.gateway_field = None
-        self.dns_field = None
-        self.domain_field = None
         self.nic = None
     
     def _show(self):
@@ -153,21 +148,6 @@
                                              y_loc, max_y, max_x,
                                              description_start,
                                              self.nic.gateway)
-        
-        y_loc += max_y
-        self.dns_field = self.make_field(NICConfigure.DNS_LABEL,
-                                         self.dns_description,
-                                         y_loc, max_y, max_x,
-                                         description_start,
-                                         self.nic.dns_address)
-        
-        y_loc += max_y
-        self.domain_field = self.make_field(NICConfigure.DOMAIN_LABEL,
-                                            self.domain_description, y_loc,
-                                            max_y, max_x, description_start,
-                                            self.nic.domain,
-                                            is_ip=False)
-        
         self.main_win.do_update()
         self.center_win.activate_object()
     
@@ -197,8 +177,7 @@
         '''Verify the syntactical validity of the IP Address fields'''
         ip_fields = [self.ip_field,
                      self.netmask_field,
-                     self.gateway_field,
-                     self.dns_field]
+                     self.gateway_field]
         for field in ip_fields:
             validate_ip(field)
         
@@ -213,16 +192,11 @@
             except ValueError:
                 raise UIMessage(_("'%s' is not a valid netmask") % netmask)
 
-        if self.domain_field.get_text() and not self.dns_field.get_text():
-            raise UIMessage(_("DNS server required if Domain set"))
-    
     def on_change_screen(self):
         '''Preserve all data on screen changes'''
         self.nic.ip_address = self.ip_field.get_text()
         self.nic.netmask = self.netmask_field.get_text()
         self.nic.gateway = self.gateway_field.get_text()
-        self.nic.dns_address = self.dns_field.get_text()
-        self.nic.domain = self.domain_field.get_text()
         self.nic.find_defaults = False
         LOGGER.debug("Setting network to:\n%s", self.nic)
     
@@ -256,6 +230,7 @@
                         edit_field.data_obj)
     return True
 
+
 # pylint: disable-msg=C0103
 # IP is an abbreviation and appropriately capitalized here
 def incremental_validate_IP(edit_field):
--- a/usr/src/cmd/system-config/profile/Makefile	Wed Jun 01 09:27:41 2011 -0700
+++ b/usr/src/cmd/system-config/profile/Makefile	Thu Jun 02 00:03:21 2011 +0200
@@ -31,6 +31,7 @@
 
 PYMODULES=	__init__.py \
 		ip_address.py \
+		nameservice_info.py \
 		network_info.py \
 		system_info.py \
 		user_info.py
--- a/usr/src/cmd/system-config/profile/__init__.py	Wed Jun 01 09:27:41 2011 -0700
+++ b/usr/src/cmd/system-config/profile/__init__.py	Thu Jun 02 00:03:21 2011 +0200
@@ -31,13 +31,14 @@
 
 USER_LABEL = "user_account"
 NETWORK_LABEL = "nic"
+NAMESERVICE_LABEL = "nsv"
 SYSTEM_LABEL = 'system_info'
 
 
 def from_engine():
     '''Convenience function for getting the ConfigProfile from
     the engine's DOC instance.
-    
+
     '''
     doc = InstallEngine.get_instance().doc
     sysconfig = doc.get_descendants(class_type=ConfigProfile)
@@ -52,7 +53,7 @@
     the engine's DOC instance. By default, removes any existing
     ConfigProfiles - set override=False to bypass that
     behavior.
-    
+
     '''
     parent = InstallEngine.get_instance().doc.persistent
     if override:
@@ -63,20 +64,20 @@
 
 class XMLElement(DataObject):
     '''Bare XML Element'''
-    
+
     def __init__(self, name, attribs=None):
         if attribs is None:
             attribs = {}
         self.attribs = attribs
         super(XMLElement, self).__init__(name)
-    
+
     def to_xml(self):
         return etree.Element(self.name, **self.attribs)
-    
+
     @classmethod
     def can_handle(cls, element):
         return False
-    
+
     @classmethod
     def from_xml(cls, element):
         return None
@@ -84,22 +85,22 @@
 
 class ConfigProfile(DataObject):
     '''Config profile will hold SMFConfig objects as children'''
-    
+
     LABEL = "sysconfig"
-    
+
     def __init__(self, nic=None, system=None):
         super(ConfigProfile, self).__init__(self.LABEL)
-        
+
         self.system = system
         self.nic = nic
         self.generates_xml_for_children = True
-    
+
     # pylint: disable-msg=E0202
     @property
     def system(self):
         '''Retrieve SystemInfo child object'''
         return self.get_first_child(name=SYSTEM_LABEL)
-    
+
     # pylint: disable-msg=E1101
     # pylint: disable-msg=E0102
     # pylint: disable-msg=E0202
@@ -111,12 +112,12 @@
             self.remove_children(old)
         if sysinfo:
             self.insert_children([sysinfo])
-    
+
     @property
     def nic(self):
         '''Retrieve NetworkInfo child object'''
         return self.get_first_child(name=NETWORK_LABEL)
-    
+
     @nic.setter
     def nic(self, netinfo):
         '''Replace NetworkInfo child object with netinfo'''
@@ -125,87 +126,102 @@
             self.remove_children(old)
         if netinfo:
             self.insert_children([netinfo])
-    
+
+    @property
+    def nameservice(self):
+        '''Retrieve NetworkServiceInfo child object'''
+        return self.get_first_child(name=NAMESERVICE_LABEL)
+
+    @nameservice.setter
+    def nameservice(self, nsvinfo):
+        '''Replace NetworkServiceInfo child object with nsvinfo'''
+        old = self.nameservice
+        if old:
+            self.remove_children(old)
+        if nsvinfo:
+            self.insert_children([nsvinfo])
+
     @property
     def users(self):
         '''Shortcut to retrieving UserInfos from the SystemInfo.
         Returns None if self.system is not set.
-        
+
         '''
         return getattr(self.system, "users", None)
-    
+
     @users.setter
     def users(self, user_infos):
         '''Shortcut to setting self.system.users = user_infos.
         May raise AttributeError if self.system is not set.
-        
+
         '''
         if user_infos:
             self.system.users = user_infos
-    
+
     def to_xml(self):
         '''Generate an SC profile XML tree'''
         element = etree.Element('service_bundle', type='profile',
                               name=self.name)
-        
+
         if self.system:
             element.extend(self.system.to_xml())
 
         # generate network configuration only if network group was configured
         if self.nic and self.nic.type is not None:
             element.extend(self.nic.to_xml())
-        
+        if self.nameservice:
+            element.extend(self.nameservice.to_xml())
+
         return element
-    
+
     @classmethod
     def from_xml(cls, xml_node):
         return None
-    
+
     @classmethod
     def can_handle(cls, xml_node):
         return False
 
-    
+
 class SMFConfig(DataObject):
     '''Represent a single SMF service. Stores SMFInstances
        or SMFPropertyGroups'''
-    
+
     def __init__(self, name):
         super(SMFConfig, self).__init__(name)
-        
+
         # For now, store data in a rather flat fashion
-    
+
     def to_xml(self):
         element = etree.Element('service', name=self.name,
                               version='1', type='service')
         return element
-    
+
     @classmethod
     def from_xml(cls, xml_node):
         return None
-    
+
     @classmethod
     def can_handle(cls, xml_node):
         return False
 
-    
+
 class SMFInstance(DataObject):
     '''Represent an instance of SMF service. Stores SMFPropertyGroups'''
-    
+
     def __init__(self, name, enabled=True):
         super(SMFInstance, self).__init__(name)
         self.enabled = enabled
-   
+
     def to_xml(self):
         enabled = 'true' if self.enabled else 'false'
-        element = etree.Element('instance', enabled=enabled,
-                                name=self.name)
+        element = etree.Element('instance', enabled=enabled, name=self.name)
         return element
-    
+
     @classmethod
     def from_xml(cls, xml_node):
         return None
-    
+
     @classmethod
     def can_handle(cls, xml_node):
         return False
@@ -213,18 +229,18 @@
 
 class SMFPropertyGroup(DataObject):
     '''Stores SMFProperties'''
-    
+
     def __init__(self, pg_name, pg_type='application'):
         super(SMFPropertyGroup, self).__init__(pg_name)
 
         self.pg_name = pg_name
         self.pg_type = pg_type
-    
+
     def to_xml(self):
         element = etree.Element('property_group', name=self.pg_name,
                                 type=self.pg_type)
         return element
-    
+
     def setprop(self, tag='propval', name=None, ptype=None, value=None):
         '''Create a child SMFProperty with given name and value'''
         existing = self.get_first_child(name=name)
@@ -235,52 +251,53 @@
         else:
             existing.propval = value
             return existing
-    
+
     @classmethod
     def from_xml(cls, xml_node):
         return None
-    
+
     @classmethod
     def can_handle(cls, xml_node):
         return False
-    
+
     def add_props(self, **properties):
         '''Create a series of child SMFProperties from all
         keyword arguments
-        
+
         '''
         smf_properties = []
         for propname, propval in properties.iteritems():
             prop = SMFProperty(propname, propval=propval)
             smf_properties.append(prop)
-    
+
         self.insert_children(smf_properties)
 
 
 class SMFProperty(DataObject):
     '''Represents an SMF property'''
-    
+
     def __init__(self, propname=None, tagname='propval', propval=None,
                  proptype=None):
 
         super(SMFProperty, self).__init__(propname)
-        
+
         self.propname = propname
         self.tagname = tagname
         self.propval = propval
         if proptype is not None:
             self.proptype = proptype
         else:
-            self.proptype = self.determine_proptype(propval)
-    
+            self.proptype = self.determine_proptype(propval, proptype)
+
     @staticmethod
-    def determine_proptype(propval, is_list=False):
+    def determine_proptype(propval, iproptype, is_list=False):
         '''Determine the SMF property type for propval
-        One of: astring, net_address_v4, or count
+        One of: astring, host, net_address, net_address_v4, or count
+        iproptype distinguishes between possible IP address types
         If propval is a list of properties, return the
         list equivalent (e.g., "astring_list" instead of
         "astring")
-        
+
         '''
         if is_list:
             if propval:
@@ -288,52 +305,58 @@
             else:
                 # Empty list
                 return "astring_list"
-        
+
         if propval is None:
             proptype = "astring"
         elif isinstance(propval, IPAddress):
-            proptype = "net_address_v4"
+            if iproptype == "host" or iproptype == "net_address":
+                proptype = iproptype
+            else:
+                proptype = "net_address_v4"
         else:
             try:
                 IPAddress(propval)
-                proptype = "net_address_v4"
+                if iproptype == "host" or iproptype == "net_address":
+                    proptype = iproptype
+                else:
+                    proptype = "net_address_v4"
             except:
                 try:
                     int(propval)
                     proptype = "count"
                 except:
                     proptype = "astring"
-        
+
         if is_list:
             proptype += "_list"
-        
+
         return proptype
-    
+
     def add_value_list(self, proptype="astring", propvals=None):
-        '''Create a child DataObject structure to represent an 
+        '''Create a child DataObject structure to represent an
         SMF property list
-        
+
         '''
         if propvals is None:
             propvals = []
-        
-        elem_name = self.determine_proptype(propvals, is_list=True)
-        
+
+        elem_name = self.determine_proptype(propvals, proptype, is_list=True)
+
         elem_type = self.get_first_child(name=elem_name)
         if elem_type is None:
             elem_type = XMLElement(elem_name)
             self.insert_children([elem_type])
-        
+
         elem_vals = []
         for val in propvals:
             if not isinstance(val, basestring):
                 raise ValueError("'%s' must be a string or unicode object" %
                                  val)
             elem_vals.append(XMLElement('value_node', {'value': val}))
-        
+
         elem_type.delete_children()
         elem_type.insert_children(elem_vals)
-    
+
     def to_xml(self):
         elem_kwargs = {}
         # map attributes of SMFProperty class to xml attributes in smf profile
@@ -345,16 +368,16 @@
             val = getattr(self, attr)
             if val:
                 elem_kwargs[obj_attr2xml_attr[attr]] = str(val)
-        
+
         # pylint: disable-msg=W0142
         element = etree.Element(self.tagname, **elem_kwargs)
-        
+
         return element
-    
+
     @classmethod
     def from_xml(cls, xml_node):
         return None
-    
+
     @classmethod
     def can_handle(cls, xml_node):
         return False
@@ -363,15 +386,15 @@
 def create_prop_group(name, **kwargs):
     '''Create an SMFPropertyGroup instance with SMFProperties
     determined by any arbitrary keyword arguments passed in.
-    
+
     '''
     group = SMFPropertyGroup(name)
-    
+
     properties = []
     for propname, propval in kwargs.iteritems():
         prop = SMFProperty(propname, propval=propval)
         properties.append(prop)
-    
+
     group.insert_children(properties)
     return group
 
@@ -379,7 +402,7 @@
 def create_other(timezone="GMT", hostname="Solaris"):
     '''Create an SMFPropertyGroup to encapsulate misc. install
     parameters (currently, timezone and hostname)
-    
+
     '''
     return create_prop_group("other_sc_params", timezone=timezone,
                              hostname=hostname)
--- a/usr/src/cmd/system-config/profile/ip_address.py	Wed Jun 01 09:27:41 2011 -0700
+++ b/usr/src/cmd/system-config/profile/ip_address.py	Thu Jun 02 00:03:21 2011 +0200
@@ -131,6 +131,10 @@
         address, e.g., one that is partly typed into the UI
         
         '''
+        if address[0] == '.':
+            raise ValueError("An IP address may not begin with a period.")
+        if address.endswith(".."):
+            raise ValueError("Periods must be separated by numbers.")
         ip = address.split(".")
         segments = []
         if len(ip) > 4:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/system-config/profile/nameservice_info.py	Thu Jun 02 00:03:21 2011 +0200
@@ -0,0 +1,293 @@
+#!/usr/bin/python
+#
+# 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, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''
+Class and functions supporting name services
+'''
+
+import logging
+
+from solaris_install.data_object import DataObject
+from solaris_install.logger import INSTALL_LOGGER_NAME
+from solaris_install.sysconfig.profile import NAMESERVICE_LABEL, SMFConfig, \
+        SMFInstance, SMFPropertyGroup
+
+
+_LOGGER = None
+# creates oft-used SMF instance list enabling default service
+ENABLED_DEFAULT_SERVICE_LIST = [SMFInstance('default', enabled=True)]
+
+
+def LOGGER():
+    global _LOGGER
+    if _LOGGER is None:
+        _LOGGER = logging.getLogger(INSTALL_LOGGER_NAME + ".sysconfig")
+    return _LOGGER
+
+
+class NameServiceInfo(SMFConfig):
+    '''Represents the Name service selected'''
+
+    LABEL = NAMESERVICE_LABEL
+    MAXDNSSERV = 3    # number of DNS server input fields
+    NIS_CHOICE_AUTO = 0
+    NIS_CHOICE_MANUAL = 1
+    LDAP_CHOICE_NO_PROXY_BIND = 0
+    LDAP_CHOICE_PROXY_BIND = 1
+
+    def __init__(self, nameservice=None, domain='',
+                 dns_server=[], dns_search=[],
+                 ldap_profile='default', ldap_ip='',
+                 ldap_proxy_bind=LDAP_CHOICE_NO_PROXY_BIND, ldap_pb_dn='',
+                 ldap_pb_psw='', nis_ip='', nis_auto=NIS_CHOICE_AUTO):
+        DataObject.__init__(self, self.LABEL)
+        self.nameservice = nameservice
+        self.domain = domain
+        # DNS-specific
+        self.dns_server = dns_server
+        self.dns_search = dns_search
+        # LDAP-specific
+        self.ldap_profile = ldap_profile
+        self.ldap_ip = ldap_ip
+        self.ldap_proxy_bind = ldap_proxy_bind
+        self.ldap_pb_dn = ldap_pb_dn
+        self.ldap_pb_psw = ldap_pb_psw
+        # NIS-specific
+        self.nis_auto = nis_auto
+        self.nis_ip = nis_ip
+
+    def __repr__(self):
+        return '\n'.join(["NS %s:" % self.nameservice,
+                          "Domain: %s" % self.domain,
+                          "DNSserv: %s" % self.dns_server,
+                          "DNSsearch: %s" % self.dns_search,
+                          "LDAPprofname: %s" % self.ldap_profile,
+                          "LDAPprofip: %s" % self.ldap_ip,
+                          "LDAPpbchoice: %s" % self.ldap_proxy_bind,
+                          "LDAPpbdn: %s" % self.ldap_pb_dn,
+                          "LDAPpbpsw: %s" % self.ldap_pb_psw,
+                          "NISauto: %s" % self.nis_auto,
+                          "NISip: %s" % self.nis_ip])
+
+    def to_xml(self):
+        data_objects = []
+        if self.nameservice is None:
+            # if no name services, files only
+            LOGGER().info('setting name service to files only')
+        # configure svc:system/name-service/switch
+        LOGGER().info('preparing profile with nsswitch')
+        # set NS switch service objects according to user's selection
+        data_objects.append(_set_nsswitch(self.nameservice))
+        # enable name service cache
+        data_objects.append(_enable_service('system/name-service/cache'))
+        LOGGER().debug('to_xml:name service type=%s', self.nameservice)
+        LOGGER().info(self)
+        if self.nameservice == 'DNS':
+            LOGGER().info('preparing profile for DNS')
+            dns = SMFConfig('network/dns/client')
+            data_objects.append(dns)
+            # configure 'config' property group
+            dns_props = SMFPropertyGroup('config')
+            dns.insert_children([dns_props])
+            # configure DNS nameservers
+            if self.dns_server:
+                # filter empty values from list
+                ilist = [val for val in self.dns_server if val]
+                if ilist:
+                    proptype = 'net_address'
+                    nameserver = dns_props.setprop("property", "nameserver",
+                                                   proptype)
+                    nameserver.add_value_list(propvals=ilist,
+                                              proptype=proptype)
+            # configure DNS domain
+            if self.domain:
+                dns_props.setprop("propval", "domain", "astring", self.domain)
+            if self.dns_search:
+                # filter empty values from list
+                ilist = [val for val in self.dns_search if val]
+                if ilist:
+                    search = dns_props.setprop("property", "search", "astring")
+                    search.add_value_list(propvals=[" ".join(ilist)])
+            # configure default service instance
+            dns.insert_children(ENABLED_DEFAULT_SERVICE_LIST)
+        elif self.nameservice == 'LDAP':
+            LOGGER().info('preparing profile for LDAP')
+            ldap = SMFConfig('network/ldap/client')
+            data_objects.append(ldap)
+            # configure 'config' property group
+            ldap_config_props = SMFPropertyGroup('config')
+            ldap.insert_children([ldap_config_props])
+            # add properties to 'config' property group
+            ldap_config_props.setprop("propval", "profile", "astring",
+                                      self.ldap_profile)
+            proptype = 'host'
+            nameserver = ldap_config_props.setprop("property", "server_list",
+                                                   proptype)
+            nameserver.add_value_list(propvals=[self.ldap_ip],
+                                      proptype=proptype)
+            # if user chose to provide proxy bind info
+            if self.ldap_proxy_bind == self.LDAP_CHOICE_PROXY_BIND:
+                # create and add properties to 'cred' property group
+                ldap_cred_props = SMFPropertyGroup('cred')
+                ldap.insert_children([ldap_cred_props])
+                ldap_cred_props.setprop("propval", "bind_dn", "astring",
+                                        self.ldap_pb_dn)
+                ldap_cred_props.setprop("propval", "bind_passwd", "astring",
+                                        self.ldap_pb_psw)
+            # configure default service instance
+            ldap.insert_children(ENABLED_DEFAULT_SERVICE_LIST)
+        # For NIS, user is given automatic (broadcast) or manual
+        # specification of NIS server.  If automatic, just set broadcast.
+        # Note: NIS domain is set below for LDAP as well as NIS
+        if self.nameservice == 'NIS' or \
+                (self.nameservice == 'LDAP' and self.domain):
+            LOGGER().info('preparing profile for NIS')
+            # manual NIS server and/or domain for LDAP and NIS
+            if self.domain or (self.nis_auto == self.NIS_CHOICE_MANUAL and
+                               self.nameservice == 'NIS'):
+                # enable network/nis/domain smf service
+                nis = SMFConfig('network/nis/domain')
+                data_objects.append(nis)
+                # configure 'config' property group
+                nis_props = SMFPropertyGroup('config')
+                nis.insert_children([nis_props])
+                # configure domain for NIS or LDAP
+                if self.domain:
+                    LOGGER().info('setting NIS domain', self.domain)
+                    nis_props.setprop("propval", "domainname", "hostname",
+                                      self.domain)
+                # manual configuration naming NIS server explicitly
+                if self.nis_auto == self.NIS_CHOICE_MANUAL and \
+                        self.nameservice == 'NIS':
+                    proptype = 'net_address'
+                    nis_ip = nis_props.setprop("property", "ypservers",
+                                               proptype)
+                    nis_ip.add_value_list(propvals=[self.nis_ip],
+                                          proptype=proptype)
+                # configure default service instance
+                nis.insert_children(ENABLED_DEFAULT_SERVICE_LIST)
+            # automatic NIS configuration (broadcast)
+            if self.nis_auto == self.NIS_CHOICE_AUTO and \
+                    self.nameservice == 'NIS':
+                # enable network/nis/client smf service
+                nis_client = SMFConfig('network/nis/client')
+                data_objects.append(nis_client)
+                # configure 'config' property group
+                nis_client_props = SMFPropertyGroup('config')
+                nis_client.insert_children([nis_client_props])
+                # set NIS broadcast property
+                nis_client_props.setprop("propval", "use_broadcast",
+                                         "boolean", "true")
+                # configure default service instance
+                nis_client.insert_children(ENABLED_DEFAULT_SERVICE_LIST)
+        return [do.get_xml_tree() for do in data_objects]
+
+    @classmethod
+    def from_xml(cls, xml_node):
+        return None
+
+    @classmethod
+    def can_handle(cls, xml_node):
+        return False
+
+
+def _set_nsswitch(nameservice):
+    ''' configure name services switch table
+    for svc: system/name-service/switch
+    Arg:
+        nameservice - name service NIS, DNS, or LDAP
+    Returns:
+        SMFconfig service with properties set
+    '''
+    svc = SMFConfig('system/name-service/switch')
+    # configure default instance
+    props = SMFPropertyGroup('config')
+    svc.insert_children([props])
+    # set name service sources per name service
+    source_dict = {
+            'DNS': {
+                'default': 'files',
+                'host': 'files dns mdns',
+                'printer': 'user files'},
+            'LDAP': {
+                'default': 'files ldap',
+                'host': 'files dns mdns',
+                'printer': 'user files ldap',
+                'netgroup': 'ldap'},
+            'NIS': {
+                'default': 'files nis',
+                'printer': 'user files nis',
+                'netgroup': 'nis'},
+            None: {
+                'default': 'files',
+                'printer': 'user files'}
+            }[nameservice]
+    for prop in source_dict:
+        props.setprop('propval', prop, 'astring', source_dict[prop])
+    # configure default service instance
+    svc.insert_children(ENABLED_DEFAULT_SERVICE_LIST)
+    return svc
+
+
+def _enable_service(service_name):
+    ''' enable a service by name
+    Args:
+        service_name - name of service to enable - no properties
+    Returns:
+        SMFconfig service enabled
+    '''
+    svc = SMFConfig(service_name)
+    svc.insert_children(ENABLED_DEFAULT_SERVICE_LIST)
+    return svc
+
+
+def _disable_ns(data_objects):
+    ''' disable all name services
+    Arg: data_objects - list of service objects to affect
+    Effect: append all name services, disabling them explicitly
+    Note: this is not presently used, since disabling name-service-switch
+        results in disabling the GNOME desktop as a result of a dependency
+        chain.
+    '''
+    for svcn in [
+            # switch and cache
+            "network/name-service-switch",
+            "network/name-service-cache",
+            # DNS
+            "network/dns/client",
+            # LDAP
+            "network/ldap/client",
+            # NIS client and server
+            "network/nis/domain",
+            "network/nis/client",
+            "network/nis/server",
+            "network/nis/passwd",
+            "network/nis/update",
+            "network/nis/xfr",
+            # supporting services for NIS
+            "network/rpc/keyserv"]:
+        svc = SMFConfig(svcn)
+        data_objects.append(svc)
+        # configure default instance, disabled
+        svc.insert_children([SMFInstance('default', enabled=False)])
--- a/usr/src/cmd/system-config/profile/network_info.py	Wed Jun 01 09:27:41 2011 -0700
+++ b/usr/src/cmd/system-config/profile/network_info.py	Thu Jun 02 00:03:21 2011 +0200
@@ -32,6 +32,7 @@
 from solaris_install.data_object import DataObject
 from solaris_install.logger import INSTALL_LOGGER_NAME
 from solaris_install.sysconfig.profile.ip_address import IPAddress
+from solaris_install.sysconfig.profile.nameservice_info import NameServiceInfo
 from solaris_install.sysconfig.profile import SMFConfig, SMFInstance, \
      SMFPropertyGroup, NETWORK_LABEL
 
@@ -48,16 +49,16 @@
 
 class NetworkInfo(SMFConfig):
     '''Represents a NIC and its network settings'''
-    
+
     AUTOMATIC = "automatic"
     MANUAL = "manual"
     NONE = "none"
     DEFAULT_NETMASK = "255.255.255.0"
-    
+
     ETHER_NICS = None
-    
+
     LABEL = NETWORK_LABEL
-    
+
     @staticmethod
     def find_links():
         '''Use dladm show-link to find available network interfaces (NICs).
@@ -71,7 +72,7 @@
             return NetworkInfo.ETHER_NICS
         
         argslist = ['/usr/sbin/dladm', 'show-link', '-o', 'link', '-p']
-        
+
         try:
             dladm_popen = Popen.check_call(argslist, stdout=Popen.STORE,
                                            stderr=Popen.STORE, logger=LOGGER())
@@ -122,7 +123,7 @@
     def __init__(self, nic_name=None, net_type=None, ip_address=None,
                  netmask=None, gateway=None, dns_address=None, domain=None):
         DataObject.__init__(self, self.LABEL)
-        
+
         self.nic_name = nic_name
         self.type = net_type
         self.ip_address = ip_address
@@ -133,7 +134,7 @@
         self.dns_address = dns_address
         self.domain = domain
         self.find_defaults = True
-    
+
     def __repr__(self):
         result = ["NIC %s:" % self.nic_name]
         result.append("Type: %s" % self.type)
@@ -163,23 +164,24 @@
                               NetworkInfo.NONE]))
         # pylint: disable-msg=W0201
         self._type = value
-    
+
     def find_dns(self):
         '''Try to determine the DNS info of the NIC if DHCP is running
         Returns True if this action was successful
         
         '''
-        dns_server = self._run_dhcpinfo("DNSserv")
+        dns_server = self._run_dhcpinfo("DNSserv",
+                                        maxent=NameServiceInfo.MAXDNSSERV)
         if dns_server:
-            self.dns_address = dns_server
+            self.dns_address = dns_server.splitlines()
             return True
         else:
             return False
-    
+
     def find_gateway(self):
         '''Try to determine the router of the NIC if DHCP is running
         Returns True if this action was successful
-        
+
         '''
         gateway = self._run_dhcpinfo("Router")
         if gateway:
@@ -187,11 +189,11 @@
             return True
         else:
             return False
-    
+
     def find_domain(self):
         '''Try to determine the domain info of the NIC if DHCP is running
         Returns True if this action was successful
-        
+
         '''
         domain = self._run_dhcpinfo("DNSdmain")
         if domain:
@@ -199,11 +201,11 @@
             return True
         else:
             return False
-    
+
     def find_netmask(self):
         '''Try to determine the netmask info of the NIC if DHCP is running
         Returns True if this action was successful
-        
+
         '''
         netmask = self._run_dhcpinfo("Subnet")
         if netmask:
@@ -211,11 +213,11 @@
             return True
         else:
             return False
-    
+
     def get_ifconfig_data(self):
         '''Returns a dictionary populated with the data returned from ifconfig
         Returns None if the call to ifconfig fails in some way
-        
+
         '''
         argslist = ['/sbin/ifconfig', self.nic_name]
         try:
@@ -235,9 +237,10 @@
         for i in range(len(ifconfig_out) / 2):
             link_data[ifconfig_out[2 * i]] = ifconfig_out[2 * i + 1]
         return link_data
-    
-    def _run_dhcpinfo(self, code):
+
+    def _run_dhcpinfo(self, code, maxent=1):
         '''Run the dhcpinfo command against this NIC, requesting 'code'
+        maxent - for lists, if >1, return a list with maxent max length
         
         This function always returns successfully; if the underlying call
         to dhcpinfo fails, then None is returned.
@@ -246,17 +249,17 @@
         if not ifconfig_data or ifconfig_data['flags'].count("DHCP") == 0:
             LOGGER().warn("This connection is not using DHCP")
             return None
-        
+
         argslist = ['/sbin/dhcpinfo',
                     '-i', self.nic_name,
-                    '-n', '1',
+                    '-n', str(maxent),
                     code]
         try:
             dhcp_popen = Popen.check_call(argslist, stdout=Popen.STORE,
                                           stderr=Popen.STORE, logger=LOGGER())
         except CalledProcessError as error:
-            LOGGER().warn("'dhcpinfo -i %s -n 1' failed with following error:"
-                          " %s" % (self.nic_name, error))
+            LOGGER().warn("'dhcpinfo -i %s -n %s' failed with following error:"
+                          " %s" % (self.nic_name, maxent, error))
             return None
             
         # pylint: disable-msg=E1103
@@ -268,11 +271,11 @@
 
         net_physical = SMFConfig("network/physical")
         data_objects.append(net_physical)
-        
+
         nwam = SMFInstance("nwam", enabled=False)
         net_default = SMFInstance("default", enabled=True)
         net_physical.insert_children([nwam, net_default])
-        
+
         if self.type == NetworkInfo.AUTOMATIC:
             nwam.enabled = True
             net_default.enabled = False
@@ -286,7 +289,7 @@
             ipv4 = SMFPropertyGroup('install_ipv4_interface')
             ipv6 = SMFPropertyGroup('install_ipv6_interface')
             net_install_default.insert_children([ipv4, ipv6])
-            
+
             static_address = IPAddress(self.ip_address, netmask=self.netmask)
 
             # IPv4 configuration
@@ -307,42 +310,12 @@
                            stateless='yes',
                            stateful='yes')
             
-            #
-            # If neither DNS nameservers nor domain was provided,
-            # there is nothing to be configured for DNS.
-            #
-            if self.dns_address or self.domain:
-                dns = SMFConfig('network/dns/install')
-                data_objects.append(dns)
-            
-                dns_default = SMFInstance('default')
-                dns.insert_children([dns_default])
-                dns_props = SMFPropertyGroup('install_props')
-                dns_default.insert_children([dns_props])
-
-                # configure DNS nameservers
-                if self.dns_address:
-                    nameserver = dns_props.setprop("property", "nameserver",
-                                               "net_address_v4")
-                    nameserver.add_value_list(propvals=[self.dns_address])
-
-                # configure DNS domain
-                if self.domain:
-                    search = dns_props.setprop("property", "search", "astring")
-                    search.add_value_list(propvals=[self.domain])
-
-                # enable dns/client smf service
-                dns_client = SMFConfig('network/dns/client')
-                data_objects.append(dns_client)
-                dns_client_default = SMFInstance('default')
-                dns_client.insert_children([dns_client_default])
-
         return [do.get_xml_tree() for do in data_objects]
 
     @classmethod
     def from_xml(cls, xml_node):
         return None
-    
+
     @classmethod
     def can_handle(cls, xml_node):
         return False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/system-config/profile/test/test_nameservice_info.py	Thu Jun 02 00:03:21 2011 +0200
@@ -0,0 +1,190 @@
+#!/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, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''
+To run these tests, see the instructions in usr/src/tools/tests/README.
+Remember that since the proto area is used for the PYTHONPATH, the gate
+must be rebuilt for these tests to pick up any changes in the tested code.
+
+'''
+
+from lxml import etree
+import unittest
+
+from solaris_install.sysconfig.profile.nameservice_info import NameServiceInfo
+
+
+SAMPLE_NONE_XML = '''<root>
+  <service version="1" type="service" name="system/name-service/switch">
+    <property_group type="application" name="config">
+      <propval type="astring" name="default" value="files"/>
+      <propval type="astring" name="printer" value="user files"/>
+    </property_group>
+    <instance enabled="true" name="default"/>
+  </service>
+  <service version="1" type="service" name="system/name-service/cache">
+    <instance enabled="true" name="default"/>
+  </service>
+</root>
+'''
+
+SAMPLE_DNS_XML = '''<root>
+  <service version="1" type="service" name="system/name-service/switch">
+    <property_group type="application" name="config">
+      <propval type="astring" name="default" value="files"/>
+      <propval type="astring" name="host" value="files dns mdns"/>
+      <propval type="astring" name="printer" value="user files"/>
+    </property_group>
+    <instance enabled="true" name="default"/>
+  </service>
+  <service version="1" type="service" name="system/name-service/cache">
+    <instance enabled="true" name="default"/>
+  </service>
+  <service version="1" type="service" name="network/dns/client">
+    <property_group type="application" name="config">
+      <property type="net_address" name="nameserver">
+        <net_address_list>
+          <value_node value="1.1.1.1"/>
+        </net_address_list>
+      </property>
+      <propval type="astring" name="domain" value="my.domain.com"/>
+      <property type="astring" name="search">
+        <astring_list>
+          <value_node value="my.domain.com"/>
+        </astring_list>
+      </property>
+    </property_group>
+    <instance enabled="true" name="default"/>
+  </service>
+'''
+
+SAMPLE_LDAP_XML = '''<root>
+  <service version="1" type="service" name="system/name-service/switch">
+    <property_group type="application" name="config">
+      <propval type="astring" name="default" value="files ldap"/>
+      <propval type="astring" name="host" value="files dns mdns"/>
+      <propval type="astring" name="printer" value="user files ldap"/>
+      <propval type="astring" name="netgroup" value="ldap"/>
+    </property_group>
+    <instance enabled="true" name="default"/>
+  </service>
+  <service version="1" type="service" name="system/name-service/cache">
+    <instance enabled="true" name="default"/>
+  </service>
+  <service version="1" type="service" name="network/ldap/client">
+    <property_group type="application" name="config">
+      <propval type="astring" name="profile" value="default"/>
+      <property type="host" name="server_list">
+        <host_list>
+          <value_node value="1.1.1.1"/>
+        </host_list>
+      </property>
+    </property_group>
+    <instance enabled="true" name="default"/>
+  </service>
+  <service version="1" type="service" name="network/nis/domain">
+    <property_group type="application" name="config">
+      <propval type="hostname" name="domainname" value="my.domain.com"/>
+    </property_group>
+    <instance enabled="true" name="default"/>
+  </service>
+</root>
+'''
+
+SAMPLE_NIS_XML = '''<root>
+  <service version="1" type="service" name="system/name-service/switch">
+    <property_group type="application" name="config">
+      <propval type="astring" name="default" value="files nis"/>
+      <propval type="astring" name="printer" value="user files nis"/>
+      <propval type="astring" name="netgroup" value="nis"/>
+    </property_group>
+    <instance enabled="true" name="default"/>
+  </service>
+  <service version="1" type="service" name="system/name-service/cache">
+    <instance enabled="true" name="default"/>
+  </service>
+  <service version="1" type="service" name="network/nis/domain">
+    <property_group type="application" name="config">
+      <propval type="hostname" name="domainname" value="my.domain.com"/>
+      <property type="net_address" name="ypservers">
+        <net_address_list>
+          <value_node value="1.1.1.1"/>
+        </net_address_list>
+      </property>
+    </property_group>
+    <instance enabled="true" name="default"/>
+  </service>
+</root>
+'''
+
+
+class TestNameServiceInfoToXML(unittest.TestCase):
+
+    def test_to_xml_no_name_service(self):
+        '''Test SCI tool name service to_xml method - no name service'''
+        self._gen_to_xml(NameServiceInfo(
+                         domain='my.domain.com',
+                         dns_server=['1.1.1.1'], dns_search=['my.domain.com']),
+                         SAMPLE_NONE_XML)
+
+    def test_to_xml_dns(self):
+        '''Test SCI tool name service to_xml method - DNS'''
+        self._gen_to_xml(NameServiceInfo(nameservice='DNS',
+                         domain='my.domain.com',
+                         dns_server=['1.1.1.1'], dns_search=['my.domain.com']),
+                         SAMPLE_DNS_XML)
+
+    def test_to_xml_ldap(self):
+        '''Test SCI tool name service to_xml method - LDAP'''
+        self._gen_to_xml(NameServiceInfo(
+                         domain='my.domain.com',
+                         nameservice='LDAP', ldap_ip='1.1.1.1'),
+                         SAMPLE_LDAP_XML)
+
+    def test_to_xml_nis(self):
+        '''Test SCI tool name service to_xml method - NIS'''
+        self._gen_to_xml(NameServiceInfo(nameservice='NIS', nis_ip='1.1.1.1',
+                         domain='my.domain.com',
+                         nis_auto=1),
+                         SAMPLE_NIS_XML)
+
+    def _gen_to_xml(self, nsv, compare_with_this):
+        '''Compare the NameServiceInfo.to_xml() output with a baseline
+            (see above).
+        Only concerned with structure, so values are ignored.  '''
+        xml = nsv.to_xml()
+        xml_root = etree.fromstring("<root/>")
+        xml_root.extend(xml)
+        xml_str = etree.tostring(xml_root, pretty_print=True)
+        xml_lines = xml_str.splitlines()
+        compare_with = compare_with_this.splitlines()
+        for xml_line, compare_with_line in zip(xml_lines, compare_with):
+            if "<propval" in compare_with_line or \
+                    "value_node" in compare_with_line:
+                continue
+            self.assertEqual(xml_line, compare_with_line)
+
+
+if __name__ == '__main__':
+    unittest.main()
--- a/usr/src/cmd/system-config/summary.py	Wed Jun 01 09:27:41 2011 -0700
+++ b/usr/src/cmd/system-config/summary.py	Thu Jun 02 00:03:21 2011 +0200
@@ -32,10 +32,12 @@
 from solaris_install.logger import INSTALL_LOGGER_NAME
 from solaris_install.engine import InstallEngine
 from solaris_install.sysconfig import _, SCI_HELP, configure_group, \
-                                      SC_GROUP_IDENTITY, SC_GROUP_NETWORK, \
-                                      SC_GROUP_KBD, SC_GROUP_LOCATION, \
-                                      SC_GROUP_USERS
+                                      SC_GROUP_IDENTITY, SC_GROUP_KBD, \
+                                      SC_GROUP_LOCATION, SC_GROUP_NETWORK, \
+                                      SC_GROUP_NS, SC_GROUP_USERS
+from solaris_install.sysconfig.nameservice import NameService
 import solaris_install.sysconfig.profile
+from solaris_install.sysconfig.profile.nameservice_info import NameServiceInfo
 from solaris_install.sysconfig.profile.network_info import NetworkInfo
 from solaris_install.sysconfig.profile.user_info import UserInfo
 
@@ -53,36 +55,36 @@
     '''Display a summary of the SC profile that will be applied
     to the system
     '''
-    
+
     HEADER_TEXT = _("System Configuration Summary")
     PARAGRAPH = _("Review the settings below before continuing."
                   " Go back (F3) to make changes.")
-    
+
     HELP_DATA = (SCI_HELP + "/%s/summary.txt",
                  _("System Configuration Summary"))
-    
+
     INDENT = 2
-    
+
     def __init__(self, main_win):
         global LOGGER
         if LOGGER is None:
             LOGGER = logging.getLogger(INSTALL_LOGGER_NAME + ".sysconfig")
         super(SummaryScreen, self).__init__(main_win)
-    
+
     def set_actions(self):
         '''Replace the default F2_Continue with F2_Apply'''
         install_action = Action(curses.KEY_F2, _("Apply"),
                                 self.main_win.screen_list.get_next)
         self.main_win.actions[install_action.key] = install_action
-    
+
     def _show(self):
         '''Prepare a text summary from the DOC and display it
         to the user in a ScrollWindow
-        
+
         '''
         y_loc = 1
         y_loc += self.center_win.add_paragraph(SummaryScreen.PARAGRAPH, y_loc)
-        
+
         y_loc += 1
         self.sysconfig = solaris_install.sysconfig.profile.from_engine()
         summary_text = self.build_summary()
@@ -96,22 +98,22 @@
         area.columns = self.win_size_x
         scroll_region = ScrollWindow(area, window=self.center_win)
         scroll_region.add_paragraph(summary_text, start_x=SummaryScreen.INDENT)
-        
+
         self.center_win.activate_object(scroll_region)
-    
+
     def on_continue(self):
         '''Have the InstallEngine generate the manifest'''
         eng = InstallEngine.get_instance()
         (status, failed_cps) = eng.execute_checkpoints()
-        
+
         if status != InstallEngine.EXEC_SUCCESS:
             print _("Error when generating SC profile\n")
         else:
             print _("SC profile successfully generated\n")
-    
+
     def build_summary(self):
         '''Build a textual summary from solaris_install.sysconfig profile'''
-        
+
         if self.sysconfig is None:
             return ""
         else:
@@ -160,7 +162,7 @@
     def get_networks(self):
         '''Build a summary of the networks in the install_profile,
         returned as a list of strings
-        
+
         '''
         network_summary = []
 
@@ -170,6 +172,8 @@
                                    self.sysconfig.system.hostname)
 
         if not configure_group(SC_GROUP_NETWORK):
+            if configure_group(SC_GROUP_NS):
+                self._get_nameservice(network_summary)
             return network_summary
 
         nic = self.sysconfig.nic       
@@ -180,20 +184,50 @@
         else:
             network_summary.append(_("  Manual Configuration: %s")
                                    % nic.nic_name)
-            network_summary.append(_("    IP Address: %s") % nic.ip_address)
-            network_summary.append(_("    Netmask: %s") % nic.netmask)
+            network_summary.append(_("IP Address: %s") % nic.ip_address)
+            network_summary.append(_("Netmask: %s") % nic.netmask)
             if nic.gateway:
-                network_summary.append(_("    Router: %s") % nic.gateway)
-            if nic.dns_address:
-                network_summary.append(_("    DNS: %s") % nic.dns_address)
-            if nic.domain:
-                network_summary.append(_("    Domain: %s") % nic.domain)
+                network_summary.append(_("Router: %s") % nic.gateway)
+            if configure_group(SC_GROUP_NS):
+                self._get_nameservice(network_summary)
         return network_summary
 
+    def _get_nameservice(self, summary):
+        ''' Find all name services information and append to summary '''
+        if not self.sysconfig.nameservice:
+            return
+        nameservice = self.sysconfig.nameservice
+        if nameservice.nameservice and nameservice.domain:
+            summary.append(_("Domain: %s") % nameservice.domain)
+        # fetch localized name for name service
+        ns_idx = NameService.CHOICE_LIST.index(nameservice.nameservice)
+        summary.append(_("Name service: %s") %
+                NameService.USER_CHOICE_LIST[ns_idx])
+        if nameservice.nameservice == 'DNS':
+            # strip empty list entries
+            dnslist = [ln for ln in nameservice.dns_server if ln]
+            summary.append(_("DNS servers: ") + " ".join(dnslist))
+            dnslist = [ln for ln in nameservice.dns_search if ln]
+            summary.append(_("Domain list: ") + " ".join(dnslist))
+        elif nameservice.nameservice == 'LDAP':
+            summary.append(_("LDAP profile: ") + nameservice.ldap_profile)
+            summary.append(_("LDAP server's IP: ") + nameservice.ldap_ip)
+            if nameservice.ldap_proxy_bind == \
+                    NameServiceInfo.LDAP_CHOICE_PROXY_BIND:
+                summary.append(_("LDAP proxy bind distinguished name: ") +
+                               nameservice.ldap_pb_dn)
+                summary.append(_("LDAP proxy bind password: ") +
+                               nameservice.ldap_pb_psw)
+        elif nameservice.nameservice == 'NIS':
+            if nameservice.nis_auto == NameServiceInfo.NIS_CHOICE_AUTO:
+                summary.append(_("NIS server: broadcast"))
+            elif nameservice.nis_ip:
+                summary.append(_("NIS server's IP: ") + nameservice.nis_ip)
+
     def get_users(self):
         '''Build a summary of the user information, and return it as a list
         of strings
-        
+
         '''
         root = self.sysconfig.users[UserInfo.ROOT_IDX]
         primary = self.sysconfig.users[UserInfo.PRIMARY_IDX]
@@ -205,7 +239,7 @@
         else:
             user_summary.append(_("  No user account"))
         return user_summary
-    
+
     def get_tz_summary(self):
         '''Return a string summary of the timezone selection'''
         timezone = self.sysconfig.system.tz_timezone
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/system-config/test/test_nameservice.py	Thu Jun 02 00:03:21 2011 +0200
@@ -0,0 +1,162 @@
+#!/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, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''
+To run these tests, see the instructions in usr/src/tools/tests/README.
+Remember that since the proto area is used for the PYTHONPATH, the gate
+must be rebuilt for these tests to pick up any changes in the tested code.
+
+'''
+
+import unittest
+
+from solaris_install.sysconfig.nameservice import NSDomain, NSDNSServer, \
+        NSDNSSearch, NSLDAPProfile, NSLDAPProxyBindInfo, NSNISIP
+import solaris_install.sysconfig.nameservice as nsv
+from terminalui.base_screen import UIMessage
+
+
+class MockField(object):
+
+    def __init__(self, val):
+        self._val = val
+    
+    def get_text(self):
+        return self._val
+
+
+class TestNS(unittest.TestCase):
+    '''Test Name Services screens'''
+    
+    def setUp(self):
+        self.NSDomain__init__ = NSDomain.__init__ 
+        self.NSDNSServer__init__ = NSDNSServer.__init__ 
+        self.NSDNSSEARCH__init__ = NSDNSSearch.__init__ 
+        self.NSLDAPProfile__init__ = NSLDAPProfile.__init__ 
+        self.NSLDAPProxyBindInfo_init_ = NSLDAPProxyBindInfo.__init__ 
+        self.NSNISIP__init__ = NSNISIP.__init__ 
+        NSNISIP.__init__ = self.NSNISIP__init__
+        NSDomain.__init__ = lambda x, y: None
+        NSDNSServer.__init__ = lambda x, y: None
+        NSDNSSearch.__init__ = lambda x, y: None
+        NSLDAPProfile.__init__ = lambda x, y: None
+        NSLDAPProxyBindInfo.__init__ = lambda x, y: None
+        NSNISIP.__init__ = lambda x, y: None
+        self.ns_domain = NSDomain(None)
+        self.ns_dnsserver = NSDNSServer(None)
+        self.ns_dnssearch = NSDNSSearch(None)
+        self.ns_ldapprofile = NSLDAPProfile(None)
+        self.ns_ldapproxy = NSLDAPProxyBindInfo(None)
+        self.ns_nisip = NSNISIP(None)
+
+    def tearDown(self):
+        NSDomain.__init__ = self.NSDomain__init__
+        NSDNSServer.__init__ = self.NSDNSServer__init__
+        NSDNSSearch.__init__ = self.NSDNSSEARCH__init__
+        NSLDAPProfile.__init__ = self.NSLDAPProfile__init__
+        NSLDAPProxyBindInfo.__init__ = self.NSLDAPProxyBindInfo_init_
+        NSNISIP.__init__ = self.NSNISIP__init__
+
+    def test_validate_ns_validate_method(self):
+        ''' Test SCI tool name service validation method - all screens '''
+        self.ns_domain.domain = MockField('dom.ain')
+        self.assertEqual(self.ns_domain.validate(), None)
+        self.ns_domain.domain = MockField('dom ain')
+        self.assertRaises(UIMessage, self.ns_domain.validate)
+
+        self.ns_dnsserver.dns_server_list = []
+        for i in range(3):
+            self.ns_dnsserver.dns_server_list += [MockField('1.1.1.1')]
+        self.assertEqual(self.ns_dnsserver.validate(), None)
+        self.ns_dnsserver.dns_server_list[0] = MockField('1.1.1.')
+        self.assertRaises(UIMessage, self.ns_dnsserver.validate)
+
+        self.ns_dnssearch.dns_search_list = []
+        for i in range(6):
+            self.ns_dnssearch.dns_search_list += [MockField('dom.ain')]
+        self.assertEqual(self.ns_dnssearch.validate(), None)
+        self.ns_dnssearch.dns_search_list[0] = MockField('dom ain')
+        self.assertRaises(UIMessage, self.ns_dnssearch.validate)
+
+        self.ns_ldapprofile.ldap_profile = MockField('profile')
+        self.ns_ldapprofile.ldap_ip = MockField('1.1.1.1')
+        self.assertEqual(self.ns_ldapprofile.validate(), None)
+        self.ns_ldapprofile.ldap_profile = MockField('prof ile')
+        self.assertRaises(UIMessage, self.ns_ldapprofile.validate)
+
+        self.ns_ldapproxy.ldap_pb_dn = MockField('distinguishedname')
+        self.ns_ldapproxy.ldap_pb_psw = MockField('password')
+        self.assertEqual(self.ns_ldapproxy.validate(), None)
+        self.ns_ldapproxy.ldap_pb_dn = MockField('distinguished bad name')
+        self.assertRaises(UIMessage, self.ns_ldapproxy.validate)
+
+        self.ns_nisip.nis_ip = MockField('1.1.1.1')
+        self.assertEqual(self.ns_nisip.validate(), None)
+        self.ns_nisip.nis_ip = MockField('1.1.1.')
+        self.assertRaises(UIMessage, self.ns_nisip.validate)
+
+    def test_validate_ns_validation_functions(self):
+        ''' Test SCI tool name service validation functions '''
+        # LDAP profile
+        self.assertRaises(UIMessage, nsv.validate_ldap_profile, '')
+        self.assertRaises(UIMessage, nsv.validate_ldap_profile, 'bad profile')
+        self.assertRaises(UIMessage, nsv.validate_ldap_profile, 'bad"profile')
+        self.assertEqual(nsv.validate_ldap_profile('goodprofile'), None)
+        # LDAP distinguished name
+        self.assertRaises(UIMessage, nsv.validate_ldap_proxy_dn, '')
+        self.assertRaises(UIMessage, nsv.validate_ldap_proxy_dn, 'bad DN')
+        self.assertRaises(UIMessage, nsv.validate_ldap_proxy_dn, 'bad"DN')
+        self.assertEqual(nsv.validate_ldap_proxy_dn('goodDN'), None)
+        # IP address
+        self.assertRaises(UIMessage, nsv.validate_ip, '1.2.1.')
+        self.assertRaises(UIMessage, nsv.validate_ip, '  ')
+        self.assertEqual(nsv.validate_ip('1.1.1.1'), None)
+        self.assertTrue(nsv.incremental_validate_ip(MockField('1.2.3.4')))
+        # incremental IP address
+        self.assertRaises(UIMessage, nsv.incremental_validate_ip,
+                          MockField(' '))
+        self.assertRaises(UIMessage, nsv.incremental_validate_ip,
+                          MockField('x'))
+        self.assertEqual(nsv.inc_validate_nowhite_nospecial(MockField('ok')),
+                         None)
+        self.assertRaises(UIMessage, nsv.incremental_validate_ip,
+                          MockField('"'))
+        self.assertRaises(UIMessage, nsv.incremental_validate_ip,
+                          MockField(' '))
+        # domain
+        self.assertRaises(UIMessage, nsv.validate_domain, 'domain.')
+        self.assertRaises(UIMessage, nsv.validate_domain, 'domain-')
+        self.assertRaises(UIMessage, nsv.validate_domain, 'domain%')
+        self.assertEqual(nsv.validate_domain('ddd.ooo.mmm.aaa.iii.n'), None)
+        # incremental domain
+        self.assertEqual(nsv.incremental_validate_domain(MockField('a')),
+                         None)
+        self.assertRaises(UIMessage, nsv.incremental_validate_domain,
+                          MockField(' '))
+        self.assertRaises(UIMessage, nsv.incremental_validate_domain,
+                          MockField('%'))
+
+
+if __name__ == '__main__':
+        unittest.main()
--- a/usr/src/cmd/text-install/summary.py	Wed Jun 01 09:27:41 2011 -0700
+++ b/usr/src/cmd/text-install/summary.py	Thu Jun 02 00:03:21 2011 +0200
@@ -33,6 +33,8 @@
 
 from solaris_install.engine import InstallEngine
 from solaris_install.logger import INSTALL_LOGGER_NAME
+from solaris_install.sysconfig.nameservice import NameService
+from solaris_install.sysconfig.profile.nameservice_info import NameServiceInfo
 from solaris_install.sysconfig.profile.network_info import NetworkInfo
 from solaris_install.sysconfig.profile.user_info import UserInfo
 from solaris_install.target.libdiskmgt import const as libdiskmgt_const
@@ -132,6 +134,7 @@
         lines.append("")
         lines.append(_("Network:"))
         lines.extend(self.get_networks())
+        self._get_nameservice(lines)
         
         return "\n".join(lines)
     
@@ -156,12 +159,43 @@
             network_summary.append(_("    Netmask: %s") % nic.netmask)
             if nic.gateway:
                 network_summary.append(_("    Router: %s") % nic.gateway)
-            if  nic.dns_address:
-                network_summary.append(_("    DNS: %s") % nic.dns_address)
-            if nic.domain:
-                network_summary.append(_("    Domain: %s") % nic.domain)
         return network_summary
     
+    def _get_nameservice(self, ns_summary):
+        ''' Find all name services information and append to summary '''
+        if not self.sysconfig.nameservice:
+            return
+        nameservice = self.sysconfig.nameservice
+        if nameservice.nameservice and nameservice.domain:
+            ns_summary.append(_("Domain: %s") % nameservice.domain)
+        ns_idx = NameService.CHOICE_LIST.index(nameservice.nameservice)
+        ns_summary.append(_("Name service: %s") %
+                NameService.USER_CHOICE_LIST[ns_idx])
+        if nameservice.nameservice == 'DNS':
+            # strip empty list entries
+            dnslist = [ln for ln in nameservice.dns_server if ln]
+            ns_summary.append(_("DNS servers: ") + " ".join(dnslist))
+            dnslist = [ln for ln in nameservice.dns_search if ln]
+            ns_summary.append(_("Domain list: ") + " ".join(dnslist))
+        elif nameservice.nameservice == 'LDAP':
+            ns_summary.append(_("LDAP profile: ") +
+                                   nameservice.ldap_profile)
+            ns_summary.append(_("LDAP server's IP: ") +
+                                   nameservice.ldap_ip)
+            if nameservice.ldap_proxy_bind == \
+                    NameServiceInfo.LDAP_CHOICE_PROXY_BIND:
+                ns_summary.append(_(
+                                    "LDAP proxy bind distinguished name: ")
+                                    + nameservice.ldap_pb_dn)
+                ns_summary.append(_("LDAP proxy bind password: ") +
+                                       nameservice.ldap_pb_psw)
+        elif nameservice.nameservice == 'NIS':
+            if nameservice.nis_auto == NameServiceInfo.NIS_CHOICE_AUTO:
+                ns_summary.append(_("NIS server: broadcast"))
+            elif nameservice.nis_ip:
+                ns_summary.append(_("NIS server's IP: ") +
+                                       nameservice.nis_ip)
+
     def get_users(self):
         '''Build a summary of the user information, and return it as a list
         of strings
--- a/usr/src/pkg/manifests/system-install-configuration.mf	Wed Jun 01 09:27:41 2011 -0700
+++ b/usr/src/pkg/manifests/system-install-configuration.mf	Thu Jun 02 00:03:21 2011 +0200
@@ -51,6 +51,8 @@
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/network_nic_configure.pyc mode=0444
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/network_nic_select.py mode=0444
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/network_nic_select.pyc mode=0444
+file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/nameservice.py mode=0444
+file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/nameservice.pyc mode=0444
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/network_type.py mode=0444
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/network_type.pyc mode=0444
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/summary.py mode=0444
@@ -65,6 +67,8 @@
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/profile/__init__.pyc mode=0444
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/profile/ip_address.py mode=0444
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/profile/ip_address.pyc mode=0444
+file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/profile/nameservice_info.py mode=0444
+file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/profile/nameservice_info.pyc mode=0444
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/profile/network_info.py mode=0444
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/profile/network_info.pyc mode=0444
 file path=usr/lib/python2.6/vendor-packages/solaris_install/sysconfig/profile/system_info.py mode=0444