Seite 1 von 1

opsi-deploy-client-agent :: IP-Address statt Hostname

Verfasst: 27 Sep 2013, 11:42
von r.witzel
Hallo zusammen,

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)

Re: opsi-deploy-client-agent :: IP-Address statt Hostname

Verfasst: 30 Sep 2013, 15:35
von n.wenselowski
Hallo r.witzel,

opsi hat die Möglichkeit zur Kommunikation mit dem Client bevorzugt die IP zu verwenden.
Dazu gibt es die Option resolveHostAddress - siehe Handbuch.
Zur Konfiguration der Namensauflösung gibt es auch im Getting Started ein Kapitel.

Haben diese Lösungen nicht zum Ziel geführt?


Mit freundlichen Grüßen

N. Wenselowski

Re: opsi-deploy-client-agent :: IP-Address statt Hostname

Verfasst: 30 Sep 2013, 16:53
von r.witzel
Hi,

leider nicht.
Diese beiden Optionen habe ich ausprobiert.
Für den OpsiConfD sind diese auch nutzbar.
Bei dem Deployment-Script handelt es sich aber [glaube ich vom Quelltext geschlossen zu haben] um ein völlig eigenständiges Produkt, das am Ende nur OPSI die Daten zuspielt.

Der Getting-Started-Guide hat leider nicht [weiter] geholfen, weil der Server nach Anleitung funktioniert.

Namensauflösung via DNS ist übrigens überhaupt gar kein Problem - dort findet er jeden und alles :)
Das Problem ist, dass die Winexe wohl mit nmblookup/WINS-Lookups zu arbeiten scheint, wenn es einen Hostnamen bekommt.
Leider konnte ich den Server beim besten Willen nicht überzeugen, das WINS-Spiel mitzuspielen (bin quasi alle entsprechenden Samba-Optionen hoch und herunter gelaufen und habe Samba, nmbd und winbind nach jedem Schritt neu gestartet, nsswitch durchgetestet, usw.). Das ganze ist ein MS-Domain-Netzwerk und der Server ein 12.04 Ubuntu.

Der schnellere Weg war hier, das Script NSlookup-IP-tauglich zu machen und auf diesem Weg zu deployen.

Ich suche auch nicht mehr nach einer Lösung für das WINS-Problem [dafür nervt es mich auch zu sehr und ich hätte es am liebsten garkein WINS mehr] - sondern wollte nur liebe Augen machen, dass [da ich das schon öfter brauchte - nicht nur wegen WINS] man vllt. generell eine offizielle IP-Unterstützung im Deployment Script bekommt :).

Danke aber für die Nachfrage.

Beste Grüße

Rene Witzel

Re: opsi-deploy-client-agent :: IP-Address statt Hostname

Verfasst: 04 Okt 2013, 13:58
von n.wenselowski
Hallo r.witzel,

ich habe in unserem internen Bugtracker ein entsprechendes Ticket aufgemacht, dass eine Verwendung des Scripts auch nur mit IP möglich ist.


Gruß

N. Wenselowski

Re: opsi-deploy-client-agent :: IP-Address statt Hostname

Verfasst: 04 Okt 2013, 14:41
von r.witzel
Danke :)

Re: opsi-deploy-client-agent :: IP-Address statt Hostname

Verfasst: 25 Mär 2014, 12:51
von r.witzel
Ich wollte das Ding hier noch mal ausgraben und mein aktuelles Script zur Verfügung stellen.

Bei diesem Script gibt es nun die Option "-i", die es erlaubt eine IP-Adresse hinzuzufügen, die dann anstelle des Hostnamens an den relevanten Stellen verwendet wird (quasi als force-override).
Beim unveränderten Original-Script hatte ich das Problem, dass ein Client mit der IPAdresse als Namen erstellt wurde und das obige Script gefiel mir nicht mehr so.
Deswegen habe ich das hier noch mal umgemodelt.

[1] Achtet bitte auf die Version - ich glaube die ist nicht mehr topaktuell - aber es funktioniert :P
[2] Beim Einsatz von "-v", teilt das Script mit, an welchen Stellen der Hostname durch die IP ersetzt wird

Aufruf:

Code: Alles auswählen

./opsi-deploy-client-agent -v -u "domain\administrator" -o -i 192.168.1.1 Mein-PC

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 _(u"    -i	manually specifiy IP address")
	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)

        if ipAddress_man == "__EMPTY__":
                logger.info(u"*** Not Replacing hostname '%s' ***" % host)
        else:
                logger.info(u"*** Replacing hostname '%s' with '%s' ***" % (host, ipAddress_man) )
                host = ipAddress_man


	winexe = './winexe'
	try:
		winexe = which('winexe')
	except:
		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:
				if ipAddress_man == "__EMPTY__":
					logger.notice(u"No manual Address provided - querying ... ")
					ipAddress = socket.gethostbyname(hostId)
				else:
					logger.notice(u"Provided manual address: '%s' " % ipAddress_man)
					ipAddress = ipAddress_man
					
			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 'nmblookup %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)
					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")

				if ipAddress_man != "__EMPTY__":
					logger.notice(u"*** SMBClient: Replacing '%s' with '%s' ***" % (hn, ipAddress_man))
					hn = ipAddress_man

				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'), 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)
						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)
				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)
						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)
				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)
				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
	global ipAddress_man 
	ipAddress_man = "__EMPTY__"
	
	# Get options
	try:
		(opts, args) = getopt.getopt(argv, "hVvcxsrou:p:f:i: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

		elif (opt == "-i"):
			ipAddress_man = arg
			logger.notice(u"*** Provided manual address: '%s' ***" % ipAddress_man)
			
	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)

Re: opsi-deploy-client-agent :: IP-Address statt Hostname

Verfasst: 11 Feb 2015, 15:42
von n.wenselowski
Hallo,

für das Deploy-Script wird es im kommenden Release die Möglichkeit geben auch über IPs zu deployen.


Gruß

Niko