Package Products :: Package ZenModel :: Module BatchDeviceLoader
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenModel.BatchDeviceLoader

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2009, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 as published by 
  8  # the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13  __doc__ = """BatchDeviceLoader.py 
 14   
 15  zenbatchload loads a list of devices read from a file. 
 16  """ 
 17   
 18  import sys 
 19  import re 
 20   
 21  import Globals 
 22  from transaction import commit 
 23   
 24  from Products.ZenUtils.ZCmdBase import ZCmdBase 
 25  from Products.ZenRelations.ZenPropertyManager import iszprop 
 26  from zExceptions import BadRequest 
 27   
 28   
29 -class BatchDeviceLoader(ZCmdBase):
30 """ 31 Base class wrapping around dmd.DeviceLoader 32 """ 33 34 sample_configs = """# 35 # Example zenbatchloader device file 36 # 37 # This file is formatted with one entry per line, like this: 38 # 39 # /Devices/device_class_name Python-expression 40 # hostname Python-expression 41 # 42 # For organizers (ie the /Devices path), the Python-expression 43 # is used to define defaults to be used for devices listed 44 # after the organizer. The defaults that can be specified are: 45 # 46 # * loader arguments (use the --show_options flag to show these) 47 # * zPropertie (from a device, use the More -> zProperties 48 # menu option to see the available ones.) 49 # 50 # NOTE: new zProperties *cannot* be created through this file 51 # 52 # The Python-expression is used to create a dictionary of settings. 53 # device_settings = eval( 'dict(' + python-expression + ')' ) 54 # 55 56 57 # If no organizer is specified at the beginning of the file, 58 # defaults to the /Devices/Discovered device class. 59 device0 comments="A simple device" 60 # All settings must be seperated by a comma. 61 device1 comments="A simple device" zSnmpCommunity='blue', zSnmpVer='v1' 62 63 # Notes for this file: 64 # * Organizer names cannot contain spaces 65 # * Oraganizer names *must* start with '/' 66 # 67 /Devices/Server/Linux zSnmpPort=1543 68 # Python strings can use either ' or " -- there's no difference. 69 # As a special case, it is also possible to specify the IP address 70 linux_device1 manageIp=10.10.10.77, zSnmpCommunity='blue', zSnmpVer="v2c" 71 # A '\' at the end of the line allows you to place more 72 # expressions on a new line. Don't forget the comma... 73 linux_device2 discoverProto='none', zLinks="<a href='http://example.org'>Support site</a>", \ 74 zTelnetEnable=True \ 75 zTelnetPromptTimeout=15.3 76 77 # A new organizer drops all previous settings, and allows 78 # for new ones to be used. Settings do not span files. 79 /Devices/Server/Windows zWinUser="administrator", zWinPassword='fred' 80 # Bind templates 81 windows_device1 zDeviceTemplates=[ 'Device', 'myTemplate' ] 82 # Override the default from the organizer setting. 83 windows_device2 zWinUser="administrator", zWinPassword='thomas' 84 85 """ 86
87 - def __init__(self):
88 ZCmdBase.__init__(self) 89 self.defaults = {} 90 91 self.loader = self.dmd.DeviceLoader.loadDevice 92 93 # Create the list of options we want people to know about 94 self.loader_args = dict.fromkeys( self.loader.func_code.co_varnames ) 95 unsupportable_args = [ 96 'REQUEST', 'device', 'self', 'xmlrpc', 'e', 'handler', 97 ] 98 for opt in unsupportable_args: 99 if opt in self.loader_args: 100 del self.loader_args[opt]
101
102 - def loadDeviceList(self, args=None):
103 """ 104 Read through all of the files listed as arguments and 105 return a list of device entries. 106 107 @parameter args: list of filenames (uses self.args is this is None) 108 @type args: list of strings 109 @return: list of device specifications 110 @rtype: list of dictionaries 111 """ 112 if args is None: 113 args = self.args 114 115 device_list = [] 116 for filename in args: 117 try: 118 data = open(filename,'r').readlines() 119 except IOError: 120 self.log.critical("Unable to open the file '%s'" % filename) 121 continue 122 123 temp_dev_list = self.parseDevices(data) 124 if temp_dev_list: 125 device_list += temp_dev_list 126 127 return device_list
128
129 - def applyZProps(self, device, device_specs):
130 """ 131 Apply zProperty settings (if any) to the device. 132 133 @parameter device: device to modify 134 @type device: DMD device object 135 @parameter device_specs: device creation dictionary 136 @type device_specs: dictionary 137 """ 138 self.log.debug( "Applying zProperties.." ) 139 # Returns a list of (key, value) pairs. 140 # Convert it to a dictionary. 141 dev_zprops = dict( device.zenPropertyItems() ) 142 143 for zprop, value in device_specs.items(): 144 if not iszprop(zprop): 145 continue 146 147 if zprop in dev_zprops: 148 try: 149 device.setZenProperty(zprop, value) 150 except BadRequest: 151 self.log.warn( "Device %s zproperty %s is invalid or duplicate" % ( 152 device_specs['deviceName'], zprop) ) 153 else: 154 self.log.debug( "The zproperty %s doesn't exist in %s" % ( 155 zprop, device_specs['deviceName']))
156
157 - def processDevices(self, device_list):
158 """ 159 Read the input and process the devices 160 * create the device entry 161 * set zproperties 162 * model the device 163 164 @parameter device_list: list of device entries 165 @type device_list: list of dictionaries 166 """ 167 processed = 0 168 for device_specs in device_list: 169 devobj = self.getDevice(device_specs) 170 if devobj is None: 171 continue 172 173 if device_specs.get('manageIp', ''): 174 manageIp = device_specs['manageIp'] 175 devobj.setManageIp(manageIp) 176 self.log.info("Setting %s IP address to '%s'", 177 devobj.id, manageIp) 178 179 self.applyZProps(devobj, device_specs) 180 181 # Default is discoverProto == 'snmp' 182 if device_specs.get('discoverProto', '') != 'none': 183 # What if zSnmpCommunity isn't set in the file? 184 devobj.manage_snmpCommunity() 185 186 # Make sure that ZODB has changes before modeling 187 commit() 188 try: 189 devobj.collectDevice(setlog=self.options.showModelOutput) 190 except (SystemExit, KeyboardInterrupt): 191 self.log.info("User interrupted modeling") 192 break 193 except Exception, ex: 194 self.log.exception("Modeling error" ) 195 196 commit() 197 processed += 1 198 199 self.log.info( "Processed %d of %d devices" % (processed, len(device_list)))
200
201 - def getDevice(self, device_specs):
202 """ 203 Find or create the specified device 204 205 @parameter device_specs: device creation dictionary 206 @type device_specs: dictionary 207 @return: device or None 208 @rtype: DMD device object 209 """ 210 if 'deviceName' not in device_specs: 211 return None 212 name = device_specs['deviceName'] 213 devobj = self.dmd.Devices.findDevice(name) 214 if devobj is not None: 215 self.log.info("Found existing device %s" % name) 216 return devobj 217 218 specs = {} 219 for key in self.loader_args: 220 if key in device_specs: 221 specs[key] = device_specs[key] 222 223 try: 224 self.log.info("Creating device %s" % name) 225 226 # Do NOT model at this time 227 specs['discoverProto'] = 'none' 228 229 self.loader(**specs) 230 devobj = self.dmd.Devices.findDevice(name) 231 if devobj is None: 232 self.log.error("Unable to find newly created device %s -- skipping" \ 233 % name) 234 except Exception, ex: 235 self.log.exception("Unable to load %s -- skipping" % name ) 236 237 return devobj
238
239 - def buildOptions(self):
240 """ 241 Add our command-line options to the basics 242 """ 243 ZCmdBase.buildOptions(self) 244 245 self.parser.add_option('--show_options', 246 dest="show_options", default=False, 247 action="store_true", 248 help="Show the various options understood by the loader") 249 250 self.parser.add_option('--sample_configs', 251 dest="sample_configs", default=False, 252 action="store_true", 253 help="Show an example configuration file.") 254 255 self.parser.add_option('--showModelOutput', 256 dest="showModelOutput", default=True, 257 action="store_false", 258 help="Show modelling activity")
259
260 - def parseDevices(self, data):
261 """ 262 From the list of strings in rawDevices, construct a list 263 of device dictionaries, ready to load into Zenoss. 264 265 @parameter data: list of strings representing device entries 266 @type data: list of strings 267 @return: list of parsed device entries 268 @rtype: list of dictionaries 269 """ 270 if not data: 271 return [] 272 273 comment = re.compile(r'#.*') 274 275 defaults = {'devicePath':"/Discovered" } 276 finalList = [] 277 i = 0 278 while i < len(data): 279 line = data[i] 280 line = re.sub(comment, '', line).strip() 281 if line == '': 282 i += 1 283 continue 284 285 # Check for line continuation character '\' 286 while line[-1] == '\\' and i < len(data): 287 i += 1 288 line = line[:-1] + data[i] 289 line = re.sub(comment, '', line).strip() 290 291 if line[0] == '/': # Found an organizer 292 defaults = self.parseDeviceEntry(line, {}) 293 defaults['devicePath'] = defaults['deviceName'] 294 del defaults['deviceName'] 295 296 else: 297 configs = self.parseDeviceEntry(line, defaults) 298 if configs: 299 finalList.append(configs) 300 i += 1 301 302 return finalList
303
304 - def parseDeviceEntry(self, line, defaults):
305 """ 306 Build a dictionary of properties from one line's input 307 308 @parameter line: string containing one device's info 309 @type line: string 310 @parameter defaults: dictionary of default settings 311 @type defaults: dictionary 312 @return: parsed device entry 313 @rtype: dictionary 314 """ 315 options = None 316 if line.find(' ') > 0: 317 name, options = line.split(None, 1) 318 else: 319 name = line 320 321 configs = defaults.copy() 322 configs['deviceName'] = name 323 324 if options: 325 try: 326 configs.update( eval( 'dict(' + options + ')' ) ) 327 except: 328 self.log.error( "Unable to parse the entry for %s -- skipping" % name ) 329 self.log.error( "Raw string: %s" % options ) 330 return None 331 332 return configs
333 334 335 if __name__=='__main__': 336 batchLoader = BatchDeviceLoader() 337 338 if batchLoader.options.show_options: 339 print "Options = %s" % sorted( batchLoader.loader_args.keys() ) 340 help(batchLoader.loader) 341 sys.exit(0) 342 343 if batchLoader.options.sample_configs: 344 print batchLoader.sample_configs 345 sys.exit(0) 346 347 device_list = batchLoader.loadDeviceList() 348 batchLoader.processDevices(device_list) 349