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 # 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 try: 184 devobj.collectDevice(setlog=self.options.showModelOutput) 185 except (SystemExit, KeyboardInterrupt): 186 self.log.info("User interrupted modelling") 187 break 188 except Exception, ex: 189 self.log.exception("Modelling error" ) 190 191 commit() 192 processed += 1 193 194 self.log.info( "Processed %d of %d devices" % (processed, len(device_list)))
195
196 - def getDevice(self, device_specs):
197 """ 198 Find or create the specified device 199 200 @parameter device_specs: device creation dictionary 201 @type device_specs: dictionary 202 @return: device or None 203 @rtype: DMD device object 204 """ 205 if 'deviceName' not in device_specs: 206 return None 207 name = device_specs['deviceName'] 208 devobj = self.dmd.Devices.findDevice(name) 209 if devobj is not None: 210 self.log.info("Found existing device %s" % name) 211 return devobj 212 213 specs = {} 214 for key in self.loader_args: 215 if key in device_specs: 216 specs[key] = device_specs[key] 217 218 try: 219 self.log.info("Creating device %s" % name) 220 221 # Do NOT model at this time 222 specs['discoverProto'] = 'none' 223 224 self.loader(**specs) 225 devobj = self.dmd.Devices.findDevice(name) 226 if devobj is None: 227 self.log.error("Unable to find newly created device %s -- skipping" \ 228 % name) 229 except Exception, ex: 230 self.log.exception("Unable to load %s -- skipping" % name ) 231 232 return devobj
233
234 - def buildOptions(self):
235 """ 236 Add our command-line options to the basics 237 """ 238 ZCmdBase.buildOptions(self) 239 240 self.parser.add_option('--show_options', 241 dest="show_options", default=False, 242 action="store_true", 243 help="Show the various options understood by the loader") 244 245 self.parser.add_option('--sample_configs', 246 dest="sample_configs", default=False, 247 action="store_true", 248 help="Show an example configuration file.") 249 250 self.parser.add_option('--showModelOutput', 251 dest="showModelOutput", default=True, 252 action="store_false", 253 help="Show modelling activity")
254
255 - def parseDevices(self, data):
256 """ 257 From the list of strings in rawDevices, construct a list 258 of device dictionaries, ready to load into Zenoss. 259 260 @parameter data: list of strings representing device entries 261 @type data: list of strings 262 @return: list of parsed device entries 263 @rtype: list of dictionaries 264 """ 265 if not data: 266 return [] 267 268 comment = re.compile(r'#.*') 269 270 defaults = {'devicePath':"/Discovered" } 271 finalList = [] 272 i = 0 273 while i < len(data): 274 line = data[i] 275 line = re.sub(comment, '', line).strip() 276 if line == '': 277 i += 1 278 continue 279 280 # Check for line continuation character '\' 281 while line[-1] == '\\' and i < len(data): 282 i += 1 283 line = line[:-1] + data[i] 284 line = re.sub(comment, '', line).strip() 285 286 if line[0] == '/': # Found an organizer 287 defaults = self.parseDeviceEntry(line, {}) 288 defaults['devicePath'] = defaults['deviceName'] 289 del defaults['deviceName'] 290 291 else: 292 configs = self.parseDeviceEntry(line, defaults) 293 if configs: 294 finalList.append(configs) 295 i += 1 296 297 return finalList
298
299 - def parseDeviceEntry(self, line, defaults):
300 """ 301 Build a dictionary of properties from one line's input 302 303 @parameter line: string containing one device's info 304 @type line: string 305 @parameter defaults: dictionary of default settings 306 @type defaults: dictionary 307 @return: parsed device entry 308 @rtype: dictionary 309 """ 310 options = None 311 if line.find(' ') > 0: 312 name, options = line.split(' ', 1) 313 else: 314 name = line 315 316 configs = defaults.copy() 317 configs['deviceName'] = name 318 319 if options: 320 try: 321 configs.update( eval( 'dict(' + options + ')' ) ) 322 except: 323 self.log.error( "Unable to parse the entry for %s -- skipping" % name ) 324 self.log.error( "Raw string: %s" % options ) 325 return None 326 327 return configs
328 329 330 if __name__=='__main__': 331 batchLoader = BatchDeviceLoader() 332 333 if batchLoader.options.show_options: 334 print "Options = %s" % sorted( batchLoader.loader_args.keys() ) 335 help(batchLoader.loader) 336 sys.exit(0) 337 338 if batchLoader.options.sample_configs: 339 print batchLoader.sample_configs 340 sys.exit(0) 341 342 device_list = batchLoader.loadDeviceList() 343 batchLoader.processDevices(device_list) 344