/
_cim_operations.py
10242 lines (8301 loc) · 436 KB
/
_cim_operations.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#
# (C) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
# (C) Copyright 2006-2007 Novell, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Tim Potter <tpot@hp.com>
# Author: Martin Pool <mbp@hp.com>
# Author: Bart Whiteley <bwhiteley@suse.de>
# Author: Ross Peoples <ross.peoples@gmail.com>
#
# pylint: disable=line-too-long
"""
Objects of the :class:`~pywbem.WBEMConnection` class represent a connection to
a WBEM server or WBEM listener.
All WBEM operations defined in :term:`DSP0200` can be issued across this connection.
Each method of this class corresponds directly to a WBEM operation.
========================================================== ==============================================================
WBEMConnection methods targeting a server Purpose
========================================================== ==============================================================
:meth:`~pywbem.WBEMConnection.EnumerateInstances` Enumerate the instances of a class (including instances of its
subclasses)
:meth:`~pywbem.WBEMConnection.EnumerateInstanceNames` Enumerate the instance paths of instances of a class
(including instances of its subclasses).
:meth:`~pywbem.WBEMConnection.GetInstance` Retrieve an instance
:meth:`~pywbem.WBEMConnection.ModifyInstance` Modify the property values of an instance
:meth:`~pywbem.WBEMConnection.CreateInstance` Create an instance
:meth:`~pywbem.WBEMConnection.DeleteInstance` Delete an instance
:meth:`~pywbem.WBEMConnection.Associators` Retrieve the instances (or classes) associated to a source
instance (or source class)
:meth:`~pywbem.WBEMConnection.AssociatorNames` Retrieve the instance paths of the instances (or classes)
associated to a source instance (or source class)
:meth:`~pywbem.WBEMConnection.References` Retrieve the association instances (or association classes)
that reference a source instance (or source class)
:meth:`~pywbem.WBEMConnection.ReferenceNames` Retrieve the instance paths of the association instances (or
association classes) that reference a source instance (or
source class)
:meth:`~pywbem.WBEMConnection.InvokeMethod` Invoke a method on a target instance or on a target class
:meth:`~pywbem.WBEMConnection.ExecQuery` Execute a query in a namespace
---------------------------------------------------------- --------------------------------------------------------------
:meth:`~pywbem.WBEMConnection.IterEnumerateInstances` Iterator API that uses either OpenEnumerateInstances and
PullInstancesWithPath or EnumerateInstances depending on
the attributes and existence of pull operations in the
server.
:meth:`~pywbem.WBEMConnection.IterEnumerateInstancePaths` Iterator API that uses either OpenEnumerateInstances and
PullInstancesWithPath or EnumerateInstances depending on
the attributes and existence of pull operations in the
server.
:meth:`~pywbem.WBEMConnection.IterAssociatorInstances` Iterator API that uses either OpenAssociatorInstances and
PullInstancesWithPath or Associators depending on
the attributes and existence of pull operations in the
server.
:meth:`~pywbem.WBEMConnection.IterAssociatorInstancePaths` Iterator API that uses either OpenAssociatorInstances and
PullInstancesWithPath or Associators depending on
the attributes and existence of pull operations in the
server.
:meth:`~pywbem.WBEMConnection.IterReferenceInstances` Iterator API that uses either OpenReferenceInstances and
PullInstancesWithPath or References depending on
the attributes and existence of pull operations in the
server.
:meth:`~pywbem.WBEMConnection.IterReferenceInstancePaths` Iterator API that uses either OpenReferenceInstances and
PullInstancesWithPath or References depending on
the attributes and existence of pull operations in the
server.
:meth:`~pywbem.WBEMConnection.IterQueryInstances` Iterator API that uses either OpenQueryInstances and
PullInstances or ExecQuery depending on
the attributes and existence of pull operations in the
server.
---------------------------------------------------------- --------------------------------------------------------------
:meth:`~pywbem.WBEMConnection.OpenEnumerateInstances` Open enumeration session to retrieve instances of
of a class (including instances of its subclass)
:meth:`~pywbem.WBEMConnection.OpenEnumerateInstancePaths` Open enumeration session to retrieve instances of a class
(including instances of its subclass)
:meth:`~pywbem.WBEMConnection.OpenAssociatorInstances` Open enumeration session to retrieve the instances
associated to a source instance
:meth:`~pywbem.WBEMConnection.OpenAssociatorInstancePaths` Open enumeration session to retrieve the instances
associated to a source instance
:meth:`~pywbem.WBEMConnection.OpenReferenceInstances` Open enumeration session to retrieve the instances
that reference a source instance
:meth:`~pywbem.WBEMConnection.OpenReferenceInstancePaths` Open enumeration session to retrieve the instances that
reference a source instance
:meth:`~pywbem.WBEMConnection.OpenQueryInstances` Open query request to retrieve instances defined by
the query parameter in a namespace
:meth:`~pywbem.WBEMConnection.PullInstancesWithPath` Continue enumeration session opened with
OpenEnumerateInstances, OpenAssociatorInstances, or
OpenReferenceinstances
:meth:`~pywbem.WBEMConnection.PullInstancePaths` Continue enumeration session opened with
OpenEnumerateInstancePaths, OpenAssociatorInstancePaths,
or OpenReferenceInstancePaths
:meth:`~pywbem.WBEMConnection.PullInstances` Continue enumeration of enumeration session opened
with OpenQueryInstances
:meth:`~pywbem.WBEMConnection.CloseEnumeration` Close an enumeration session in process.
---------------------------------------------------------- --------------------------------------------------------------
:meth:`~pywbem.WBEMConnection.EnumerateClasses` Enumerate the subclasses of a class, or the top-level classes
in a namespace
:meth:`~pywbem.WBEMConnection.EnumerateClassNames` Enumerate the names of subclasses of a class, or of the
top-level classes in a namespace
:meth:`~pywbem.WBEMConnection.GetClass` Retrieve a class
:meth:`~pywbem.WBEMConnection.ModifyClass` Modify a class
:meth:`~pywbem.WBEMConnection.CreateClass` Create a class
:meth:`~pywbem.WBEMConnection.DeleteClass` Delete a class
---------------------------------------------------------- --------------------------------------------------------------
:meth:`~pywbem.WBEMConnection.EnumerateQualifiers` Enumerate qualifier declarations
:meth:`~pywbem.WBEMConnection.GetQualifier` Retrieve a qualifier declaration
:meth:`~pywbem.WBEMConnection.SetQualifier` Create or modify a qualifier declaration
:meth:`~pywbem.WBEMConnection.DeleteQualifier` Delete a qualifier declaration
========================================================== ==============================================================
NOTE: The method EnumerationCount is to be deprecated from the DMTF specs
and has not been implemented by any WBEM servers so was not implemented
in pywbem.
========================================================== ==============================================================
WBEMConnection methods targeting a listener Purpose
========================================================== ==============================================================
:meth:`~pywbem.WBEMConnection.ExportIndication` Send an indication to a WBEM listener
========================================================== ==============================================================
""" # noqa: E501
# pylint: enable=line-too-long
# Note: When used before module docstrings, Pylint scopes the disable statement
# to the whole rest of the file, so we need an enable statement.
# This module is meant to be safe for 'import *'.
from __future__ import absolute_import
import os
import re
from datetime import datetime, timedelta
from xml.dom import minidom
from collections import namedtuple
import logging
import warnings
import requests
from requests.packages import urllib3
import six
from . import _cim_xml
from .config import DEFAULT_ITER_MAXOBJECTCOUNT, AUTO_GENERATE_SFCB_UEP_HEADER
from ._cim_constants import DEFAULT_NAMESPACE, CIM_ERR_NOT_SUPPORTED, \
CIM_ERR_FAILED, DEFAULT_TIMEOUT
from ._cim_types import CIMType, CIMDateTime, atomic_to_cim_xml
from ._nocasedict import NocaseDict
from ._cim_obj import CIMInstance, CIMInstanceName, CIMClass, CIMClassName, \
CIMParameter, CIMQualifierDeclaration, tocimxml, cimvalue
from ._cim_http import get_cimobject_header, wbem_request, parse_url
from ._tupleparse import TupleParser
from ._tupletree import xml_to_tupletree_sax
from ._exceptions import CIMXMLParseError, XMLParseError, CIMError
from ._exceptions import ConnectionError # pylint: disable=redefined-builtin
from ._statistics import Statistics
from ._recorder import LogOperationRecorder
from ._logging import DEFAULT_LOG_DETAIL_LEVEL, LOG_DESTINATIONS, \
LOGGER_API_CALLS_NAME, LOGGER_HTTP_NAME, LOG_DETAIL_LEVELS, \
LOGGER_SIMPLE_NAMES
from ._utils import _ensure_unicode, _format
__all__ = ['WBEMConnection', 'IterQueryInstancesReturn']
URLLIB3_VERSION_INFO = tuple(map(int, urllib3.__version__.split('.')[0:3]))
# Parameters for urllib3.Retry, see
# https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html
HTTP_TOTAL_RETRIES = None # total: Override for total number of HTTP
HTTP_CONNECT_RETRIES = 2 # connect: Max number of HTTP connect retries
HTTP_READ_RETRIES = 0 # read: Max number of HTTP read retries
HTTP_STATUS_RETRIES = 0 # status: Max number of HTTP status retries
HTTP_OTHER_RETRIES = 0 # other: Max number of other HTTP retries
HTTP_MAX_REDIRECTS = 5 # redirect: Max number of HTTP redirects
HTTP_RETRY_BACKOFF_FACTOR = 0.1 # backoff_factor: Backoff factor for retries
# urllib3 1.26.0 started issuing a DeprecationWarning for using the
# 'method_whitelist' init parameter of Retry and announced its removal in
# version 2.0. The replacement parameter is 'allowed_methods'.
# Find out which init parameter to use:
with warnings.catch_warnings():
warnings.filterwarnings('error')
try:
urllib3.Retry(method_whitelist={})
except (DeprecationWarning, TypeError):
RETRY_METHODS_PARM = 'allowed_methods'
else:
RETRY_METHODS_PARM = 'method_whitelist'
RETRY_KWARGS = {
'total': HTTP_TOTAL_RETRIES,
'connect': HTTP_CONNECT_RETRIES,
'read': HTTP_READ_RETRIES,
'status': HTTP_STATUS_RETRIES,
'redirect': HTTP_MAX_REDIRECTS,
'backoff_factor': HTTP_RETRY_BACKOFF_FACTOR
}
# The urllib3 default does not allow retries on POST operations. This definition
# is urllib3 version dependent
try:
RETRY_KWARGS[RETRY_METHODS_PARM] = urllib3.Retry.DEFAULT_ALLOWED_METHODS
except (DeprecationWarning, AttributeError):
RETRY_KWARGS[RETRY_METHODS_PARM] = urllib3.Retry.DEFAULT_METHOD_WHITELIST
if URLLIB3_VERSION_INFO >= (1, 26, 0):
RETRY_KWARGS['other'] = HTTP_OTHER_RETRIES
# Global named tuples. Used by the pull operation responses to return
# (entities, end_of_sequence, and enumeration_context) to the caller.
# openenumerateInstances, OpenAssociators, etc and PullInstanceWithPath
# responses
# pylint: disable=invalid-name
pull_path_result_tuple = namedtuple("pull_path_result_tuple",
["paths", "eos", "context"])
# OpenEnumerateInstancePaths, etc. and PullInstancePath responses
pull_inst_result_tuple = namedtuple("pull_inst_result_tuple",
["instances", "eos", "context"])
# openqueryInstances and PullInstance responses
pull_query_result_tuple = namedtuple("pull_query_result_tuple",
["instances", "eos", "context",
"query_result_class"])
def _to_pretty_xml(xml_item):
"""
Common function to produce a prettified XML string from an input XML item
that can be a string or a minidom.Element object.
This function is NOT intended to be used in major code paths since it uses
the minidom module to produce the prettified XML and to parse the input XML
if provided as a string. This processing uses a lot of memory and takes
significant elapsed time.
"""
if isinstance(xml_item, (six.text_type, six.binary_type)):
xml_item = minidom.parseString(xml_item)
pretty_result = xml_item.toprettyxml(indent=' ')
# remove extra empty lines
return re.sub(r'>( *[\r\n]+)+( *)<', r'>\n\2<', pretty_result)
def _iparam_propertylist(property_list):
"""
Validate property_list input parameter and return it as a tuple/list,
or None.
This is a test for a particular issue where the user supplies a single
string instead of a list for a PropertyList parameter. It prevents
an XML error.
"""
if property_list is None:
pass
elif isinstance(property_list, (list, tuple)):
pass
elif isinstance(property_list, six.string_types):
property_list = [property_list]
else:
raise TypeError(
_format("The 'PropertyList' parameter of the WBEMConnection "
"operation has invalid type {0} (must be a string, or "
"a list/tuple of strings",
type(property_list)))
return property_list
def _validate_OperationTimeout(OperationTimeout):
"""
Validate the OperationTimeout input parameter for the Iter...() and
Open...() operations.
Parameters:
OperationTimeout: Must be of integer type >= 0, or None.
Raises:
TypeError: Invalid type
ValueError: Invalid value
"""
if not isinstance(OperationTimeout, (six.integer_types, type(None))):
raise TypeError(
_format("The 'OperationTimeout' parameter of the WBEMConnection "
"operation has invalid type {0} (must be integer)",
type(OperationTimeout)))
if OperationTimeout is not None and OperationTimeout < 0:
raise ValueError(
_format("The 'OperationTimeout' parameter of the WBEMConnection "
"operation has invalid value {0!r} (must be >= 0 or None)",
OperationTimeout))
def _validate_MaxObjectCount_Iter(MaxObjectCount):
"""
Validate the MaxObjectCount input parameter for the Iter...() operations.
Parameters:
MaxObjectCount: Must be integer type and > 0. Must not be None.
Raises:
TypeError: Invalid type
ValueError: Invalid value, including None
"""
if not isinstance(MaxObjectCount, (six.integer_types, type(None))):
raise TypeError(
_format("The 'MaxObjectCount' parameter of the WBEMConnection "
"operation has invalid type {0} (must be integer)",
type(MaxObjectCount)))
if MaxObjectCount is None or MaxObjectCount <= 0:
raise ValueError(
_format("The 'MaxObjectCount' parameter of the WBEMConnection "
"operation has invalid value {0!r} (must be > 0)",
MaxObjectCount))
def _validate_MaxObjectCount_OpenPull(MaxObjectCount):
"""
Validate the MaxObjectCount input parameter for the Open...() and Pull...()
operations.
Parameters:
MaxObjectCount: Must be integer type and >= 0, or None
Raises:
TypeError: Invalid type
ValueError: Invalid value
"""
if MaxObjectCount is None:
return
if not isinstance(MaxObjectCount, six.integer_types):
raise TypeError(
_format("The 'MaxObjectCount' parameter of the WBEMConnection "
"operation has invalid type {0} (must be integer or None)",
type(MaxObjectCount)))
if MaxObjectCount < 0:
raise ValueError(
_format("The 'MaxObjectCount' parameter of the WBEMConnection "
"operation has invalid value {0!r} (must be >= 0)",
MaxObjectCount))
def _validate_context(context):
"""
Validate the context input parameter for the Pull...() and
CloseEnumeration() operations.
Parameters:
context: Must be tuple/list type of length 2
Raises:
TypeError: Invalid type
ValueError: Invalid value, including None
"""
if context is None:
raise ValueError(
_format("The 'context' parameter of the WBEMConnection "
"operation has invalid value None "
"(enumeration may be exhausted)"))
if not isinstance(context, (tuple, list)):
raise TypeError(
_format("The 'context' parameter of the WBEMConnection "
"operation has invalid type {0} (must be tuple or list)",
type(context)))
if len(context) != 2:
raise ValueError(
_format("The 'context' parameter of the WBEMConnection "
"operation has invalid value {0!r} (must be tuple or list "
"of size 2)", context))
class IterQueryInstancesReturn(object):
"""
The return data for
:meth:`~pywbem.WBEMConnection.IterQueryInstances`.
"""
def __init__(self, instances, query_result_class=None):
"""Save any query_result_class and instances returned"""
self._query_result_class = query_result_class
self.instances = instances
@property
def query_result_class(self):
"""
:class:`~pywbem.CIMClass`: The query result class, if requested
via the `ReturnQueryResultClass` parameter of
:meth:`~pywbem.WBEMConnection.IterQueryInstances`.
`None`, if a query result class was not requested.
"""
return self._query_result_class
@property
def generator(self):
"""
:term:`py:generator` iterating :class:`~pywbem.CIMInstance`:
A generator object that iterates the CIM instances representing
the query result. These instances do not have an instance path
set.
"""
for inst in self.instances:
yield inst
class WBEMConnection(object): # pylint: disable=too-many-instance-attributes
"""
A client's connection to a WBEM server or WBEM listener. This is the main
class of the WBEM client library API.
This class is used for communication with WBEM servers and WBEM listeners,
because the communication mechanisms are very similar. However, a
particular object of this class will connect to only one target which is
either a WBEM server or a WBEM listener, but never both.
When targeting a WBEM server, the connection object knows a default CIM
namespace, which is used when no namespace is specified on subsequent
WBEM operations (that support specifying namespaces). Thus, the connection
object can be used as a connection to multiple CIM namespaces on a
WBEM server (when the namespace is specified on subsequent operations),
or as a connection to only the default namespace (this allows omitting the
namespace on subsequent operations).
As usual in HTTP, there is no persistent TCP connection; the connectedness
provided by this class is only conceptual. That is, the creation of the
connection object does not cause any interaction with the WBEM server or
WBEM listener, and each subsequent WBEM operation performs an independent,
state-less HTTP/HTTPS request. However, usage of the `requests` Python
package causes the underlying resources such as sockets to be pooled.
A :meth:`~pywbem.WBEMConnection.close` method closes the underlying
session of the `requests` package, releasing any sockets.
The WBEMConnection class can also be used as a context manager, which
causes the connection to be closed at context manager exit:
.. code-block:: python
with WBEMConnection('https://myserver') as conn:
conn.EnumerateInstances('CIM_Foo')
The :class:`~pywbem.WBEMConnection` class supports connection through
HTTP and SOCKS proxies, by utilizing the proxy support in the `requests`
Python package.
After creating a :class:`~pywbem.WBEMConnection` object, various methods
may be called on the object, which cause WBEM operations to be issued to
the WBEM server or WBEM listener. See :ref:`WBEM operations` for a list of
these methods. Each operation method describes whether it can be used
with a WBEM server or with a WBEM listener.
CIM elements such as instances or classes are represented as Python objects
(see :ref:`CIM objects`). The caller does not need to know about the CIM-XML
encoding of CIM elements and protocol payload that is used underneath (It
should be possible to use a different WBEM protocol below this layer without
disturbing any callers).
The connection remembers the XML of the last request and last reply if
debugging is turned on via the :attr:`debug` attribute of the
connection object.
This may be useful in debugging: If a problem occurs, you can examine the
:attr:`last_request` and :attr:`last_reply` attributes of the
connection object.
These are the prettified XML of request and response, respectively.
The real request and response that are sent and received are available in
the :attr:`last_raw_request` and :attr:`last_raw_reply` attributes
of the connection object.
The methods of this class may raise the following exceptions:
* Exceptions indicating operational errors:
- :exc:`~pywbem.ConnectionError` - A connection with the WBEM server
or WBEM listener could not be established or broke down.
- :exc:`~pywbem.AuthError` - Authentication failed with the WBEM server.
This exception cannot occur when targeting a WBEM listener.
- :exc:`~pywbem.TimeoutError` - The WBEM server or WBEM listener did not
respond in time and the client timed out.
Such exceptions can typically be resolved by the client user or server
admin.
* Exceptions indicating server-side issues:
- :exc:`~pywbem.HTTPError` - HTTP error (bad status code) received from
WBEM server or WBEM listener.
- :exc:`~pywbem.CIMXMLParseError` - The response from the WBEM server
or WBEM listener cannot be parsed because it is invalid CIM-XML
(for example, a required attribute is missing on an XML element).
- :exc:`~pywbem.XMLParseError` - The response from the WBEM server or
WBEM listener cannot be parsed because it is invalid XML (for example,
invalid characters or UTF-8 sequences, or ill-formed XML).
Such exceptions nearly always indicate an issue with the implementation
of the WBEM server or WBEM listener.
* Other exceptions:
- :exc:`~pywbem.CIMError` - The WBEM server or WBEM listener returned an
error response with a CIM status code.
Depending on the nature of the request, and on the CIM status code, the
reason may be a client user error (e.g. incorrect class name) or
a server side issue (e.g. some internal error in the server or listener).
* Exceptions indicating programming errors (in pywbem or by the user):
- :exc:`py:TypeError`
- :exc:`py:KeyError`
- :exc:`py:ValueError`
- :exc:`py:AttributeError`
- ... possibly others ...
Exceptions indicating programming errors should not happen. If you think
the reason such an exception is raised lies in pywbem,
`report a bug <https://github.com/pywbem/pywbem/issues>`_.
"""
# Class level counter. Incremented at each WBEMConnection creation
# and used as part of WBEMConnection instance ID.
_conn_counter = 0
# Detail levels to be used for log operation recorders of any newly created
# WBEMConnection objects.
# Key: Simple logger name (e.g. 'api')
# Value: Detail level string from LOG_DETAIL_LEVELS.
_log_detail_levels = {}
# If True, logging will be activated for any newly created WBEMConnection
# objects.
_activate_logging = False
def __init__(self, url, creds=None, default_namespace=None,
x509=None, ca_certs=None,
no_verification=False, timeout=DEFAULT_TIMEOUT,
use_pull_operations=False,
stats_enabled=False, proxies=None):
# pylint: disable=line-too-long
"""
Parameters:
url (:term:`string`):
URL of the WBEM server or WBEM listener, in the format:
``[{scheme}://]{host}[:{port}]``
Possibly present trailing path segments in the URL are ignored.
The following URL schemes are supported:
* ``http``: Causes HTTP to be used (default).
* ``https``: Causes HTTPS to be used.
The host can be specified in any of the usual formats:
* a short or fully qualified DNS hostname
* a literal (= dotted) IPv4 address
* a literal IPv6 address, formatted as defined in :term:`RFC3986`
with the extensions for zone identifiers as defined in
:term:`RFC6874`, supporting ``-`` (minus) for the delimiter
before the zone ID string, as an additional choice to ``%25``.
If no port is specified in the URL, it defaults to:
* Port 5988 for URL scheme ``http``
* Port 5989 for URL scheme ``https``
For WBEM listeners, the port usually different and should therefore
be specified in the URL.
Examples for some URL formats:
* ``"https://10.11.12.13:6989"``:
Use HTTPS to port 6989 on host 10.11.12.13
* ``"https://mysystem.acme.org"``:
Use HTTPS to port 5989 on host mysystem.acme.org
* ``"10.11.12.13"``:
Use HTTP to port 5988 on host 10.11.12.13
* ``"http://[2001:db8::1234]:15988"``:
Use HTTP to port 15988 on host 2001:db8::1234
* ``"http://[::ffff.10.11.12.13]"``:
Use HTTP to port 5988 on host ::ffff.10.11.12.13 (an
IPv4-mapped IPv6 address)
* ``"http://[2001:db8::1234%25eth0]"`` or
``"http://[2001:db8::1234-eth0]"``:
Use HTTP to port 5988 to host 2001:db8::1234 (a link-local IPv6
address) using zone identifier eth0
creds (:class:`py:tuple` of userid, password):
Credentials for HTTP authentication with the WBEM server, as a
tuple(userid, password), with:
* userid (:term:`string`):
Userid for authenticating with the WBEM server.
* password (:term:`string`):
Password for that userid.
If `None`, the client will not generate ``Authorization`` headers
in the HTTP request. Otherwise, the client will generate an
``Authorization`` header using HTTP Basic Authentication.
See :ref:`Authentication types` for an overview.
This parameter will be ignored when targeting a WBEM listener.
default_namespace (:term:`string`):
Default CIM namespace for this connection.
Leading and trailing slash characters will be stripped. The lexical
case will be preserved.
If `None`, the default namespace of the connection will be set to
the built-in default namespace |DEFAULT_NAMESPACE|.
The default namespace of the connection is used if no namespace
or a namespace of `None` is specified for an operation.
This parameter will be ignored when targeting a WBEM listener.
x509 (:class:`py:dict`):
:term:`X.509` client certificate and key file to be presented
to the WBEM server or WBEM listener during the TLS/SSL handshake.
This parameter is ignored when HTTP is used.
If `None`, no client certificate is presented to the server or
listener, resulting in TLS/SSL 1-way authentication to be used.
Otherwise, the client certificate is presented to the server or
listener, resulting in TLS/SSL 2-way authentication to be used.
This parameter must be a dictionary containing the following
two items:
* ``"cert_file"`` (:term:`string`):
The file path of a file containing an :term:`X.509` client
certificate. Required. If the file does not exist,
:exc:`py:IOError` will be raised.
* ``"key_file"`` (:term:`string`):
The file path of a file containing the private key belonging to
the public key that is part of the :term:`X.509` certificate
file. Optional; if omitted or `None`, the private key must
be in the file specified with ``"cert_file"``. If specified
but the file does not exist, :exc:`py:IOError` will
be raised.
The dictionary is copied.
See :ref:`Authentication types` for an overview.
ca_certs (:term:`string`):
Selects the CA certificates (trusted certificates) for
verifying the X.509 server certificate returned by the WBEM server
or WBEM listener.
This parameter is ignored when HTTP is used or when the
`no_verification` parameter is set to disable verification.
The parameter value must be one of:
* :term:`string`: A path to a file containing one or more CA
certificates in PEM format. See the description of `CAfile` in
the OpenSSL `SSL_CTX_load_verify_locations`_ function for
details. If the file does not exist,
:exc:`py:IOError` will be raised.
* :term:`string`: A path to a directory with files each of which
contains one CA certificate in PEM format. See the description
of `CApath` in the OpenSSL `SSL_CTX_load_verify_locations`_
function for details. If the directory does not exist,
:exc:`py:IOError` will be raised.
* `None` (default): Use the certificates provided by the
`certifi Python package`_. This package provides the certificates
from the `Mozilla Included CA Certificate List`_.
.. _`certifi Python package`: https://certifi.io/en/latest/
.. _`Mozilla Included CA Certificate List`: https://wiki.mozilla.org/CA/Included_Certificates
.. _`SSL_CTX_load_verify_locations`: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_load_verify_locations.html
no_verification (:class:`py:bool`):
Disables verification of the X.509 server certificate returned by
the WBEM server or WBEM listener during TLS/SSL handshake, and
disables verification of the hostname.
If `True`, verification is disabled; otherwise, verification is
enabled.
This parameter is ignored when HTTP is used.
Disabling the verification of the server certificate is insecure
and should be avoided!
timeout (:term:`number`):
Timeout in seconds, for completing a CIM operation to a server or a
CIM indication delivery to a listener.
*New in pywbem 0.8.*
If the CIM operation or CIM indication delivery could not be
completed within the specified timeout, :exc:`~pywbem.TimeoutError`
is raised.
A value of `None` means that the connection uses the standard
timeout behavior of Python sockets, which can be between several
minutes and forever. Because this is somewhat unpredictable,
it is recommended to specify a value for the timeout.
A value of ``0`` means the timeout is very short, and does not
really make any sense.
Note that not all situations can be handled within this timeout, so
for some issues, operations may take longer before raising an
exception.
use_pull_operations (:class:`py:bool`):
Controls the use of pull operations in any `Iter...()` methods.
*New in pywbem 0.11 as experimental and finalized in 0.13.*
`None` means that the `Iter...()` methods will attempt a pull
operation first, and if the WBEM server does not support it, will
use a traditional operation from then on, on this connection.
This detection is performed for each pull operation separately, in
order to accomodate WBEM servers that support only some of the pull
operations. This will work on any WBEM server whether it supports
no, some, or all pull operations. Note that as per DSP0200, WBEM
servers need to return status code `CIM_ERR_NOT_SUPPORTED` if a
pull operation is not supported. Some WBEM servers return status
code `CIM_ERR_FAILED` in this case, which is treated by pywbem to
also mean that the pull operation is not supported.
`True` means that the `Iter...()` methods will only use pull
operations. If the WBEM server does not support pull operations, a
:exc:`~pywbem.CIMError` with status code `CIM_ERR_NOT_SUPPORTED`
(or `CIM_ERR_FAILED` for some WBEM servers) will be raised.
`False` (default) means that the `Iter...()` methods will only use
traditional operations.
This parameter will be ignored when targeting a WBEM listener.
stats_enabled (:class:`py:bool`):
Initial enablement status for maintaining statistics about the
WBEM operations executed via this connection.
*New in pywbem 0.11 as experimental, renamed from `enable_stats`
and finalized in 0.12.*
See the :ref:`WBEM operation statistics` section for details.
This parameter will be ignored when targeting a WBEM listener.
proxies (:class:`py:dict`):
Dictionary with the URLs of HTTP or SOCKS proxies to use for
the connection to the WBEM server or WBEM listener.
*New in pywbem 1.0*
`None` (the default) causes the use of direct connections without
using a proxy.
An input dictionary is copied.
This parameter is passed on to the `proxies` parameter of the
requests package. See the :ref:`Proxy support` section for details.
""" # noqa: E501
# pylint: enable=line-too-long
# Connection attributes
scheme, hostport, url = parse_url(url)
self._scheme = scheme
self._host = hostport
self._url = url
self._creds = creds # tuple is immutable, so no copy
if x509 is not None:
if not isinstance(x509, dict):
raise TypeError(
"The x509 parameter must be a dictionary but has type: {0}".
format(type(x509)))
try:
cert_file = x509['cert_file']
except KeyError:
raise ValueError(
"The x509 parameter does not have the required key "
"'cert_file': {0!r}".format(x509))
if not isinstance(cert_file, six.string_types):
raise TypeError(
"The 'cert_file' item in the x509 parameter must be a "
"string but has type: {0}".
format(type(cert_file)))
key_file = x509.get('key_file', None)
if key_file is not None and \
not isinstance(key_file, six.string_types):
raise TypeError(
"The 'key_file' item in the x509 parameter must be a "
"string but has type: {0}".
format(type(key_file)))
# x509 dict is mutable, so we copy it
self._x509 = None if x509 is None else dict(x509)
self._ca_certs = ca_certs
self._no_verification = no_verification
self._timeout = timeout
if proxies is not None:
if not isinstance(proxies, dict):
raise TypeError(
"The proxies parameter must be a dictionary but has "
"type: {0}".
format(type(proxies)))
self._proxies = proxies.copy()
else:
self._proxies = None
self._set_default_namespace(default_namespace)
# Requests session
self.session = requests.Session()
self.session.proxies = self.proxies
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
if self.x509 is not None:
cert_file = self.x509['cert_file']
key_file = self.x509.get('key_file', None)
if not os.path.exists(cert_file):
raise IOError(
"Client certificate file for TLS/SSL 2-way "
"authentication not found: {}".
format(cert_file))
if key_file is not None and not os.path.exists(key_file):
raise IOError(
"Client key file for TLS/SSL 2-way "
"authentication not found: {}".
format(key_file))
cert = (cert_file, key_file)
else:
cert = None
self.session.cert = cert
# In addition to the possibilities below, the REQUESTS_CA_BUNDLE
# and CURL_CA_BUNDLE environment variables can be set to override the
# choice. The value is in both cases the path to a certificate file or
# certificate directory.
if self.no_verification:
verify = False
elif self.ca_certs is None:
# Use the certificates provided by the Python certifi package.
verify = True
elif isinstance(self.ca_certs, six.string_types):
# Use the specified path name (to file or directory).
if not os.path.exists(self.ca_certs):
raise IOError(
"CA certificate file or directory not found: {}".
format(self.ca_certs))
verify = self.ca_certs
else:
raise TypeError(
_format("The ca_certs parameter has invalid type: {0}",
type(self.ca_certs)))
self.session.verify = verify
retry = urllib3.Retry(**RETRY_KWARGS)
# While it would be technically sufficient to set a retry transport
# adapter only for the scheme specified in the input URL, we are
# setting it for both schemes that have existing adapters, in order to
# avoid confusion for the human reader.
retry_adapter = requests.adapters.HTTPAdapter(max_retries=retry)
self.session.mount('http://', retry_adapter)
self.session.mount('https://', retry_adapter)
# Saving last request and reply
self._debug = False
self._last_raw_request = None
self._last_raw_reply = None
self._last_request = None
self._last_request_xml_item = None
self._last_reply = None
self._last_reply_xml_item = None
# Time statistics
self._last_request_len = 0
self._last_reply_len = 0
# control of operation recorders
self._operation_recorders = []
# Create the connection identifier for this WBEMConnection
# Includes class level counter and process pid
self.__class__._conn_counter += 1
self._conn_id = '{0}-{1}'.format(
self.__class__._conn_counter, # pylint: disable=protected-access
os.getpid())
# Intent to use pull operations
self._use_pull_operations = use_pull_operations
# Actual status of using pull operations
self._use_enum_inst_pull_operations = use_pull_operations
self._use_enum_path_pull_operations = use_pull_operations
self._use_ref_inst_pull_operations = use_pull_operations
self._use_ref_path_pull_operations = use_pull_operations
self._use_assoc_inst_pull_operations = use_pull_operations
self._use_assoc_path_pull_operations = use_pull_operations
self._use_query_pull_operations = use_pull_operations
self._statistics = Statistics(stats_enabled)
self._last_operation_time = None
self._last_server_response_time = None
if self._activate_logging:
recorder = LogOperationRecorder(
conn_id=self.conn_id,
detail_levels=self._log_detail_levels)
self.add_operation_recorder(recorder)
def __enter__(self):
"""
*New in pywbem 1.2.*
Enter method when the class is used as a context manager.
Returns the connection object.
"""
return self
def __exit__(self, exc_type, exc_value, traceback):
"""
*New in pywbem 1.2.*
Exit method when the class is used as a context manager.
It closes the connection by calling
:meth:`~pywbem.WBEMConnection.close`.
"""
self.close()
return False # re-raise any exceptions
def copy(self):
"""
*New in pywbem 1.3.*
Return a deep copy of the object with internal state reset.
The user-specifiable attributes of the object are deep-copied, and all
other internal state (e.g. session, statistics, debug data) is reset.
Any operation recorders on the original object are also deep-copied
while resetting their internal state (e.g. staged operations).
"""
cpy = WBEMConnection(
url=self.url,
creds=self.creds,
default_namespace=self.default_namespace,
x509=self.x509,
ca_certs=self.ca_certs,
no_verification=self.no_verification,
timeout=self.timeout,
use_pull_operations=self.use_pull_operations,
stats_enabled=self.stats_enabled,
proxies=self.proxies,
) # init makes copies of mutable parameters
for rec in self.operation_recorders:
cpy.add_operation_recorder(rec.copy())
return cpy
@property
def url(self):
"""
:term:`unicode string`: Normalized URL of the WBEM server or
WBEM listener.
The scheme is in lower case and the default scheme (http) has been
applied. Default port numbers (5988 for http and 5989 for https) have
been applied. For IPv6 addresses, the host has been normalized to
RFC6874 URI syntax. Trailing path segments have been removed.
For examples, see the description of the same-named init
parameter of :class:`this class <pywbem.WBEMConnection>`.
*Changed in pywbem 1.0: The URL is now normalized.*
"""
return self._url