1
2 """
3 This module provides a set of tools to access Common Information Model (CIM) classes
4 through Windows Management Instrumentation (WMI).
5
6 WMI API can be accessed by various protocols such as
7 * Windows shell -- by using 'wmic' command line tool
8 * WMI protocol -- WMI natively provides remote connection capabilities
9 * PowerShell -- PowerShell 2.0 also provides an access to WMI and remote connection capabilities
10
11 This module provides a set of classes which are created on top of Universal Discovery clients and provide unified interface to access CIM.
12 That said, the code which uses this module is totally detached from the underlying protocol.
13 """
14 import logger
15 import shellutils
16 import re
17
18 from java.util import Comparator
19 from java.util import TreeSet
20 from java.util import Random
21 from java.lang import System
22 from java.lang import Long
23 from java.lang import Exception as JavaException
24
25 from appilog.common.system.types import ObjectStateHolder
26 from appilog.common.system.types import AttributeStateHolder
27
28
29 from com.hp.ucmdb.discovery.library.communication.downloader.cfgfiles import GeneralSettingsConfigFile
30
32 """
33 This class is a base abstract class for converters.
34 Converters are used to transform values obtained from CIM.
35 @deprecated: This class is deprecated
36 """
38 raise ValueError, 'Not implemented'
39
41 """
42 This is a default converter which does just nothing.
43 @deprecated: This class is deprecated
44 """
47
48 "Instance of the default 'no operation' (NOOP) converter. This API is deprecated"
49 NOOP_CONVERTER = NoopConverter()
50
52 """
53 This converter considers value obtained from CIM as a key in the dictionary.
54 @deprecated: This class is deprecated
55 """
58
60 """
61 Returns value from the dictionary corresponding to 'originalValue' key
62 @raise UnmapperValueException: if there is no value registered for passed key.
63 """
64 if self.map.has_key(originalValue):
65 return self.map[originalValue]
66 else:
67 raise UnmappedValueException, originalValue
68
70 """
71 This converter allows making conversion by custom function.
72 For example if it is required to parse out a part of the string by some regular expression.
73 @deprecated: This class is deprecated
74 """
76 """
77 @param function: callable object which will do actual conversion.
78 """
79 self.function = function
80
82 """
83 Converts provided value by function passed to converter constructor.
84 This method does not make any special exception handling. It just invokes conversion function.
85 """
86 return self.function(originalValue)
87
89 """
90 This is a stub class used for result objects.
91 All properties are added here dynamically at runtime.
92 @see: WmiQueryBuilder.parseResults
93 """
94 pass
95
97 """
98 This class is used to sort queried properties names.
99 Sorting is important because it allows to specify queried class properties in arbitrary order.
100 """
101 - def compare(self, element1, element2):
102 return cmp(element1.columnName, element2.columnName)
103
105 """
106 This class represents queried CIM class property
107 """
108 - def __init__(self, columnName, attributeName, type):
109 """
110 @param columnName: The name of the queried property
111 @param attributeName: deprecated
112 @type type: deprecated
113 """
114 self.columnName = columnName
115 self.attributeName = attributeName
116 self.type = type
117
118
120 """
121 This is a base class for all QueryBuilders.
122 QueryBuilder is a class which provides single
123 interface to compose WMI query regardless to the underlying protocol
124 """
126 "@param objectName: the name of the queried CIM class"
127 self.queryElements = TreeSet(ColumnNameBasedComparator())
128 self.objectName = objectName
129 self.whereClause = None
130
131 - def addQueryElement(self, columnName, attributeName = None, type = 'string'):
132 """
133 @deprecated: This method should not be used by clients
134 @see: addWmiObjectProperties method instead
135 """
136 if not attributeName:
137 attributeName = columnName
138 queryElement = QueryElement(columnName, attributeName, type)
139 self.queryElements.add(queryElement)
140
142 """
143 @types: *str -> BaseWmiQueryBuilder
144 @param columnNames: the list of the names of the queried properties, order of properties if not important.
145 """
146 for columnName in columnNames:
147 self.addQueryElement(columnName)
148 return self
149
151 """
152 This method allows to add 'WHERE' clause to the WMI query
153 @types: str -> None
154 """
155 self.whereClause = whereClause
156
158 """
159 Abstract method for query buidling
160 """
161 raise NotImplemented, "buildQuery"
162
164 """
165 Abstract method for results parsing
166 """
167 raise NotImplemented, "parseResults"
168
170 """
171 Returns queried CIM class name
172 """
173 return self.objectName
174
176 """
177 This class represents QueryBuilder for WMI protocol.
178 """
179
180 WMI_QUERY_TEMPLATE = 'SELECT %s FROM %s'
181
184
186 """
187 This method builds WMI query using the following template:
188 SELECT property [, property ...] FROM objectName [WHERE whereClause]
189 """
190 COMMA = ', '
191 columnNamesList = [element.columnName for element in self.queryElements if element.columnName]
192 columnNames = COMMA.join(columnNamesList)
193
194 wmiQuery = self.WMI_QUERY_TEMPLATE % (columnNames, self.objectName)
195
196 if self.whereClause:
197 wmiQuery += ' WHERE ' + self.whereClause
198
199 return wmiQuery
200
202 """
203 resultSet -> list(ResultItem)
204 This method forms the result of WMI query. The result is a list of objects of type ResultItem
205 with dynamically added properties corresponding to the queried properties names.
206 """
207 resultItems = []
208 table = resultSet.asTable()
209
210 for rowIndex in range(len(table)):
211 columnIndex = 0
212 resultItem = ResultItem()
213 iterator = self.queryElements.iterator()
214 while iterator.hasNext():
215 queryElement = iterator.next()
216 name = queryElement.attributeName
217 setattr(resultItem, name, table[rowIndex][columnIndex])
218 columnIndex += 1
219 resultItems.append(resultItem)
220
221 return resultItems
222
223
225 """
226 This class forms WMI query to be executed by 'wmic' command line tool
227 """
228
229 WMIC_QUERY_TEMPLATE = 'wmic %(output)s%(namespace)s%(path)s%(object)s%(where)s get %(properties)s /value < %%SystemRoot%%\\win.ini'
230
232 BaseWmiQueryBuilder.__init__(self, objectName)
233 self._keyAttributesLowerCase = []
234 self.__outputFile = None
235 self.__usePathCommand = 0
236 self._namespace = None
237 self._splitListOutput = None
238
240 self.__outputFile = outputFile
241
243 self.__usePathCommand = value
244
246 """
247 Sets queried object namespace
248 @types: str -> void
249 """
250 if namespace:
251 self._namespace = namespace
252
254 self._splitListOutput = value
255
257 """
258 Builds wmic query using template:
259 wmic %(output)s%(namespace)s%(path)s%(object)s%(where)s get %(properties)s /value < %%SystemRoot%%\\win.ini
260 """
261
262 queryParameters = {}
263
264 queryParameters['output'] = self.__outputFile and '/output:"%s" ' % self.__outputFile or ''
265
266 queryParameters['namespace'] = self._namespace and '/namespace:%s ' % self._namespace or ''
267
268 queryParameters['path'] = self.__usePathCommand and 'path ' or ''
269
270 queryParameters['object'] = self.objectName
271
272 queryParameters['where'] = self.whereClause and ' where "%s"' % self.whereClause or ''
273
274 COMMA = ', '
275 columnNamesList = [element.columnName for element in self.queryElements if element.columnName]
276 columnNames = COMMA.join(columnNamesList)
277 self._keyAttributesLowerCase += [name.lower() for name in columnNamesList]
278
279 queryParameters['properties'] = columnNames
280
281 return self.WMIC_QUERY_TEMPLATE % queryParameters
282
284 if fieldValue and re.match("\s*\{.*\}\s*", fieldValue):
285 return 1
286
288 resultList = []
289 if fieldValue:
290 resultList = re.split('[\,\"]+', fieldValue.replace('{', '').replace('}', ''))
291 return map(lambda elem: elem.strip(), resultList)
292
294 resultItems = []
295
296 output = output.strip()
297 firstToken = output[0:output.find(separator)]
298
299 output = '\n%s' % output
300
301 for fragment in output.split('\n%s%s' % (firstToken, separator)):
302 if not fragment: continue
303 attributes = {}
304 previousKey = None
305
306 for line in (('%s%s%s' % (firstToken, separator, fragment)).strip().split('\n')):
307 keyIndex = line.find(separator)
308 key = line[0:keyIndex].lower().strip()
309 if keyIndex == -1 or key not in self._keyAttributesLowerCase:
310 if previousKey:
311 attributes[previousKey] += line and line.strip()
312 else:
313 value = line[keyIndex + 1:]
314 attributes[key] = value and value.strip()
315 previousKey = key
316
317 if not attributes: continue
318 resultItem = ResultItem()
319 iterator = self.queryElements.iterator()
320 while iterator.hasNext():
321 queryElement = iterator.next()
322 name = queryElement.attributeName
323 columnName = queryElement.columnName.lower()
324 value = attributes.get(columnName)
325 value = value and value.strip()
326 if self._splitListOutput and self.__isListType(value):
327 value = self.__parseAsList(value)
328 setattr(resultItem, name, value)
329 resultItems.append(resultItem)
330 return resultItems
331
333 ''' PowerShell specific query builder. It uses PowerShell native WMI command line calls.
334 Added some fake methods in order to provide compatibility and abstraction layer from the exact call methods of shell WmicQueryBuilder
335 '''
336 QUERY_TEMPLATE = 'Get-WmiObject %(namespace)s -Query "SELECT %(properties)s FROM %(object)s %(where)s" | Format-List %(properties)s'
339
341 logger.warn("Intermediate file is not supported in WMI by PowerShell")
342
344 logger.warn("WMI class names supported only. No aliases")
345
347 if namespace:
348 self._namespace = namespace
349
351 self._splitListOutput = value
352
354
355 queryParameters = {}
356
357 queryParameters['namespace'] = self._namespace and '-Namespace "%s" ' % self._namespace or ''
358
359 queryParameters['object'] = self.objectName
360
361 queryParameters['where'] = self.whereClause and ' WHERE %s' % self.whereClause.replace('"', "'") or ''
362
363 COMMA = ', '
364 columnNamesList = [element.columnName for element in self.queryElements if element.columnName]
365 columnNames = COMMA.join(columnNamesList)
366 self._keyAttributesLowerCase += [name.lower() for name in columnNamesList]
367
368 queryParameters['properties'] = columnNames
369
370 return self.QUERY_TEMPLATE % queryParameters
371
373 """Base class for WMI agent"""
374
376 '@types: BaseWmiQueryBuilder, int -> list(ResultItem)'
377 raise NotImplemented
378
381
384
385
387 'Agent wraps WMI agent'
388 - def __init__(self, wmiClient, Framework = None):
389 self.wmiClient = wmiClient
390
392 '''@types: WmiQueryBuilder, int -> list(ResultItem)
393 @param timeout: parameter is not used, provided only for unique interface with WmicAgent
394 '''
395 if timeout:
396 logger.warn("Specified timeout for query by WMI protocol has no effect as it is not supported by client")
397 query = queryBuilder.buildQuery()
398 resultSet = self.wmiClient.executeQuery(query)
399 return queryBuilder.parseResults(resultSet)
400
402 self.wmiClient.close()
403
404
406 """
407 This class wraps WMI client to provide an interface to run WMI queries.
408 """
409
410 __PROPERTY_NAME_USE_INTERMEDIATE_FILE = 'useIntermediateFileForWmic'
411 __DEFAULT_REMOTE_TEMP_SHARE = "admin$\\Temp"
412 __DEFAULT_REMOTE_INTERNAL_TEMP_PATH = "%SystemRoot%\\Temp"
413 __WMIC_OUTPUT_ENCODING = "utf-16"
414
416 '@types: Shell -> None'
417 self.shell = shell
418 globalSettings = GeneralSettingsConfigFile.getInstance()
419 self.useIntermediateFile = globalSettings.getPropertyBooleanValue(WmicAgent.__PROPERTY_NAME_USE_INTERMEDIATE_FILE, 0)
420
422 """
423 @types: WmiQueryBuilder, int -> list(ResultItem)
424 @param timeout: parameter is not used, provided only for unique interface with WmicAgent
425 """
426 output = None
427 if self.useIntermediateFile:
428 output = self.getWmiDataWithIntermediateFile(queryBuilder, timeout)
429 else:
430 output = self.executeWmiQuery(queryBuilder, timeout)
431 return queryBuilder.parseResults(output)
432
434 """ Execute query and return raw string output
435 @types: WmicQueryBuilder[, int = 0] -> str
436 @raise ValueError: if WMIC query execution failed
437 """
438 query = queryBuilder.buildQuery()
439 result = self.shell.execCmd(query, timeout)
440 errorCode = self.shell.getLastCmdReturnCode()
441 if errorCode:
442 raise ValueError, "Wmic query execution failed. %s Error code %s" % (result, errorCode)
443 return result
444
488
490 random = Random().nextLong()
491 systime = System.currentTimeMillis()
492 return Long.toHexString( random ^ systime )
493
504
507
510 '@types: shellutils.PowerShell -> None'
511 self.shell = shell
512
516
518 """ Execute query and return raw string output
519 @types: PowerShelWmilQueryBuilder[, int = 0] -> str
520 @raise ValueError: if query execution failed
521 """
522 query = queryBuilder.buildQuery()
523 output = self.shell.execCmd(query, timeout)
524 errorCode = self.shell.getLastCmdReturnCode()
525 if errorCode != 0:
526 raise ValueError, "Powershell WMI query execution failed. %s Error code %s" % (output, errorCode)
527 return output
528
531
532
534 'Provides access to WMI using WMI agent'
536 'WMI agent'
537 self.__agent = WmiAgent(wmiClient)
538
540 '@types: -> WmiAgent'
541 return self.__agent
542
544 '@types: str, str -> WmiQueryBuilder'
545 return WmiQueryBuilder(className)
546
550
552 '@types: -> WmicAgent'
553 return self.__agent
554
561
563 'Provides access to WMI using powershell commands'
566
568 '@types: -> WmiPowerShellAgent or WmicAgent'
569 return self.__agent
570
576
578 """
579 This method returns Provider to work with WMI.
580 @rtype: WmiAgentProvider
581 @param client: row client. Supported client types: Windows shell, PowerShell, WMI
582 @raise ValueError: if there is no WMI provider implemented for client
583 """
584 clientType = client.getClientType()
585 if clientType == 'wmi':
586 return WmiAgentProvider(client)
587 if (clientType == 'ntadmin') or (clientType == 'ssh') or (clientType == 'uda'):
588 return WmicProvider(client)
589 if clientType == 'powershell':
590 return PowerShellWmiProvider(client)
591 raise ValueError, 'No WMI provider for client type: %s' % clientType
592
594 """
595 @deprecated:
596 """
597 - def __init__(self, attributeName, sourceAttributeName, type, converter):
598 self.attributeName = attributeName
599
600 if sourceAttributeName:
601 self.sourceAttributeName = sourceAttributeName
602 else:
603 self.sourceAttributeName = attributeName
604
605 self.type = type
606 self.converter = converter
607
609 sourceValue = getattr(sourceElement, self.sourceAttributeName)
610
611 try:
612 convertedValue = self.converter.convert(sourceValue)
613 except ConversionException:
614 raise AttributeMappingException, self.attributeName
615
616 osh.setAttribute(AttributeStateHolder(self.attributeName, convertedValue, self.type))
617
619 """
620 @deprecated:
621 """
623 self.oshName = oshName
624 self.attributeMappings = []
625
627 attributeMapping = AttributeMapping(attributeName, sourceAttributeName, type, converter)
628 self.attributeMappings.append(attributeMapping)
629
630 - def createOSHs(self, sourceElements, OSHVResult = None):
631 oshs = []
632 for sourceElement in sourceElements:
633 osh = self.createOSH(sourceElement)
634 oshs.append(osh)
635
636 if OSHVResult:
637 for osh in oshs:
638 OSHVResult.add(osh)
639
640 return oshs
641
642 - def fillOSH(self, osh, sourceElement):
643 for attributeMapping in self.attributeMappings:
644 attributeMapping.setAttribute(osh, sourceElement)
645
647 osh = ObjectStateHolder(self.oshName)
648 self.fillOSH(osh, sourceElement)
649 return osh
650
652 """
653 @deprecated:
654 """
655 - def __init__(self, wmiObjectName, oshName, wmiAgent):
656 self.wmiAgent = wmiAgent
657 self.oshMapping = OshMapping(oshName)
658 self.queryBuilder = WmiQueryBuilder(wmiObjectName)
659
661 self.queryBuilder.addQueryElement(wmiColumnName)
662 self.oshMapping.defineMapping(wmiColumnName, oshAttributeName, oshAttributeType, converter)
663
665 wmiData = self.wmiAgent.getWmiData(self.queryBuilder)
666 return self.oshMapping.createOSHs(wmiData)
667
668
671
674
677
680
681
682 Language = shellutils.Language
683
684 LANGUAGES = shellutils.LANGUAGES
685
686 DEFAULT_LANGUAGE = shellutils.DEFAULT_LANGUAGE
687
689 """
690 Discoverer determines the language of target system via WMI queries.
691 Currently it implements the same flow as in shellutils.
692 Supports both WMI and shell/wmic clients, however using it for shell is redundant since shellutils already detects language.
693 """
695 self._provider = wmiProvider
696
698 language = None
699 osInfo = self._getOsInfo()
700 if osInfo:
701 osLanguage = osInfo.OSLanguage
702 language = self._getLanguageByOsLanguage(osLanguage)
703
704 if language is None:
705 codeSet = osInfo.CodeSet
706 language = self._getLanguageByCodeSet(codeSet)
707
708 if language is None:
709 logger.debug("Failed to determine target operating system language, falling back to default language")
710 language = DEFAULT_LANGUAGE
711
712 logger.debug('Bundle postfix %s' % language.bundlePostfix)
713 return language
714
716 queryBuilder = self._provider.getBuilder('Win32_OperatingSystem')
717 queryBuilder.addWmiObjectProperties('CodeSet', 'OSLanguage')
718 agent = self._provider.getAgent()
719 results = agent.getWmiData(queryBuilder)
720 if results is not None and len(results) == 1:
721 return results[0]
722 else:
723 logger.warn("Query for OS language details has failed")
724
726 if osLanguage and osLanguage.isdigit():
727 try:
728 intOsLanguage = int(osLanguage)
729 for lang in LANGUAGES:
730 if intOsLanguage in lang.wmiCodes:
731 return lang
732 except:
733 pass
734
736 if codeSet and codeSet.isdigit():
737 try:
738 intCodeSet = int(codeSet)
739 for lang in LANGUAGES:
740 if intCodeSet == lang.codepage:
741 return lang
742 except:
743 pass
744