ich wollte hier einen Problem-Fix mit einer Anregung verbinden.
Ich hatte gerade das Problem, dass ich in einem Netzwerk den Client verteilen wollte, allerdings WINS nicht wirklich mitspielen wollte.
Ich bekam immer nur eine aberweitzige NT_STATUS* - Meldung nach der anderen (also eine gelöst, gab es direkt eine neue) - obwohl nslookup kein Problem hatte, die Hosts zu finden.
Da mir das zu blöd wurde und ein paar Tests mit winexe (die von Ubuntu) gezeigt haben, dass IP-Adresse auch funktioniert, habe ich versucht mit Hilfe der IPs zu deployen.
Leider scheint deploy-opsi-client-agent dies nicht direkt zu unterstützen.
Wenn ich z. B. zu 192.168.yy.xx deployen möchte, bekommt 'winexe' "192" als Hostname übergeben.
Ergo habe ich mir das selbst zurechtgefummelt, sodass es mit IP funktioniert (habe schlicht in den Befehlen "hn" für Hostname, gegen "ipAddress" für IP-Adresse getauscht.
Ich würde mir wünschen, dass das in zukünftigen Versionen vielleicht als Schalter verfügbar ist.
Das Deployment hat übrigens nun ohne Probleme funktioniert.
Anbei meine Version des Scriptes "opsi-deploy-client-agent.ip_address" [kann man sich einfach mit in den Ordner des opsi-client-agent legen, wenn man es nutzen möchte].
Die Stellen an denen ich bearbeitet habe, erkennt man daran, dass der Original-Befehl nochmal auskommentiert darüber oder darunter steht.
Code: Alles auswählen
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
   = = = = = = = = = = = = = = = = = =
   =   opsi-deploy-client-agent    =
   = = = = = = = = = = = = = = = = = =
   
   This tool is part of the desktop management solution opsi
   (open pc server integration) http://www.opsi.org
   
   Copyright (C) 2007-2010 uib GmbH
   
   http://www.uib.de/
   
   All rights reserved.
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 as
   published by the Free Software Foundation.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   
   @copyright:	uib GmbH <info@uib.de>
   @author: Jan Schneider <j.schneider@uib.de>
   @license: GNU General Public License version 2
"""
__version__ = '4.0.1.4'
import sys, os, socket, time, gettext, getopt, getpass, re, threading, inspect
# OPSI imports
from OPSI.Logger import *
from OPSI.System import *
from OPSI.Util import *
from OPSI.Util.File import IniFile
from OPSI.Object import *
from OPSI.Types import *
from OPSI.Backend.BackendManager import BackendManager
logger = Logger()
logLevel = LOG_WARNING
logger.setConsoleLevel(logLevel)
logger.setLogFormat('[%L] %M')
logger.setConsoleColor(True)
# Get locale
try:
	t = gettext.translation('opsi-deploy-client-agent', LOCALE_DIR)
	_ = t.ugettext
except Exception, e:
	#logger.error("Locale not found: %s" % e)
	def _(string):
		"""Dummy method, created and called when no locale is found.
		Uses the fallback language (called C; means english) then."""
		return string
def usage():
	print _(u"\nUsage: %s [options] [host]...") % os.path.basename(sys.argv[0])
	print _(u"Deploy opsi client agent to the specified clients.")
	print _(u"The c$ and admin$ must be accessable on every client.")
	print _(u"Simple File Sharing (Folder Options) should be disabled on the Windows machine.")
	print _(u"Options:")
	print _(u"    -h        show this help text")
	print _(u"    -V        show version information")
	print _(u"    -v        increase verbosity (can be used multiple times)")
	print _(u"    -u        username for authentication (default: Administrator)")
	print _(u"              example for a domain account: -u \"<DOMAIN>\\\\<username>\"")
	print _(u"    -p        password for authentication")
	print _(u"    -c        use fqdn instead of hostname for smb/cifs connection")
	print _(u"    -x        try installation even if ping fails")
	print _(u"    -r        reboot computer after installation")
	print _(u"    -s        shutdown computer after installation")
	print _(u"    -o        start opsiclientd service after installation")
	print _(u"    -f        file containing list of clients (one hostname per line)")
	print _(u"    -S        skip known opsi clients")
	print _(u"    -t        number of concurrent deployment threads (default: 1)")
	print ""
def winexe(cmd, host, username, password):
	cmd      = forceUnicode(cmd)
	host     = forceUnicode(host)
	username = forceUnicode(username)
	password = forceUnicode(password)
	
	match = re.search('^([^\\\\]+)\\\\+([^\\\\]+)$', username)
	if match:
		username = match.group(1) + u'\\' + match.group(2)
	winexe = './winexe'
	try:
		print _(u"Using system's winexe.")
		winexe = which('winexe')
	except:
		print _(u"Using own winexe.")
		pass
	return execute( u"%s -U '%s' //%s '%s'" % (winexe, username + '%' + password.replace("'", "'\"'\"'"), host, cmd) )
class DeployThread(threading.Thread):
	def __init__(self, host, backend, username, password, shutdown, reboot, startService, useNetbios, stopOnPingFailure, skipExistingClient):
		threading.Thread.__init__(self)
		self.host = host
		self.backend = backend
		self.username = username
		self.password = password
		self.shutdown = shutdown
		self.reboot = reboot
		self.startService = startService
		self.useNetbios = useNetbios
		self.stopOnPingFailure = stopOnPingFailure
		self.skipExistingClient = skipExistingClient
		
	def run(self):
		host = forceUnicodeLower(self.host)
		hostId = u''
		hostObj = None
		hostCreated = False
		try:
			hostName = host.split('.')[0]
			if (host.count(u'.') < 2):
				hostId = forceHostId(host.replace('_', '-') + u'.' + u'.'.join(getFQDN().split(u'.')[1:]))
			else:
				hostId = forceHostId(host.replace('_', '-'))
			
			if self.backend.host_getIdents(type = 'OpsiClient', id = hostId) and self.skipExistingClient:
				logger.notice(u"Skipping host '%s'" % hostId)
				return
			
			logger.notice(u"Starting deployment to host '%s'" % hostId)
			
			logger.notice(u"Querying for ip address of host '%s'" % hostId)
			ipAddress = u''
			logger.info(u"Getting host '%s' by name" % hostId)
			try:
				ipAddress = socket.gethostbyname(hostId)
			except Exception, e:
				logger.warning(u"Failed to get ip address for host '%s' by syscall: %s" % (hostId, e))
			
			if ipAddress:
				logger.notice(u"Got ip address '%s' from syscall" % ipAddress)
			else:
				logger.info(u"Executing 'nslookup %s#20'" % hostName)
				for line in execute(u"nmblookup %s#20" % hostName):
					match = re.search("^(\d+\.\d+\.\d+\.\d+)\s+%s<20>" % hostName, line, re.IGNORECASE)
					if match:
						ipAddress = match.group(1)
						break
				if ipAddress:
					logger.notice(u"Got ip address '%s' from netbios lookup" % ipAddress)
				else:
					raise Exception(u"Failed to get ip address for host '%s'" % hostName)
			
			logger.notice(u"Pinging host %s ..." % ipAddress)
			alive = False
			try:
				for line in execute(u"ping -q -c2 %s" % ipAddress):
					match = re.search("\s+(\d+)%\s+packet\s+loss", line)
					if match and (forceInt(match.group(1)) < 100):
						alive = True
			except Exception, e:
				logger.error(e)
			if alive:
				logger.notice(u"Host %s is up" % ipAddress)
			elif self.stopOnPingFailure:
				raise Exception(u"No ping response received from %s" % ipAddress)
			else:
				logger.warning(u"No ping response received from %s" % ipAddress)
			
			logger.notice(u"Getting hardware ethernet address of host '%s'" % hostId)
			mac = u''
			f = open("/proc/net/arp")
			for line in f.readlines():
				line = line.strip()
				if not line:
					continue
				if (line.split()[0] == ipAddress):
					mac = line.split()[3].lower()
					break
			f.close()
			
			if not mac or (mac == u'00:00:00:00:00:00'):
				logger.warning(u"Failed to get hardware ethernet address for host '%s'" % hostName)
				mac = u''
			else:
				logger.notice(u"Found hardware ethernet address '%s'" % mac)
			
			if not self.backend.host_getIdents(type = 'OpsiClient', id = hostId):
				logger.notice(u"Creating client '%s'" % hostId)
				
				hostObj = OpsiClient(
					id              = hostId,
					hardwareAddress = mac,
					ipAddress       = ipAddress,
					description     = u"",
					notes           = u"Created by opsi-deploy-client-agent at %s" \
								% time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
				)
				self.backend.host_createObjects([hostObj])
				hostCreated = True
			hostObj = self.backend.host_getObjects(type = 'OpsiClient', id = hostId)[0]
			
			hn = hostId
			if self.useNetbios:
				hn = hostName
			
			logger.notice(u"Testing winexe")
			cmd = u'cmd.exe /C "del /s /q c:\\tmp\\opsi-client-agent_inst && rmdir /s /q c:\\tmp\\opsi-client-agent_inst || echo not found"'
			for trynum in (1, 2):
				try:
					
					#winexe(cmd, hn, self.username, self.password)
					winexe(cmd, ipAddress, self.username, self.password)
					break
				except Exception, e:
					if (trynum == 2):
						raise Exception(u"Failed to execute command on host '%s': winexe error: %s" % (hn, e))
					logger.info(u"Winexe failure '%s', retrying" % e)
					time.sleep(2)
			
			logger.notice(u"Patching config.ini")
			configIniName = u'%s_config.ini' % randomString(10)
			copy(os.path.join(u'files', u'opsi', u'cfg', u'config.ini'), '/tmp/%s' % configIniName)
			configFile = IniFile('/tmp/%s' % configIniName)
			config = configFile.parse()
			if not config.has_section('shareinfo'):
				config.add_section('shareinfo')
			config.set('shareinfo', 'pckey', hostObj.opsiHostKey)
			if not config.has_section('general'):
				config.add_section('general')
			config.set('general', 'dnsdomain', u'.'.join(hostObj.id.split('.')[1:]))
			configFile.generate(config)
			
			try:
				logger.notice(u"Copying installation files")
				cmd = u"%s //%s/c$ -U '%s' -c 'prompt; recurse; md tmp; cd tmp; md opsi-client-agent_inst; cd opsi-client-agent_inst; mput files; mput utils; cd files\\opsi\\cfg; lcd /tmp; put %s config.ini; exit;'" \
					% (which('smbclient'), ipAddress, self.username + '%' + self.password.replace("'", "'\"'\"'"), configIniName)
					#% (which('smbclient'), hn, self.username + '%' + self.password.replace("'", "'\"'\"'"), configIniName)
				execute(cmd)
				
				logger.notice(u"Installing opsi-client-agent")
				cmd = u'c:\\tmp\\opsi-client-agent_inst\\files\\opsi\\opsi-winst\\winst32.exe /batch c:\\tmp\\opsi-client-agent_inst\\files\\opsi\\setup.ins c:\\tmp\\opsi-client-agent.log /PARAMETER REMOTEDEPLOY'
				for trynum in (1, 2):
					try:
						#winexe(cmd, hn, self.username, self.password)
						winexe(cmd, ipAddress, self.username, self.password)
						break
					except Exception, e:
						if (trynum == 2):
							raise Exception(u"Failed to install opsi-client-agent: %s" % e)
						logger.info(u"Winexe failure '%s', retrying" % e)
						time.sleep(2)
			finally:
				os.remove('/tmp/%s' % configIniName)
				
				try:
					cmd = u'cmd.exe /C "del /s /q c:\\tmp\\opsi-client-agent_inst && rmdir /s /q c:\\tmp\\opsi-client-agent_inst"'
					#winexe(cmd, hn, self.username, self.password)
					winexe(cmd, ipAddress, self.username, self.password)
				except Exception, e:
					logger.error(e)
			
			logger.notice(u"opsi-client-agent successfully installed on '%s'" % hostId)
			self.backend.productOnClient_updateObjects([
					ProductOnClient(
						productType        = u'LocalbootProduct',
						clientId           = hostId,
						productId          = u'opsi-client-agent',
						installationStatus = u'installed',
						actionResult       = u'successful'
					)
				])
			
			if self.reboot or self.shutdown:
				cmd = u''
				if self.reboot:
					logger.notice(u"Rebooting machine '%s'" % hostId)
					cmd = u'"%ProgramFiles%\\opsi.org\\opsi-client-agent\\utilities\\shutdown.exe" /L /R /T:20 "opsi-client-agent installed - reboot" /Y /C'
				elif self.shutdown:
					logger.notice(u"Shutting down machine '%s'" % hostId)
					cmd = u'"%ProgramFiles%\\opsi.org\\opsi-client-agent\\utilities\\shutdown.exe" /L /T:20 "opsi-client-agent installed - shutdown" /Y /C'
				try:
					pf = None
					for const in ('%ProgramFiles(x86)%', '%ProgramFiles%'):
						lines = []
						try:
							#lines = winexe(u'cmd.exe /C "echo %s"' % const, hn, self.username, self.password)
							lines = winexe(u'cmd.exe /C "echo %s"' % const, ipAddress, self.username, self.password)
						except Exception, e:
							logger.warning(e)
							continue
						for line in lines:
							line = line.strip()
							if (line.find('unavailable') != -1):
								continue
							pf = line
						if pf and (pf != const):
							break
						pf = None
					if not pf:
						raise Exception(u"Failed to get program files path")
					logger.info(u"Program files path is '%s'" % pf)
					#winexe(cmd.replace(u'%ProgramFiles%', pf), hn, self.username, self.password)
					winexe(cmd.replace(u'%ProgramFiles%', pf), ipAddress, self.username, self.password)
				except Exception, e:
					if self.reboot:
						logger.error(u"Failed to reboot computer: %s" % e)
					else:
						logger.error(u"Failed to shutdown computer: %s" % e)
			elif self.startService:
				try:
					#winexe(u'net start opsiclientd', hn, self.username, self.password)
					winexe(u'net start opsiclientd', ipAddress, self.username, self.password)
				except Exception, e:
					logger.error(u"Failed to start opsiclientd: %s" % e)
				
		except Exception, e:
			logger.error(u"Deployment to '%s' failed: %s" % (hostId, e))
			if hostObj and hostCreated:
				try:
					self.backend.host_deleteObjects([hostObj])
				except Exception, e2:
					logger.error(e2)
		
def main(argv):
	global logLevel
	hosts = []
	hostFile = None
	username = u''
	password = u''
	shutdown = False
	reboot = False
	startService = False
	useNetbios = True
	stopOnPingFailure = True
	skipExistingClient = False
	maxThreads = 1
	
	# Get options
	try:
		(opts, args) = getopt.getopt(argv, "hVvcxsrou:p:f:St:")
	
	except getopt.GetoptError:
		usage()
		sys.exit(1)
	
	for (opt, arg) in opts:
		if   (opt == "-h"):
			usage()
			return
		elif (opt == "-V"):
			print __version__
			return
		elif (opt == "-v"):
			logLevel += 1
			logger.setConsoleLevel(logLevel)
		elif (opt == "-p"):
			password = arg
		elif (opt == "-u"):
			username = arg
		elif (opt == "-r"):
			reboot = True
		elif (opt == "-s"):
			shutdown = True
		elif (opt == "-o"):
			startService = True
		elif (opt == "-c"):
			useNetbios = False
		elif (opt == "-x"):
			stopOnPingFailure = False
		elif (opt == "-S"):
			skipExistingClient = True
		elif (opt == "-f"):
			hostFile = arg
			if not os.path.isfile(hostFile):
				raise Exception(u"File '%s' not found!" % hostFile)
		elif (opt == "-t"):
			maxThreads = forceInt(arg)
			if (maxThreads < 1):
				maxThreads = 1
			
	if hostFile:
		f = open(hostFile)
		for line in f.readlines():
			line = line.strip()
			if not line or line.startswith('#') or line.startswith(';'):
				continue
			hosts.append(forceUnicodeLower(line))
		f.close()
		
	elif (len(args) > 0):
		hosts = args
	
	else:
		raise Exception("No hosts given.")
	
	if not username:
		username = u'Administrator'
	
	if not password:
		password = forceUnicode(getpass.getpass())
		if not password:
			raise Exception("No password given.")
		
	logger.addConfidentialString(password)
	
	# Create BackendManager
	backend = BackendManager(
		dispatchConfigFile = u'/etc/opsi/backendManager/dispatch.conf',
		backendConfigDir   = u'/etc/opsi/backends',
		extend             = True,
		depotbackend       = False,
		hostControlBackend = False
	)
	runningThreads = []
	while hosts:
		while (len(runningThreads) > maxThreads):
			time.sleep(1)
			for t in runningThreads:
				if t.isAlive():
					continue
				runningThreads.remove(t)
				break
		host = hosts.pop()
		t = DeployThread(host, backend, username, password, shutdown, reboot, startService, useNetbios, stopOnPingFailure, skipExistingClient)
		t.start()
		runningThreads.append(t)
		time.sleep(0.5)
		
	for t in runningThreads:
		if t.isAlive():
			t.join()
	time.sleep(1)	
		
if (__name__ == "__main__"):
	
	try:
		os.chdir( os.path.dirname(os.path.abspath(sys.argv[0])) )
		main(sys.argv[1:])
		
	except SystemExit:
		pass
		
	except Exception, e:
		logger.setConsoleLevel(LOG_ERROR)
		logger.logException(e)
		print >> sys.stderr, u"ERROR: %s" % forceUnicode(e)
		sys.exit(1)
	
	sys.exit(0)


