source: opsi/server/dass-opsi-tools/usr/bin/opsiclient@ 1257

Last change on this file since 1257 was 1255, checked in by joergs, on Feb 15, 2021 at 7:56:31 PM

adapt outpput of listInstalled

  • Property svn:executable set to *
File size: 26.4 KB
Line 
1#!/usr/bin/env python
2
3# -*- coding: utf-8 -*-
4
5"""ospi-client: performs operation for opsi clients on opsi server via JSON-RPC."""
6
7from __future__ import print_function
8
9__author__ = "Joerg Steffens"
10__copyright__ = "Copyright 2012-2021, dass IT GmbH"
11__license__ = "GPL"
12__version__ = "1.3"
13__email__ = "joerg.steffens@dass-it.de"
14
15#self.command("opsi-admin -d method host_createOpsiClient "+ \
16 #computername + " null " + "\\'"+description+"\\'" + \
17 #" \\'created by dassadmin\\' " + mac_address + " " + \
18 #ip_address)
19#self.command("opsi-admin -d method configState_create clientconfig.depot.id " + \
20 #computername + " " + depotName)
21
22import argparse
23from datetime import datetime, timedelta
24from dateutil import parser as dateparser
25import logging
26import os
27from pprint import pprint, pformat
28import sys
29import ssl
30import time
31
32from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
33from tinyrpc.transports.http import HttpPostClientTransport
34from tinyrpc import RPCClient
35
36UrlJsonRpc="https://<username>:<password>@opsi:4447/rpc"
37
38HelpEpilog="WARNING: json-rpc is known to have problems with HTTP proxies. In case of problems, make sure, the environment variables http_proxy and/or https_proxy are *not* set. It might also be necessary to set variable PYTHONHTTPSVERIFY=0."
39
40class Nrpe:
41 OK = 0
42 WARNING = 1
43 CRITICAL = 2
44 UNKNOWN = 3
45
46 def toString(self, status):
47 if status == self.OK:
48 return 'OK'
49 elif status == self.WARNING:
50 return 'WARNING'
51 elif status == self.CRITICAL:
52 return 'CRITICAL'
53 else:
54 return 'UNKNOWN'
55
56class Output(Nrpe):
57 def __init__(self, nagios=False):
58 self.nagios = nagios
59 self.result = Nrpe.UNKNOWN
60 self.message = ''
61
62 def setStatus(self, status, message = None):
63 self.result = status
64 if message is not None:
65 self.setMessage(message)
66
67 def setMessage(self, string):
68 self.message = string
69
70 def finalize(self):
71 if self.nagios:
72 print('{0} - {1}'.format(self.toString(self.result), self.message))
73 sys.exit(self.result)
74 else:
75 print('{0} - {1}'.format(self.toString(self.result), self.message))
76 return (self.result == Nrpe.OK)
77
78class OpsiRpc:
79
80 UrlJsonRpcDefault="https://opsi:4447/rpc"
81
82 ProductAttributesCopy = ['actionRequest','actionResult','installationStatus','packageVersion','productVersion']
83
84 def __init__(self, urlJsonRpc = UrlJsonRpcDefault, debug=False, nagios=False):
85 self.logger=logging.getLogger(__name__)
86 self.debug=debug
87 self.nagios=nagios
88 self.urlJsonRpc=urlJsonRpc
89 self.rpc = RPCClient(JSONRPCProtocol(), HttpPostClientTransport(self.urlJsonRpc, verify=False)).get_proxy()
90
91 self.logger.debug( "initialized: " + self.urlJsonRpc )
92 self.logger.debug(dir(self.rpc))
93
94
95 def list(self):
96 exceptions = []
97 if 'jsonrpc' in sys.modules:
98 exceptions = [ jsonrpc.json.JSONDecodeException ]
99 try:
100 print( "\n".join( self.rpc.getClientIds_list() ) )
101 except exceptions as e:
102 self.logger.debug( pformat(self.rpc.getClientIds_list()) )
103 self.logger.exception( "failed" )
104 return True
105
106
107 def getClientsWithProduct( self, product ):
108 return self.rpc.productOnClient_getObjects( [], { "productId": product, "installationStatus": "installed" } )
109
110 def getHardwareSerialNumber(self, src):
111 serialNumbers = self.rpc.auditHardwareOnHost_getHashes( [], {"hostId":src, "hardwareClass":"BIOS", "serialNumber":"*"} )
112 serialNumbers += self.rpc.auditHardwareOnHost_getHashes( [], {"hostId":src, "hardwareClass":"CHASSIS", "serialNumber":"*"} )
113 result = set()
114 for i in serialNumbers:
115 if i['serialNumber']:
116 result.add(i['serialNumber'])
117 if len(result) == 1:
118 return result.pop()
119 elif len(result) > 1:
120 self.logger.warning("found more then one serial number")
121 return list(result)
122
123 def listClients( self, product ):
124 if product:
125 for client in self.getClientsWithProduct( product ):
126 print(client['clientId'])
127 else:
128 return self.list()
129 return True
130
131 def exists(self, src):
132 return len( self.rpc.host_getObjects( [], {"id":src} ) ) == 1
133
134 def info(self, src):
135 if not self.exists( src ):
136 print("failed: opsi client", src, "does not exist")
137 return False
138 print(src + ":")
139 host = self.rpc.host_getHashes( [], {"id":src} )[0]
140 print(" IP:", host["ipAddress"])
141 print(" MAC:", host["hardwareAddress"])
142 print(" inventory:", host["inventoryNumber"])
143 print(" serial number:", self.getHardwareSerialNumber(src))
144 print(" last seen:", host["lastSeen"])
145 print(" notes:", host["notes"])
146 print(" depot:", self.clientGetDepot(src))
147
148 print(" products:")
149 products = self.getProductOnClient( src, [] )
150 for i in products:
151 print(" " + i['productId'] + ":")
152 print(" " + i['installationStatus'], "(", end='')
153 if i['actionRequest']:
154 print(i['actionRequest'], end='')
155 if i['actionProgress']:
156 print(i['actionProgress'], end='')
157 print(")")
158 print(" ", end='')
159 pprint( i, indent=8 )
160 return True
161
162 def clean(self, src):
163 if not self.exists( src ):
164 return False
165 products = self.rpc.productOnClient_getObjects( [], { 'clientId': src } )
166 self.rpc.productOnClient_deleteObjects( products )
167
168 products = self.rpc.productPropertyState_getObjects( [], { 'objectId': src } )
169 self.rpc.productPropertyState_deleteObjects( products )
170
171 if self.debug:
172 pprint( self.getProductOnClient( src ) )
173 return True
174
175 def getOpsiConfigserverId(self):
176 # there should always be only one OpsiConfigserver
177 opsiConfigservers=self.rpc.host_getHashes( [], { "type": "OpsiConfigserver" } )
178 try:
179 return opsiConfigservers[0]['id']
180 except (KeyError,IndexError) as e:
181 self.logger.error( "failed to retreive OpsiConfigserver" )
182
183 def createClient(self, name, opsiHostKey, description, notes, hardwareAddress, ipAddress):
184 # self.rpc.host_createOpsiClient( name, opsiHostKey, description, notes, hardwareAddress, ipAddress )
185 self.updateClient( name, opsiHostKey, description, notes, None, hardwareAddress, ipAddress )
186
187 def deleteClient(self, name):
188 self.rpc.host_delete( name )
189
190 def updateClient(self, src, opsiHostKey = None, description = None, notes = None, inventoryNumber = None, hardwareAddress = None, ipAddress = None, depot = None ):
191 obj = {
192 "id" : src,
193 "type" : "OpsiClient",
194 }
195 if opsiHostKey:
196 obj['opsiHostKey'] = opsiHostKey
197 if description:
198 obj['description'] = description
199 if notes:
200 obj['notes'] = notes
201 if inventoryNumber:
202 obj['inventoryNumber'] = inventoryNumber
203 if hardwareAddress:
204 obj['hardwareAddress'] = hardwareAddress
205 if ipAddress:
206 obj['ipAddress'] = ipAddress
207
208 if self.exists( src ):
209 self.rpc.host_updateObject(obj)
210 else:
211 self.rpc.host_insertObject(obj)
212
213 if depot:
214 self.clientSetDepot(src,depot)
215 return True
216
217 def clientGetDepot(self, name):
218 depot = self.rpc.configState_getHashes( [], {
219 "configId": "clientconfig.depot.id",
220 "objectId": name } )
221 try:
222 return depot[0]["values"][0]
223 except (IndexError,KeyError):
224 return self.getOpsiConfigserverId()
225
226 def clientSetDepot(self, name, depot):
227 self.rpc.configState_create( "clientconfig.depot.id", name, depot )
228
229 def copyClient( self, src, dst, ipAddress = None, hardwareAddress = None, depot = None, description = "", copyProperties = True ):
230
231 print("create/update", dst, "from template", src + ":", end='')
232 obj = {
233 "id" : dst,
234 "type" : "OpsiClient",
235 "notes" : "copy of " + src,
236 "description" : description,
237 #"inventoryNumber" : "",
238 }
239 if hardwareAddress:
240 obj['hardwareAddress'] = hardwareAddress
241 if ipAddress:
242 obj['ipAddress'] = ipAddress
243
244 if self.exists( dst ):
245 self.rpc.host_updateObject(obj)
246 else:
247 self.rpc.host_insertObject(obj)
248
249 if depot:
250 self.clientSetDepot(dst,depot)
251
252 if self.debug:
253 pprint( self.getProductOnClient( src ) )
254 self.copyProductOnClient( src, dst )
255 if copyProperties:
256 if self.debug:
257 print("copy product properties")
258 if not depot:
259 # get default Properties from Master Depot Server (OpsiConfigserver)
260 depot = self.getOpsiConfigserverId()
261 self.copyProductPropertyState( src, dst, depot )
262 print("done")
263 return True
264
265 def getProductOnClient( self, client, attributes = ProductAttributesCopy ):
266 return self.rpc.productOnClient_getHashes( [], { 'clientId': client } )
267
268 def copyProductOnClient( self, src, dst, attributes = ProductAttributesCopy ):
269 products_src = self.rpc.productOnClient_getHashes( attributes, { 'clientId': src } )
270 products_dst = []
271 for i in products_src:
272 if self.debug:
273 print(i['productId'])
274 pprint( i )
275 i['clientId'] = dst
276 products_dst.append(i)
277 self.rpc.productOnClient_createObjects( products_dst )
278 if self.debug:
279 pprint( self.getProductOnClient( dst ) )
280
281
282 def getProductPropertyState( self, client, attributes = [] ):
283 return self.rpc.productPropertyState_getHashes( [], { 'objectId': client } )
284
285
286 def copyProductPropertyState( self, src, dst, default = None, attributes = [] ):
287 if default:
288 productProperties_default = self.getProductPropertyState( default, attributes )
289 else:
290 productProperties_default = []
291 productProperties_src = self.getProductPropertyState( src, attributes )
292 productProperties_dst = []
293 for i in productProperties_src:
294 use_default=False
295 default_value=None
296 for j in productProperties_default:
297 if i['productId'] == j['productId'] and i["propertyId"] == j["propertyId"]:
298 default_value = j['values']
299 if i['values'] == j['values']:
300 use_default=True
301 if self.debug:
302 print(i['productId'], "-", i["propertyId"] + ": ", pformat(i["values"]), end='')
303 if use_default:
304 print("(use default)")
305 else:
306 print("(set, default:", default_value, ")")
307 if not use_default:
308 i['objectId'] = dst
309 productProperties_dst.append(i)
310 self.rpc.productPropertyState_createObjects( productProperties_dst )
311 if self.debug:
312 pprint( self.getProductPropertyState( dst ) )
313
314
315 def getClientProductProperty( self, client, product ):
316 return self.rpc.getProductProperties_hash( product, [ client ] )
317
318 def setProductPropertiesOnClient( self, dst, product, properties ):
319 self.rpc.setProductProperties(product,properties,dst)
320
321 def setProductPropertyOnClient( self, dst, product, prop, value):
322 self.rpc.setProductProperty(product,prop,value,dst)
323
324
325
326 def write_value_conf(self, fd, key, properties, default=None):
327 value = None
328 comment = ''
329 try:
330 value = properties[key.lower()]
331 except KeyError:
332 pass
333 if not value and default:
334 value = default
335 if not value:
336 # prevent a None to be written
337 value = ''
338 comment = '# '
339 fd.write(' {}{} = "{}"\n'.format(comment, key, value))
340
341
342
343 def write_client_conf( self, fd, client, properties ):
344 #Client {
345 #Name = ting-fd
346 #Address = ting.dass-it
347 #FDPort = 9102
348 #Password = "D5w2V5w6B8a9H5Z"
349 #Catalog = MyCatalog
350 #File Retention = 6 months
351 #Job Retention = 6 months
352 #AutoPrune = yes
353 #}
354 params = [ 'catalog', "FDPort", "FileRetention", "JobRetention", "AutoPrune" ]
355 fd.write( "Client {\n" )
356 fd.write( ' Name = "' + properties['filedaemon_full_name'] + '"' + "\n" )
357 fd.write( ' Address = "' + properties['filedaemon_client_address'] + '"' + "\n" )
358 # ipAddress: method host_getObjects [] '{"id":client['clientId']}'
359 #print(" # Address =", ipAddress)
360 fd.write( ' Password = "' + properties['filedaemon_full_password'] + '"' + "\n" )
361 for i in params:
362 self.write_value_conf(fd, i, properties)
363 fd.write( "}\n")
364 fd.write( "\n" )
365
366
367 def write_job_conf(self, fd, client, properties, defaultjobdefs, defaultfileset):
368 #Job {
369 #FileSet = "tingfileset"
370 #Name = "ting"
371 #Client = ting-fd
372 #JobDefs = "LaptopJob"
373 ## Write Bootstrap = "/var/lib/bacula/ting.bsr"
374 #}
375 params = [ "JobDefs", "FileSet" ]
376 fd.write( "Job {" + "\n" )
377 fd.write( ' Name = "' + client['clientId'] + '-job"' + "\n" )
378 fd.write( ' Client = "' + properties['filedaemon_full_name'] + '"' + "\n" )
379 self.write_value_conf(fd, 'JobDefs', properties, defaultjobdefs)
380 self.write_value_conf(fd, 'FileSet', properties, defaultfileset)
381 fd.write( "}" + "\n" )
382 fd.write( "\n" )
383
384
385 def write_config_file_header( self, fd ):
386 try:
387 fd.write( "#\n" )
388 fd.write( "# automatically generated at {0}\n".format( time.asctime() ) )
389 fd.write( "#\n\n" )
390 except BaseException as e:
391 self.logger.exception( "failed to create files" )
392 return False
393 return True
394
395
396
397 def createBareosConfigFiles(self, defaultjobdefs, defaultfileset):
398 bareosDirConfigPath = '/etc/bareos/bareos-dir.d/'
399 clientsWithBacula=self.getClientsWithProduct('winbareos')
400 if clientsWithBacula:
401 try:
402 configfile = bareosDirConfigPath + 'client/opsi-clients-generated.conf'
403 file_opsi_clients = open(configfile, 'w')
404 self.write_config_file_header( file_opsi_clients )
405 except (BaseException, IOError) as e:
406 self.logger.exception( "failed to create configuration file {}".format(configfile) )
407 return False
408
409 try:
410 configfile = bareosDirConfigPath + 'job/opsi-jobs-generated.conf'
411 file_opsi_jobs = open(configfile, 'w')
412 self.write_config_file_header( file_opsi_jobs )
413 except (BaseException, IOError) as e:
414 self.logger.exception( "failed to create configuration file {}".format(configfile) )
415 return False
416
417 for client in clientsWithBacula:
418 clientId = client['clientId']
419 try:
420 clientBaculaProperties=self.getClientProductProperty( clientId, 'winbareos' )
421 except ValueError as e:
422 self.logger.warn( "%s: no valid information found: %s" %(clientId, e) )
423 else:
424 if clientBaculaProperties:
425 #pprint( clientBaculaProperties )
426 self.write_client_conf(file_opsi_clients, client, clientBaculaProperties)
427 self.write_job_conf(file_opsi_jobs, client, clientBaculaProperties, defaultjobdefs, defaultfileset)
428 self.logger.info( "%s: OK" % clientId )
429 else:
430 self.logger.warn( "%s: failed: no product properties defined" %(clientId) )
431 return True
432
433 def __getVersionString(self, product):
434 return '{productVersion}-{packageVersion}'.format(**product)
435
436 def getProductCurrentVersion(self, productId):
437 products = self.rpc.product_getHashes( [], { 'id': productId } )
438 if products:
439 return self.__getVersionString(products[0])
440 else:
441 return None
442
443 def __getClientId(self, d):
444 return d.get("clientId")
445
446 def listInstalled(self, productId):
447 productVersion = self.getProductCurrentVersion(productId)
448 self.logger.debug('version: {0}'.format(productVersion))
449 products = self.rpc.productOnClient_getHashes( [], { 'productId': productId } )
450 for i in sorted(products, key=self.__getClientId):
451 i['version'] = self.__getVersionString(i)
452 if i.get('installationStatus') == 'installed':
453 if productVersion != i['version']:
454 i['proposedAction'] = 'update'
455 else:
456 i['proposedAction'] = 'None'
457 print('{clientId}: {version} (proposed action={proposedAction}) request={actionRequest}, result={actionResult}'.format(**i))
458 else:
459 i['proposedAction'] = 'install'
460 print('{clientId}: (proposed action={proposedAction})'.format(**i))
461 self.logger.debug('{clientId}: {actionRequest} {installationStatus} {version}'.format(**i))
462 #pprint( i, indent=8 )
463 return True
464
465 def isInstalled(self, clientId, productId):
466 """
467 CRITICAL: not installed
468 WARNING: installed, but not current version
469 OK: current version is installed
470 UNKNOWN: otherwise
471 """
472 output = Output(self.nagios)
473 if not self.exists(clientId):
474 output.setMessage("failed: opsi client {0} does not exist".format(clientId))
475 return output.finalize()
476 productVersion = self.getProductCurrentVersion(productId)
477 if not productVersion:
478 output.setMessage("failed: product {0} does not exist".format(productId))
479 return output.finalize()
480 self.logger.debug('version: {0}'.format(productVersion))
481 products = self.rpc.productOnClient_getHashes( [], { "clientId": clientId,'productId': productId } )
482 if len(products) != 1:
483 print("failed: opsi client ({0}) product ({1}) combination does not exist".format(clientId, productId))
484 return False
485 for i in sorted(products, key=self.__getClientId):
486 i['version'] = self.__getVersionString(i)
487 if i.get('installationStatus') == 'installed':
488 if productVersion != i['version']:
489 i['proposedAction'] = 'update'
490 output.setStatus(Nrpe.WARNING)
491 else:
492 i['proposedAction'] = 'None'
493 output.setStatus(Nrpe.OK)
494 output.setMessage('{version} (proposed action={proposedAction})'.format(**i))
495 else:
496 i['proposedAction'] = 'install'
497 output.setStatus(Nrpe.CRITICAL, 'not installed (proposed action={proposedAction})'.format(**i))
498 self.logger.debug('{clientId}: {actionRequest} {installationStatus} {version}'.format(**i))
499 return output.finalize()
500
501
502 def clientLastSeen(self, clientId):
503 """
504 < 1 day: OK
505 < 2 days: WARNING
506 otherwise: CRITICAL
507 """
508 output = Output(self.nagios)
509 if not self.exists(clientId):
510 output.setMessage("failed: opsi client {0} does not exist".format(clientId))
511 return output.finalize()
512 host = self.rpc.host_getHashes( [], {"id":clientId} )[0]
513 lastSeen = dateparser.parse(host["lastSeen"])
514 output.setMessage(str(lastSeen))
515 diff = datetime.now() - lastSeen
516 output.setMessage('{0} ({1} ago)'.format(str(lastSeen), diff))
517 if diff < timedelta(1):
518 output.setStatus(Nrpe.OK)
519 elif diff < timedelta(2):
520 output.setStatus(Nrpe.WARNING)
521 else:
522 output.setStatus(Nrpe.CRITICAL)
523 return output.finalize()
524
525
526
527if __name__ == '__main__':
528 logging.basicConfig(format='%(message)s')
529 logger = logging.getLogger(__name__)
530 logger.setLevel(logging.INFO)
531
532 parser = argparse.ArgumentParser(
533 description='Command line tool for OPSI configuration.',
534 epilog=HelpEpilog
535 )
536
537 parser.add_argument('--debug', action='store_true', help="enable debugging output")
538 parser.add_argument('--nagios', action='store_true', help='output in Nagios NRPE format')
539
540 parser_url = parser.add_mutually_exclusive_group(required=True)
541 parser_url.add_argument( '--url', help="OPSI Server JSON-RPC url, in following format: " + UrlJsonRpc )
542
543 parser_url.add_argument( '--server', help="OPSI Server (instead of URL)" )
544 username_default=os.getlogin()
545
546 parser.add_argument( '--username', help="username (instead of URL), default: " + username_default, default=username_default )
547 parser.add_argument( '--password', help="password (instead of URL)" )
548
549 subparsers = parser.add_subparsers(title='subcommands',
550 description='valid subcommands',
551 help='additional help',
552 dest='subcommand' )
553
554 parser_clean = subparsers.add_parser('clean', help='remove all product states from a opsi client' )
555 parser_clean.add_argument( 'src', help="source opsi client to clean" )
556
557 parser_copy = subparsers.add_parser('copy', help='copy/create a opsi client from a template opsi client')
558 parser_copy.add_argument( 'src', help="source/template opsi client" )
559 parser_copy.add_argument( 'dst', help="opsi client to be created" )
560 parser_copy.add_argument( '--ip', help="IP address of the new opsi client" )
561 parser_copy.add_argument( '--mac', help="MAC address of the new opsi client" )
562 parser_copy.add_argument( '--depot', help="depot server the new opsi client should be located" )
563 #parser_copy.add_argument( '--no-properties', action='store_false', help="don't copy product properties" )
564
565 parser_createBareosConfigFiles = subparsers.add_parser(
566 'createBareosConfigFiles',
567 help='create Bareos config files for all clients that have winbareos installed',
568 formatter_class=argparse.ArgumentDefaultsHelpFormatter
569 )
570 parser_createBareosConfigFiles.add_argument(
571 '--defaultjobdefs',
572 metavar='JobDefs',
573 default='DefaultJob',
574 help="use this JobDefs if no other is defined for a client"
575 )
576 parser_createBareosConfigFiles.add_argument(
577 '--defaultfileset',
578 metavar='FileSet',
579 help="use this FileSet if no other is defined for a client"
580 )
581
582 parser_exists = subparsers.add_parser('exists', help='check, if a opsi clients exists' )
583 parser_exists.add_argument( 'src', help="source opsi client" )
584 #parser_list = subparsers.add_parser('list', help='list all opsi clients' )
585
586 parser_listClients = subparsers.add_parser('listClients', help='list opsi clients')
587 parser_listClients.add_argument( '--product', help="only list clients, that have product installed" )
588
589 parser_info = subparsers.add_parser('info', help='print information about a opsi client' )
590 parser_info.add_argument( 'src', help="opsi client" )
591
592 parser_clientLastSeen = subparsers.add_parser('clientLastSeen', help='print information about a opsi client' )
593 parser_clientLastSeen.add_argument( 'client', help="opsi client" )
594
595 parser_listInstalled = subparsers.add_parser('listInstalled', help='check if product is installed on client')
596 parser_listInstalled.add_argument('product', help='opsi product')
597
598 parser_isInstalled = subparsers.add_parser('isInstalled', help='check if product is installed on client')
599 parser_isInstalled.add_argument('client', help='opsi client')
600 parser_isInstalled.add_argument('product', help='opsi product')
601
602 parser_update = subparsers.add_parser('update', help='update/create a opsi client')
603 parser_update.add_argument( 'src', help="opsi client to be created/updated" )
604 parser_update.add_argument( '--ip', help="IP address of the opsi client" )
605 parser_update.add_argument( '--mac', help="MAC address of the opsi client" )
606 parser_update.add_argument( '--description', help="a description of the client" )
607 parser_update.add_argument( '--notes', help="notes about the client" )
608 parser_update.add_argument( '--inventory', help="inventory number" )
609 parser_update.add_argument( '--depot', help="depot server the opsi client should be located" )
610
611 args = parser.parse_args()
612
613 if args.debug:
614 logger.setLevel(logging.DEBUG)
615
616 url=args.url
617 if (not url):
618 if args.server:
619 account=""
620 if args.username and args.password:
621 account=args.username + ":" + args.password + "@"
622 elif args.username:
623 account=args.username + "@"
624 url="https://" + account + args.server + ":4447/rpc"
625 else:
626 parser.error( "argument --url is required" )
627
628 opsi=OpsiRpc(url, args.debug, args.nagios)
629
630 result = True
631
632 try:
633 if args.subcommand == "clean":
634 result = opsi.clean( args.src )
635 elif args.subcommand == "copy":
636 result = opsi.copyClient( args.src, args.dst, args.ip, args.mac, args.depot )
637 elif args.subcommand == "createBareosConfigFiles":
638 result = opsi.createBareosConfigFiles(args.defaultjobdefs, args.defaultfileset)
639 elif args.subcommand == "exists":
640 result = opsi.exists( args.src )
641 elif args.subcommand == "list":
642 result = opsi.list()
643 elif args.subcommand == "listClients":
644 result = opsi.listClients( args.product )
645 elif args.subcommand == "info":
646 result = opsi.info( args.src )
647 elif args.subcommand == "update":
648 result = opsi.updateClient( args.src, None, args.description, args.notes, args.inventory, args.mac, args.ip, args.depot )
649 elif args.subcommand == 'listInstalled':
650 result = opsi.listInstalled(args.product)
651 elif args.subcommand == 'isInstalled':
652 result = opsi.isInstalled(args.client, args.product)
653 elif args.subcommand == 'clientLastSeen':
654 result = opsi.clientLastSeen(args.client)
655 else:
656 print("not yet implemented")
657 except IOError as e:
658 result = False
659 # connection refused
660 print("failed:", e)
661
662 if args.debug: print(result)
663
664 if result:
665 exit(0)
666 else:
667 exit(1)
Note: See TracBrowser for help on using the repository browser.