/
_cim_operations.py
9810 lines (7937 loc) · 418 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.
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 method 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.
""" # 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 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
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 ._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']
# Parameters for HTTP retry
HTTP_TOTAL_RETRIES = 2 # Max number of total HTTP retries
HTTP_CONNECT_RETRIES = 2 # Max number of HTTP connect retries
HTTP_READ_RETRIES = 2 # Max number of HTTP read retries
HTTP_RETRY_BACKOFF_FACTOR = 0.1 # Backoff factor for retries
HTTP_MAX_REDIRECTS = 5 # Max number of HTTP redirects
# 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 _check_classname(val):
"""
Validate a classname.
At this point, only the type is validated to be a string.
"""
if not isinstance(val, six.string_types):
raise ValueError(
_format("string expected for classname, not {0!A}", val))
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 WBEMConnection(object): # pylint: disable=too-many-instance-attributes
"""
A client's connection to a WBEM server. This is the main class of the
WBEM client library API.
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, and
each subsequent WBEM operation performs an independent, state-less
HTTP/HTTPS request. Usage of the `requests` Python package causes the
underlying resources such as sockets to be pooled, though.
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. See :ref:`WBEM operations` for a list of these methods.
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.
WBEMConnection objects can record the operations performed by calling
:class:`~pywbem.WBEMConnection` methods that interact with a WBEM server
using *operation recorders*, at the level of method calls and returns, and
at the level of CIM-XML requests and responses.
The :class:`~pywbem.LogOperationRecorder` class records the operations in
the Python logging facility. This recorder is activated through the
`connection` parameter of :func:`~pywbem._logging.configure_logger`.
The :class:`~pywbem.TestClientRecorder` class records the operations in a
file in the YAML format suitable for the test_client.py unit test program.
The :meth:`~pywbem.WBEMConnection.add_operation_recorder` method is used
to add an operation recorder to a connection, and the
:attr:`~pywbem.WBEMConnection.operation_recorders` property is used to
retrieve the current operation recorders of a connection.
The methods of this class may raise the following exceptions:
* Exceptions indicating operational errors:
- :exc:`~pywbem.ConnectionError` - A connection with the WBEM server
could not be established or broke down.
- :exc:`~pywbem.AuthError` - Authentication failed with the WBEM server.
- :exc:`~pywbem.TimeoutError` - The WBEM server 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.
- :exc:`~pywbem.CIMXMLParseError` - The response from the WBEM server
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
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.
* Other exceptions:
- :exc:`~pywbem.CIMError` - The WBEM server 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).
* Exceptions indicating programming errors (in pywbem or by the user):
- :exc:`~py:exceptions.TypeError`
- :exc:`~py:exceptions.KeyError`
- :exc:`~py:exceptions.ValueError`
- :exc:`~py:exceptions.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=None, use_pull_operations=False,
stats_enabled=False, proxies=None):
# pylint: disable=line-too-long
"""
Parameters:
url (:term:`string`):
URL of the WBEM server, 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``
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 (:func:`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.
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.
x509 (:class:`py:dict`):
:term:`X.509` client certificate and key file to be presented
to the WBEM server during the TLS/SSL handshake.
This parameter is ignored when HTTP is used.
If `None`, no client certificate is presented to the server,
resulting in TLS/SSL 1-way authentication to be used.
Otherwise, the client certificate is presented to the server,
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:exceptions.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:exceptions.IOError` will
be raised.
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.
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:exceptions.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:exceptions.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 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 requests sent to the server.
*New in pywbem 0.8.*
If the server did not respond within the timeout duration, the
socket for the connection will be closed, causing a
:exc:`~pywbem.TimeoutError` to be raised.
A value of `None` means that the connection uses the standard
timeout behavior of Python sockets, which can be between several
minutes and much longer. 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.
`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`
will be raised.
`False` (default) means that the `Iter...()` methods will only use
traditional operations.
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.
proxies (:class:`py:dict`):
Dictionary with the URLs of HTTP or SOCKS proxies to use for
the connection to the WBEM server.
*New in pywbem 1.0*
`None` (the default) causes the use of direct connections without
using a proxy.
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
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)))
self._x509 = 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(
total=HTTP_TOTAL_RETRIES,
connect=HTTP_CONNECT_RETRIES,
read=HTTP_READ_RETRIES,
method_whitelist={'POST'},
redirect=HTTP_MAX_REDIRECTS,
backoff_factor=HTTP_RETRY_BACKOFF_FACTOR)
# 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)
@property
def url(self):
"""
:term:`unicode string`: Normalized URL of the WBEM server.
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
@property
def scheme(self):
"""
:term:`unicode string`: Normalized scheme of the URL of the WBEM
server, for example 'http' or 'https'.
The scheme is in lower case and the default scheme (http) has been
applied.
*New in pywbem 1.0.*
"""
return self._scheme
@property
def host(self):
"""
:term:`unicode string`: Normalized host and port number of the WBEM
server, in the format ``host:port``.
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.
This value is used as the host portion of CIM namespace paths in
any objects returned from the WBEM server that did not specify a
CIM namespace path.
*New in pywbem 0.11.*
*Changed in pywbem 1.0: The host is now normalized and the port is
always present.*
"""
return self._host
@property
def creds(self):
"""
:func:`py:tuple`: Credentials for HTTP authentication with the WBEM
server.
For details, see the description of the same-named init
parameter of :class:`this class <pywbem.WBEMConnection>`.
"""
return self._creds
@property
def default_namespace(self):
"""
:term:`unicode string`: Name of the CIM namespace to be used by default
(if no namespace is specified for an operation).
For details, see the description of the same-named init
parameter of :class:`this class <pywbem.WBEMConnection>`.
This attribute is settable.
"""
return self._default_namespace
@default_namespace.setter
def default_namespace(self, default_namespace):
"""Setter method; for a description see the getter method."""
# pylint: disable=attribute-defined-outside-init
self._set_default_namespace(default_namespace)
def _set_default_namespace(self, default_namespace):
"""Internal setter function."""
if default_namespace is not None:
default_namespace = default_namespace.strip('/')
else:
default_namespace = DEFAULT_NAMESPACE
self._default_namespace = _ensure_unicode(default_namespace)
@property
def x509(self):
"""
:class:`py:dict`: :term:`X.509` client certificate and key file to be
presented to the WBEM server during the TLS/SSL handshake.
For details, see the description of the same-named init
parameter of :class:`this class <pywbem.WBEMConnection>`.
"""
return self._x509
@property
def ca_certs(self):
"""
:term:`string`: Selects the CA certificates (trusted certificates) for
verifying the X.509 server certificate returned by the WBEM server.
For details, see the description of the same-named init
parameter of :class:`this class <pywbem.WBEMConnection>`.
"""
return self._ca_certs
@property
def no_verification(self):
"""
:class:`py:bool`: Boolean indicating that verifications are disabled
for this connection.
For details, see the description of the same-named init
parameter of :class:`this class <pywbem.WBEMConnection>`.
"""
return self._no_verification
@property
def timeout(self):
"""
:term:`number`: Timeout in seconds, for requests sent to the server.
*New in pywbem 0.8.*
For details, see the description of the same-named init
parameter of :class:`this class <pywbem.WBEMConnection>`.
"""
return self._timeout
@property
def operation_recorders(self):
"""
Tuple of :class:`BaseOperationRecorder` subclass objects:
**Experimental:** The operation recorders of this connection.
*New in pywbem 0.12 as experimental.*
"""
return tuple(self._operation_recorders)
@property
def operation_recorder_enabled(self):
"""
:class:`py:bool`: **Experimental:** Enablement status for all operation
recorders of the connection.
*New in pywbem 0.11 as experimental*
This is a writeable property; setting this property will change the
operation recorder enablement status accordingly for all operation
recorders of the connection.
Reading this property returns `True` if one or more operation recorders