--- a/usr/src/Makefile.master Tue Oct 07 14:28:42 2008 -0600
+++ b/usr/src/Makefile.master Wed Oct 08 10:13:32 2008 +0200
@@ -130,6 +130,7 @@
ROOTPYTHONVENDORINSTALL= $(ROOTPYTHONVENDOR)/osol_install
ROOTPYTHONVENDORINSTALLBEADM= $(ROOTPYTHONVENDORINSTALL)/beadm
ROOTPYTHONVENDORINSTALLDC= $(ROOTPYTHONVENDORINSTALL)/distro_const
+ROOTPYTHONVENDORINSTALLAI= $(ROOTPYTHONVENDORINSTALL)/auto_install
ROOTDC= $(ROOT)/usr/share/distro_const
ROOTDC_SLIM= $(ROOTDC)/slim_cd
ROOTDC_AI= $(ROOTDC)/auto_install
--- a/usr/src/Targetdirs Tue Oct 07 14:28:42 2008 -0600
+++ b/usr/src/Targetdirs Wed Oct 08 10:13:32 2008 +0200
@@ -86,6 +86,7 @@
/usr/lib/python2.4/vendor-packages/osol_install \
/usr/lib/python2.4/vendor-packages/osol_install/beadm \
/usr/lib/python2.4/vendor-packages/osol_install/distro_const \
+ /usr/lib/python2.4/vendor-packages/osol_install/auto_install \
/usr/dt \
/usr/dt/bin \
/usr/dt/appconfig \
--- a/usr/src/cmd/Makefile.targ Tue Oct 07 14:28:42 2008 -0600
+++ b/usr/src/cmd/Makefile.targ Wed Oct 08 10:13:32 2008 +0200
@@ -111,6 +111,9 @@
$(ROOTPYTHONVENDORINSTALLDC):
$(INS.dir)
+$(ROOTPYTHONVENDORINSTALLAI):
+ $(INS.dir)
+
#
# Python .py and .pyc files need to be installed with the original
# timestamp of the file preserved. Otherwise, .pyc files will
@@ -127,6 +130,9 @@
$(ROOTPYTHONVENDORINSTALLDC)/%: %
$(CP_P.file)
+$(ROOTPYTHONVENDORINSTALLAI)/%: %
+ $(CP_P.file)
+
$(ROOTDC):
$(INS.dir)
--- a/usr/src/cmd/auto-install/Makefile Tue Oct 07 14:28:42 2008 -0600
+++ b/usr/src/cmd/auto-install/Makefile Wed Oct 08 10:13:32 2008 +0200
@@ -31,9 +31,9 @@
clobber:= TARGET= clobber
install:= TARGET= install
-PROGS= ai_get_manifest
+PROGS= ai_get_manifest ai_sd
-PYMODULES=
+PYMODULES= __init__.py
PYCMODULES= $(PYMODULES:%.py=%.pyc)
@@ -63,6 +63,10 @@
$(CP) ai_get_manifest.py ai_get_manifest
$(CHMOD) 755 ai_get_manifest
+ai_sd: ai_sd.py
+ $(CP) ai_sd.py ai_sd
+ $(CHMOD) 755 ai_sd
+
install_h:
include ../Makefile.targ
--- a/usr/src/cmd/auto-install/README.test Tue Oct 07 14:28:42 2008 -0600
+++ b/usr/src/cmd/auto-install/README.test Wed Oct 08 10:13:32 2008 +0200
@@ -75,3 +75,66 @@
# ./ai_get_manifest -s <service_list> -o <manifest> -d 1-4
where '-d 4' enables the most verbose mode
+
+AI service discovery engine (AISD)
+==================================
+Name of executable: /usr/bin/ai_sd
+Package: SUNWauto-install
+Implementation: Python script
+
+Purpose: Tries to look up AI service which could provide AI and System Configuration
+(SC) combined manifest valid for the client. Please refer to AI design specification
+for more details - it can be obtained from Mercurial workspace
+ssh://[email protected]/hg/caiman/caiman-docs - AI/ai_design_doc.odt file
+
+Usage:
+ai_sd [-s service_type] [-n service_name] [-t timeout] [-d debug_level] [-h] -o discovered_services_file
+
+Input: Service name to look up, timeout
+Output: List of AI server providing the service in format <address>:<port>
+
+[1] Test of looking up 'named' service
+--------------------------------------
+* Prerequisites:
+ -none
+
+* Test procedure
+[a] Publish service '_test_service' using dns-sd(1M) command
+$ dns-sd -R _test_service _OSInstall._tcp local 8081 aiwebserver=tio:8081
+
+[b] Run AISD to look up the service '_test-service'
+$ ai_sd -n _test_service -o <service_list>
+
+* Expected output
+<service_list> file containing address of AI server in form
+'tio:8081'
+
+* Return codes
+0 - success
+2 - service not found
+
+[2] Test of looking up 'default' service
+----------------------------------------
+* Prerequisites:
+ -none
+
+* Test procedure
+[a] Publish service '_default' using dns-sd(1M) command
+$ dns-sd -R _default _OSInstall._tcp local 8081 aiwebserver=tio:8081
+
+[b] Run AISD to look up the service '_default'
+$ ai_sd -o <service_list>
+
+* Expected output
+<service_list> file containing address of AI server in form
+'tio:8081'
+
+* Return codes
+0 - success
+2 - service not found
+
+[3] Run with increased debug verbosity
+--------------------------------------
+$ ai_sd -n <service_name> -o <service_list> -d 1-4
+
+where '-d 4' enables the most verbose mode
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/auto-install/__init__.py Wed Oct 08 10:13:32 2008 +0200
@@ -0,0 +1,32 @@
+#!/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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#
+# This file is installed into
+# usr/lib/python2.4/vendor-packages/osol_install/auto_install/ directory
+# and lets the Python interpreter know that this directory contains valid
+# Python modules which can be imported using following command:
+# from osol_install.auto_install.<module_name> import <object>
+#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/auto-install/ai_sd.py Wed Oct 08 10:13:32 2008 +0200
@@ -0,0 +1,428 @@
+#!/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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ai_sd - AI Service Discovery Engine
+#
+
+import getopt
+import os
+import re
+import signal
+import string
+import sys
+import traceback
+from subprocess import *
+from osol_install.auto_install.ai_get_manifest import ai_log
+
+#
+# ID of process running dns-sd(1M) command. It will be used by os.kill()
+# to terminate the process. This is global variable for now, so only one
+# dns-sd(1M) command can be run at a time.
+#
+# TODO: Needs to be modified if it turns out, that it would be usefull
+# to have more dns-sd(1M) running at once
+#
+sd_pid = -1
+
+#
+# Flag which indicates that timer expired for running dns-sd(1M)
+# This is global variable for now, so only one dns-sd(1M)
+# command can be run at a time.
+#
+# TODO: Needs to be modified if it turns out, that it would be usefull
+# to have more dns-sd(1M) running at once
+#
+sd_timeout_expired = False
+
+#
+# AI service discovery logging service
+#
+aisd_log = ai_log("AISD")
+
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def svc_lookup_timeout_handler(signum, frame):
+ global sd_pid
+ global sd_timeout_expired
+
+ """ Function: svc_lookup_timeout_handler()
+
+ Description:
+ Handles raising of SIGALRM signal - kill the process
+ with ID saved in sd_pid by sending SIGTERM signal
+
+ Parameters:
+ signum - number of signal
+ frame - current stack frame
+
+ Returns:
+
+ """
+
+ if sd_pid >= 0:
+ os.kill(sd_pid, signal.SIGTERM)
+
+ # set the flag and disable the alarm
+ sd_timeout_expired = True
+ signal.alarm(0)
+
+#
+# ai_service class - base class for holding/manipulating AI service
+#
+class ai_service:
+ # service type
+ type = '_OSInstall._tcp'
+
+ def __init__(self, name = "_default", timeout = 5, domain = "local"):
+ """ Metod: __init__
+
+ Parameters:
+ name - name of the service instance
+ timeout - max time to lookup the service
+ domain - .local for multicast DNS
+
+ Returns:
+ True..service found, False..service not found
+
+ """
+ self.name = name
+ self.timeout = timeout
+ self.domain = domain
+ self.found = False
+ self.svc_info = self.svc_txt_rec = None
+ return
+
+ def found(self):
+ """ Metod: found
+
+ Returns:
+ True..service found, False..service not found
+
+ """
+ return self.found
+
+ def get_txt_rec(self):
+ """ Metod: get_txt_rec
+
+ Returns:
+ Service TXT record
+
+ """
+ return self.svc_txt_rec
+
+ def lookup(self):
+ global sd_pid
+ global sd_timeout_expired
+
+ """ Metod: lookup
+
+ Description:
+ Tries to look up service instance
+
+ Returns:
+ 0..service found, -1..service not found
+
+ """
+
+ #
+ # discard the information we obtained so far
+ # and start new look up process
+ #
+ self.svc_info = self.svc_txt_rec = None
+ self.found = False
+
+ # dns-sd(1M) is used for look up the service
+ cmd = "/usr/bin/dns-sd -L %s %s %s" % (self.name, \
+ self.__class__.type, self.domain)
+
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, "cmd: %s", cmd)
+
+ cmd_args = cmd.split()
+
+ #
+ # spawn new process which will take care of looking up
+ # the service. If the service is not found within specified
+ # time, raise the SIGALRM signal and kill the process
+ #
+ try:
+ cmd_popen = Popen(cmd_args, stdout=PIPE, stderr=PIPE)
+
+ except OSError:
+ aisd_log.post(ai_log.AI_DBGLVL_ERR, \
+ "Popen() raised OSError exception")
+
+ return -1
+
+ except ValueError:
+ aisd_log.post(ai_log.AI_DBGLVL_ERR, \
+ "Popen() raised ValueError exception")
+
+ return -1
+
+ #
+ # save ID of new process - os.kill() will use it later
+ # to terminate the process
+ #
+ sd_pid = cmd_popen.pid
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ "dns-sd pid: %d", sd_pid)
+
+ signal.signal(signal.SIGALRM, svc_lookup_timeout_handler)
+
+ # activate timeout
+ sd_timeout_expired = False
+ signal.alarm(self.timeout)
+
+ #
+ # Save file descriptors for stdout, stderr
+ # from dns-sd for later purposes
+ #
+ cmd_stdout_fd = cmd_popen.stdout
+ cmd_stderr = None
+
+ svc_info = svc_txt_rec = None
+ svc_info_found = svc_txt_rec_found = False
+
+ #
+ # process output from dns-sd(1M) until either service
+ # is found or timeout expires
+ #
+ while cmd_popen.poll() == None and not sd_timeout_expired:
+ try:
+ # read main service info - wait for the line
+ # containing service name
+ line = cmd_stdout_fd.readline().strip()
+
+ # search for exact match of given service name
+ svc = line.split()
+ if len(svc) > 1 and re.search( \
+ "^%s." % self.name, svc[1].strip()):
+
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ " Svc: %s", line)
+
+ svc_info_found = True
+ svc_info = line
+ svc_txt_rec_found = False
+ continue
+
+ #
+ # read and verify TXT records -
+ # following format is expected:
+ #
+ # aiwebserver=<address>:<port>
+ #
+ if re.search(r"^aiwebserver=", line):
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ " TXT: %s", line)
+ svc_txt_rec = line
+ svc_txt_rec_found = True
+ else:
+ svc_info_found = \
+ svc_txt_rec_found = False
+
+ #
+ # Abort look up process, if service was found
+ #
+
+ if svc_info_found and svc_txt_rec_found:
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ " %s service found", self.name)
+ break;
+
+ #
+ # IOError exception is raised when the process
+ # is terminated during I/O operation. This will
+ # happen if service can't be found, since in this case
+ # we would be waiting in cmd_stdout_fd.readline()
+ #
+ except IOError:
+ # wait for process to finish
+ (cmd_stdout, cmd_stderr) = \
+ cmd_popen.communicate()
+
+ if sd_timeout_expired:
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ "Timeout expired, dns-sd (pid=%d)" \
+ " terminated", sd_pid)
+
+ else:
+ aisd_log.post(ai_log.AI_DBGLVL_WARN, \
+ "IOError raised for unknown reason")
+
+ # disable alarm
+ signal.alarm(0)
+
+ #
+ # if the process didn't finish yet, it is either
+ # still running or in the phase of finishing its job.
+ #
+ if cmd_popen.poll() == None:
+ # If process is running, terminate it
+ if not sd_timeout_expired:
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ "dns-sd (pid=%d) is running, will be " \
+ "terminated", sd_pid)
+
+ os.kill(sd_pid, signal.SIGTERM)
+
+ if cmd_popen.poll() != None:
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ "dns-sd return code: %d", cmd_popen.returncode)
+
+ # capture output of stderr for debugging purposes
+ if cmd_stderr != None and cmd_stderr != "":
+ aisd_log.post(ai_log.AI_DBGLVL_WARN, \
+ " stderr: %s", cmd_stderr)
+
+ if svc_info_found and svc_txt_rec_found:
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ "Valid service found:\n svc: %s\n TXT: %s", \
+ svc_info, svc_txt_rec)
+
+ self.found = True
+ self.svc_info = svc_info
+ self.svc_txt_rec = svc_txt_rec
+ return 0
+
+ return -1
+
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def usage():
+ print >> sys.stderr, ("Usage:\n" \
+ " ai_sd -s service type -n service_name " \
+ "-o discovered_services_file -t timeout [-d debug_level] [-h]")
+ sys.exit(1)
+
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def parse_cli(cli_opts_args):
+ rc = 0;
+
+ if len(cli_opts_args) == 0:
+ usage()
+
+ opts_args = cli_opts_args[1:]
+
+ try:
+ opts, args = getopt.getopt(opts_args, "t:s:n:o:d:h")
+ except getopt.GetoptError:
+ aisd_log.post(ai_log.AI_DBGLVL_ERR, \
+ "Invalid options or arguments provided")
+ usage()
+
+ service_name = ""
+ service_lookup_timeout = 5
+
+ service_file = "/tmp/service_list"
+
+ for o, a in opts:
+ if o == "-s":
+ ai_service.type = a
+ if o == "-n":
+ service_name = a
+ elif o == "-o":
+ service_file = a
+ elif o == "-t":
+ service_lookup_timeout = int(a)
+ elif o == "-d":
+ aisd_log.set_debug_level(int(a))
+ elif o == "-h":
+ usage()
+
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ "Service file: %s", service_file)
+
+ service_list = []
+
+ #
+ # if service name was specified, add it to the list
+ # of services to be looked up
+ #
+ if service_name != "":
+ service_list.append(ai_service(service_name, \
+ service_lookup_timeout))
+
+ # add default service
+ service_list.append(ai_service('_default', service_lookup_timeout))
+
+ # Go through the list of services and try to look up them
+ for i in range(len(service_list)):
+ svc_instance = "%s.%s.%s" % (service_list[i].name, \
+ ai_service.type, service_list[i].domain)
+
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ "Service to look up: %s", svc_instance)
+
+ # look up the service
+ ret = service_list[i].lookup()
+ if ret == 0:
+ break;
+
+ if ret == -1:
+ aisd_log.post(ai_log.AI_DBGLVL_ERR, \
+ "No valid AI service found")
+ return 2
+
+ #
+ # parse information captured from dns-sd in order
+ # to obtain source of service (address and port)
+ # extract value from 'aiwebserver' name-value pair
+ #
+ svc_address = service_list[i].get_txt_rec(). \
+ strip().split('aiwebserver=', 1)[1].split(',')[0]
+ (svc_address, svc_port) = svc_address.split(':')
+
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ "%s can be reached at: %s:%s", svc_instance, \
+ svc_address, svc_port)
+
+ # write the information to the given location
+ aisd_log.post(ai_log.AI_DBGLVL_INFO, \
+ "Storing service list into %s", service_file)
+
+ try:
+ fh_svc_list = open(service_file, 'w')
+ except IOError:
+ aisd_log.post(ai_log.AI_DBGLVL_ERR, \
+ "Couldn't open %s for saving service list", service_file)
+ return 2
+
+ fh_svc_list.write("%s:%s\n" % (svc_address, svc_port))
+ fh_svc_list.close()
+
+ return 0
+
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+if __name__ == "__main__":
+ try:
+ rc = parse_cli(sys.argv)
+ except SystemExit, e:
+ raise e
+ except:
+ traceback.print_exc()
+ sys.exit(1)
+ sys.exit(rc)
--- a/usr/src/pkgdefs/SUNWauto-install/prototype_com Tue Oct 07 14:28:42 2008 -0600
+++ b/usr/src/pkgdefs/SUNWauto-install/prototype_com Wed Oct 08 10:13:32 2008 +0200
@@ -36,3 +36,16 @@
d none usr 755 root sys
d none usr/bin 755 root bin
f none usr/bin/ai_get_manifest 555 root bin
+f none usr/bin/ai_sd 555 root bin
+
+#
+# Automated Installer python modules
+#
+d none usr/lib 0755 root bin
+d none usr/lib/python2.4 0755 root bin
+d none usr/lib/python2.4/vendor-packages 0755 root bin
+d none usr/lib/python2.4/vendor-packages/osol_install 0755 root bin
+d none usr/lib/python2.4/vendor-packages/osol_install/auto_install 0755 root bin
+f none usr/lib/python2.4/vendor-packages/osol_install/auto_install/__init__.py 0444 root bin
+f none usr/lib/python2.4/vendor-packages/osol_install/auto_install/__init__.pyc 0444 root bin
+s none usr/lib/python2.4/vendor-packages/osol_install/auto_install/ai_get_manifest.py=../../../../../bin/ai_get_manifest