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