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

Source Code for Module 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 linux_device1 zSnmpCommunity='blue', zSnmpVer="v2c" 70 # A '\' at the end of the line allows you to place more 71 # expressions on a new line. Don't forget the comma... 72 linux_device2 discoverProto='none', zLinks="<a href='http://example.org'>Support site</a>", \ 73 zTelnetEnable=True \ 74 zTelnetPromptTimeout=15.3 75 76 # A new organizer drops all previous settings, and allows 77 # for new ones to be used. Settings do not span files. 78 /Devices/Server/Windows zWinUser="administrator", zWinPassword='fred' 79 # Bind templates 80 windows_device1 zDeviceTemplates=[ 'Device', 'myTemplate' ] 81 # Override the default from the organizer setting. 82 windows_device2 zWinUser="administrator", zWinPassword='thomas' 83 84 """ 85
86 - def __init__(self):
87 ZCmdBase.__init__(self) 88 self.defaults = {} 89 90 self.loader = self.dmd.DeviceLoader.loadDevice 91 92 # Create the list of options we want people to know about 93 self.loader_args = dict.fromkeys( self.loader.func_code.co_varnames ) 94 unsupportable_args = [ 95 'REQUEST', 'device', 'self', 'xmlrpc', 'e', 'handler', 96 ] 97 for opt in unsupportable_args: 98 if self.loader_args.has_key(opt): 99 del self.loader_args[opt]
100
101 - def loadDeviceList(self, args=None):
102 """ 103 Read through all of the files listed as arguments and 104 return a list of device entries. 105 106 @parameter args: list of filenames (uses self.args is this is None) 107 @type args: list of strings 108 @return: list of device specifications 109 @rtype: list of dictionaries 110 """ 111 if args is None: 112 args = self.args 113 114 device_list = [] 115 for filename in args: 116 try: 117 data = open(filename,'r').readlines() 118 except IOError: 119 self.log.critical("Unable to open the file '%s'" % filename) 120 continue 121 122 temp_dev_list = self.parseDevices(data) 123 if temp_dev_list: 124 device_list += temp_dev_list 125 126 return device_list
127
128 - def applyZProps(self, device, device_specs):
129 """ 130 Apply zProperty settings (if any) to the device. 131 132 @parameter device: device to modify 133 @type device: DMD device object 134 @parameter device_specs: device creation dictionary 135 @type device_specs: dictionary 136 """ 137 self.log.debug( "Applying zProperties.." ) 138 # Returns a list of (key, value) pairs. 139 # Convert it to a dictionary. 140 dev_zprops = dict( device.zenPropertyItems() ) 141 142 for zprop, value in device_specs.items(): 143 if not iszprop(zprop): 144 continue 145 146 if zprop in dev_zprops: 147 try: 148 device.setZenProperty(zprop, value) 149 except BadRequest: 150 self.log.warn( "Device %s zproperty %s is invalid or duplicate" % ( 151 device_specs['deviceName'], zprop) ) 152 else: 153 self.log.debug( "The zproperty %s doesn't exist in %s" % ( 154 zprop, device_specs['deviceName']))
155
156 - def processDevices(self, device_list):
157 """ 158 Read the input and process the devices 159 * create the device entry 160 * set zproperties 161 * model the device 162 163 @parameter device_list: list of device entries 164 @type device_list: list of dictionaries 165 """ 166 processed = 0 167 for device_specs in device_list: 168 devobj = self.getDevice(device_specs) 169 if devobj is None: 170 continue 171 172 self.applyZProps(devobj, device_specs) 173 174 # Default is discoverProto == 'snmp' 175 if device_specs.get('discoverProto', '') != 'none': 176 try: 177 devobj.collectDevice(setlog=self.options.showModelOutput) 178 except (SystemExit, KeyboardInterrupt): 179 self.log.info("User interrupted modelling") 180 break 181 except Exception, ex: 182 self.log.exception("Modelling error" ) 183 184 commit() 185 processed += 1 186 187 self.log.info( "Processed %d of %d devices" % (processed, len(device_list)))
188
189 - def getDevice(self, device_specs):
190 """ 191 Find or create the specified device 192 193 @parameter device_specs: device creation dictionary 194 @type device_specs: dictionary 195 @return: device or None 196 @rtype: DMD device object 197 """ 198 if not device_specs.has_key('deviceName'): 199 return None 200 name = device_specs['deviceName'] 201 devobj = self.dmd.Devices.findDevice(name) 202 if devobj is not None: 203 self.log.info("Found existing device %s" % name) 204 return devobj 205 206 specs = {} 207 for key in self.loader_args: 208 if key in device_specs: 209 specs[key] = device_specs[key] 210 211 try: 212 self.log.info("Creating device %s" % name) 213 214 # Do NOT model at this time 215 specs['discoverProto'] = 'none' 216 217 self.loader(**specs) 218 devobj = self.dmd.Devices.findDevice(name) 219 if devobj is None: 220 self.log.error("Unable to find newly created device %s -- skipping" \ 221 % name) 222 except Exception, ex: 223 self.log.exception("Unable to load %s -- skipping" % name ) 224 225 return devobj
226
227 - def buildOptions(self):
228 """ 229 Add our command-line options to the basics 230 """ 231 ZCmdBase.buildOptions(self) 232 233 self.parser.add_option('--show_options', 234 dest="show_options", default=False, 235 action="store_true", 236 help="Show the various options understood by the loader") 237 238 self.parser.add_option('--sample_configs', 239 dest="sample_configs", default=False, 240 action="store_true", 241 help="Show an example configuration file.") 242 243 self.parser.add_option('--showModelOutput', 244 dest="showModelOutput", default=True, 245 action="store_false", 246 help="Show modelling activity")
247
248 - def parseDevices(self, data):
249 """ 250 From the list of strings in rawDevices, construct a list 251 of device dictionaries, ready to load into Zenoss. 252 253 @parameter data: list of strings representing device entries 254 @type data: list of strings 255 @return: list of parsed device entries 256 @rtype: list of dictionaries 257 """ 258 if not data: 259 return [] 260 261 comment = re.compile(r'#.*') 262 263 defaults = {'devicePath':"/Discovered" } 264 finalList = [] 265 i = 0 266 while i < len(data): 267 line = data[i] 268 line = re.sub(comment, '', line).strip() 269 if line == '': 270 i += 1 271 continue 272 273 # Check for line continuation character '\' 274 while line[-1] == '\\' and i < len(data): 275 i += 1 276 line = line[:-1] + data[i] 277 line = re.sub(comment, '', line).strip() 278 279 if line[0] == '/': # Found an organizer 280 defaults = self.parseDeviceEntry(line, {}) 281 defaults['devicePath'] = defaults['deviceName'] 282 del defaults['deviceName'] 283 284 else: 285 configs = self.parseDeviceEntry(line, defaults) 286 if configs: 287 finalList.append(configs) 288 i += 1 289 290 return finalList
291
292 - def parseDeviceEntry(self, line, defaults):
293 """ 294 Build a dictionary of properties from one line's input 295 296 @parameter line: string containing one device's info 297 @type line: string 298 @parameter defaults: dictionary of default settings 299 @type defaults: dictionary 300 @return: parsed device entry 301 @rtype: dictionary 302 """ 303 options = None 304 if line.find(' ') > 0: 305 name, options = line.split(' ', 1) 306 else: 307 name = line 308 309 configs = defaults.copy() 310 configs['deviceName'] = name 311 312 if options: 313 try: 314 configs.update( eval( 'dict(' + options + ')' ) ) 315 except: 316 self.log.error( "Unable to parse the entry for %s -- skipping" % name ) 317 self.log.error( "Raw string: %s" % options ) 318 return None 319 320 return configs
321 322 323 if __name__=='__main__': 324 batchLoader = BatchDeviceLoader() 325 326 if batchLoader.options.show_options: 327 print "Options = %s" % sorted( batchLoader.loader_args.keys() ) 328 help(batchLoader.loader) 329 sys.exit(0) 330 331 if batchLoader.options.sample_configs: 332 print batchLoader.sample_configs 333 sys.exit(0) 334 335 device_list = batchLoader.loadDeviceList() 336 batchLoader.processDevices(device_list) 337