/
_wbemconnection_mock.py
1757 lines (1447 loc) · 71.7 KB
/
_wbemconnection_mock.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 2018 InovaDevelopment.com
#
# 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: Karl Schopmeyer <inovadevelopment.com>
#
"""
Mock support for the WBEMConnection class to allow pywbem users to test the
pywbem client without requiring a running WBEM server.
For documentation, see mocksupport.rst.
"""
from __future__ import absolute_import, print_function
import os
import time
import re
from xml.dom import minidom
from mock import Mock
import six
from pywbem import WBEMConnection, CIMClass, CIMClassName, \
CIMInstance, CIMInstanceName, CIMParameter, CIMQualifierDeclaration, \
cimtype, CIMError, CIM_ERR_FAILED, DEFAULT_NAMESPACE, MOFCompiler
from pywbem._nocasedict import NocaseDict
from pywbem._utils import _format
from ._mainprovider import MainProvider
from ._inmemoryrepository import InMemoryRepository
from ._mockmofwbemconnection import _MockMOFWBEMConnection
from ._providerregistry import ProviderRegistry
from ._providerdispatcher import ProviderDispatcher
from ._dmtf_cim_schema import build_schema_mof
from ._utils import _uprint
from ._namespaceprovider import CIMNamespaceProvider
__all__ = ['FakedWBEMConnection']
# Fake Server default values for parameters that apply to repo and operations
# allowed output formats for the repository display
OUTPUT_FORMATS = ['mof', 'xml', 'repr']
# Issue #2065. We have not considered that iq and ico are deprecated in
# on DSP0200 for get_instance, etc. We could set up a default to ignore these
# parameters for the operations in which they are deprecated and we
# should/could ignore them. We need to document our behavior in relation to the
# spec.
def _pretty_xml(xml_string):
"""
Common function to produce pretty xml string from an input xml_string.
This function is NOT intended to be used in major code paths since it
uses the minidom to produce the prettified xml and that uses a lot
of memory
"""
result_dom = minidom.parseString(xml_string)
pretty_result = result_dom.toprettyxml(indent=' ')
# remove extra empty lines
return re.sub(r'>( *[\r\n]+)+( *)<', r'>\n\2<', pretty_result)
def _cvt_rqd_classname(classname):
"""Convert required classname to string"""
if isinstance(classname, CIMClassName):
classname = classname.classname
return classname
def _cvt_opt_classname(classname):
"""Convert optional classname to string if it exists"""
if classname is None:
return classname
if isinstance(classname, CIMClassName):
return classname.classname
return classname
def _cvt_obj_name(objname):
"""Convert objectname to string if classname or return if inst name"""
if isinstance(objname, CIMInstanceName):
return objname
if isinstance(objname, CIMClassName):
return objname.classname
return objname
class FakedWBEMConnection(WBEMConnection):
"""
A subclass of :class:`pywbem.WBEMConnection` that mocks the communication
with a WBEM server by utilizing a local in-memory CIM repository to
generate responses in the same way the WBEM server would.
**Experimental:** *New in pywbem 0.12 as experimental.*
Each :class:`~pywbem.FakedWBEMConnection` object has its own
CIM repository which contains multiple CIM namespaces, and each namespace
may contain CIM qualifier types (declarations), CIM classes and
CIM instances.
This class provides only a subset of the init parameters of
:class:`~pywbem.WBEMConnection` because it does not have a connection to a
WBEM server. It uses a faked and fixed URL for the WBEM server
(``http://FakedUrl``) as a means of identifying the connection by users.
Logging of the faked operations is supported via the pywbem logging
facility and can be controlled in the same way as for
:class:`~pywbem.WBEMConnection`. For details, see
:ref:`WBEM operation logging`.
"""
def __init__(self, default_namespace=DEFAULT_NAMESPACE,
use_pull_operations=False, stats_enabled=False,
timeout=None, response_delay=None,
disable_pull_operations=None):
"""
Parameters:
default_namespace (:term:`string`):
Default namespace.
This parameter has the same characteristics as the same-named init
parameter of :class:`~pywbem.WBEMConnection`.
use_pull_operations (:class:`py:bool`):
Flag to control whether pull or traditional operations are
used in the iter... operations.
This parameter has the same characteristics as the same-named init
parameter of :class:`~pywbem.WBEMConnection`.
timeout (:term:`number`):
This parameter has the same characteristics as the same-named init
parameter of :class:`~pywbem.WBEMConnection`.
stats_enabled (:class:`py:bool`):
Flag to enable operation statistics.
This parameter has the same characteristics as the same-named init
parameter of :class:`~pywbem.WBEMConnection`.
response_delay (:term:`number`):
Artifically created delay for each operation, in seconds. This must
be a positive number. Delays less than a second or other fractional
delays may be achieved with float numbers.
`None` disables the delay.
Note that the
:attr:`~pywbem_mock.FakedWBEMConnection.response_delay` property
can be used to set this delay subsequent to object creation.
disable_pull_operations (:class:`py:bool`):
Flag to allow user to disable the pull operations ( Open... and
Pull.. requests). The default is None which enables pull operations
to execute. Setting the flag to True causes pull operations
to raise CIMError(CIM_ERR_NOT_SUPPORTED).
The :attr:`~pywbem_mock.FakedWBEMConnection.disable_pull_operations`
property can be used to set this variable.
"""
# Response delay in seconds. Any operation is delayed by this time.
# Initialize before superclass init because otherwise logger may
# fail with this attribute not found
self._response_delay = response_delay
# define attribute here to assure it is defined before CIM repository
# created. Reset again after repository created.
self._disable_pull_operations = disable_pull_operations
super(FakedWBEMConnection, self).__init__(
'http://FakedUrl:5988',
default_namespace=default_namespace,
use_pull_operations=use_pull_operations,
stats_enabled=stats_enabled, timeout=timeout)
# See the cimrepository property for more information
self._cimrepository = None
# Provider registry defines user added providers. This is a dictionary
# with key equal classname that contains entries for namespaces,
# provider type, and provider for each class name defined
self._provider_registry = ProviderRegistry()
# Define the datastore to be used with an initial namespace, the client
# connection default namespace. This is passed to the mainprovider
# and not used further in this class.
# Initiate the MainProvider with parameters required to execute
self._mainprovider = MainProvider(self.host,
self.disable_pull_operations,
self.cimrepository)
# Initiate instance of the ProviderDispatcher with required
# parameters including the CIM repository
self._providerdispatcher = ProviderDispatcher(
self._cimrepository, self._provider_registry)
# Flag to allow or disallow the use of the Open... and Pull...
# operations. Uses the setter method
self.disable_pull_operations = disable_pull_operations
# Defines the connection for the compiler. The compiler uses this
# instance of this class as the client interface.
self._mofwbemconnection = _MockMOFWBEMConnection(self)
self._imethodcall = Mock(side_effect=self._mock_imethodcall)
self._methodcall = Mock(side_effect=self._mock_methodcall)
@property
def namespaces(self):
"""
:term:`NocaseList` of :term:`string`:
The names of the namespaces that exist in the CIM repository.
"""
return self._mainprovider.namespaces
@property
def interop_namespace_names(self):
"""
:term:`NocaseList` of :term:`string`:
The valid Interop namespace names.
Only these names may be the Interop namespace and only one
Interop namespace may exist in a WBEM server environment.
This list is defined in :attr:`pywbem.WBEMServer.INTEROP_NAMESPACES`.
"""
return self._mainprovider.interop_namespace_names
@property
def cimrepository(self):
"""
:class:`~pywbem_mock.InMemoryRepository`: The mocked in-memory CIM
repository.
The CIM repository is the data store for CIM classes, CIM instances,
and CIM qualifier declarations, all partitioned by CIM namespaces.
"""
if self._cimrepository is None:
self._cimrepository = InMemoryRepository(self.default_namespace)
return self._cimrepository
@property
def response_delay(self):
"""
:term:`number`:
Artifically created delay for each operation, in seconds.
If `None`, there is no delay.
This attribute is settable. For details, see the description of the
same-named init parameter of
:class:`this class <pywbem.FakedWBEMConnection>`.
"""
return self._response_delay
@response_delay.setter
def response_delay(self, delay):
"""Setter method; for a description see the getter method."""
if isinstance(delay, (int, float)) and delay >= 0 or delay is None:
self._response_delay = delay
else:
raise ValueError(
_format("Invalid value for response_delay: {0!A}, must be a "
"positive number", delay))
@property
def disable_pull_operations(self):
"""
:class:`py:bool`:
Boolean flag to set option to disable the execution of the open and
pull operation request handlers in the CIM repository. This
emulates the characteristic in some CIM servers that did not
implement pull operations. The default is to allow pull operations.
All pull operations requests may be forbidden from executing by
setting disable_pull_operations to True.
This attribute is settable. For details, see the description of the
same-named init parameter of
:class:`this class <pywbem.FakedWBEMConnection>`.
"""
return self._disable_pull_operations
@disable_pull_operations.setter
def disable_pull_operations(self, disable):
"""Setter method; for a description see the getter method."""
# Attribute will always be boolean
if disable is None:
disable = False
if isinstance(disable, bool):
# pylint: disable=attribute-defined-outside-init
self._disable_pull_operations = disable
# modify the parameter in the mainprovider
self._mainprovider.disable_pull_operations = disable
else:
raise ValueError(
_format('Invalid type for disable_pull_operations: {0!A}, '
'must be a boolean', disable))
def __str__(self):
return _format(
"FakedWBEMConnection("
"response_delay={s.response_delay}, "
"super={super})",
s=self, super=super(FakedWBEMConnection, self).__str__())
def __repr__(self):
return _format(
"FakedWBEMConnection("
"response_delay={s.response_delay}, "
"disable_pull_operations={s.disable_pull_operations} "
"super={super})",
s=self, super=super(FakedWBEMConnection, self).__repr__())
# The namespace management methods must be in the this class directly
# so they can be access with call to the methods from instance of
# this class. they are considered part of the external API.
def add_namespace(self, namespace):
"""
Add a CIM namespace to the CIM repository of the faked connection.
The namespace must not yet exist in the CIM repository.
Parameters:
namespace (:term:`string`):
The name of the CIM namespace to be added to the CIM repository.
Must not be `None`. Any leading or trailing slash characters are
removed before the string is used to define the namespace name.
Raises:
ValueError: Namespace argument must not be None.
:exc:`~pywbem.CIMError`: CIM_ERR_ALREADY_EXISTS if the namespace
already exists in the CIM repository.
"""
self._mainprovider.add_namespace(namespace)
def remove_namespace(self, namespace):
"""
Remove a CIM namespace from the CIM repository of the faked connection.
The namespace must exist in the CIM repository and must be empty.
Parameters:
namespace (:term:`string`):
The name of the CIM namespace in the CIM repository (case
insensitive). Must not be `None`. Leading or trailing
slash characters are ignored.
Raises:
ValueError: Namespace argument must not be None
:exc:`~pywbem.CIMError`: (CIM_ERR_NOT_FOUND) if the namespace does
not exist in the CIM repository.
:exc:`~pywbem.CIMError`: (CIM_ERR_NAMESPACE_NOT_EMPTY) if the
namespace is not empty.
:exc:`~pywbem.CIMError`: (CIM_ERR_NAMESPACE_NOT_EMPTY) if attempting
to delete the default connection namespace. This namespace cannot
be deleted from the CIM repository
"""
self._mainprovider.remove_namespace(namespace)
def is_interop_namespace(self, namespace):
"""
Tests if a namespace name is a valid Interop namespace name.
This method does not access the CIM repository for this test; it
merely compares the specified namespace name against the list of valid
Interop namespace names returned by :meth:`interop_namespace_names`.
Parameters:
namespace (:term:`string`):
The namespace name that is to be tested.
Returns:
:class:`py:bool`: Indicates whether the namespace name is a valid
Interop namespace name.
"""
return self._mainprovider.is_interop_namespace(namespace)
def find_interop_namespace(self):
"""
Find the Interop namespace in the CIM repository, or return `None`.
The Interop namespace is identified by comparing all namespace names
in the CIM repository against the list of valid Interop namespace names
returned by :meth:`interop_namespace_names`.
Returns:
:term:`string`: The name of the Interop namespace if one exists in
the CIM repository or otherwise `None`.
"""
return self._mainprovider.find_interop_namespace()
def install_namespace_provider(self, interop_namespace,
schema_pragma_file=None,
verbose=None):
"""
FakedWBEMConnection user method to install the namespace provider in
the Interop namespace where the proposed interop_namespace is defined
by the parameter interop_namespace
Because this provider requires a set of classes from the
DMTF schema, the schema_pragma_file install the schema is required.
This method should only be called once at the creation of the
mock environment.
Parameters:
interop_namespace (:term:`string`):
The Interop namespace defined for this environment
schema_pragma_file (:term:`string`):
File path defining a CIM schema pragma file for the set of
CIM classes that make up a schema such as the DMTF schema.
This file must contain a pragma statement for each of the
classes defined in the schema.
If None, no attempt is made to any CIM classes required for the
provider and it is assumed that the CIM classes are already
installed
verbose (:class:`py:bool`):
If True, displays progress information as providers are installed.
Raises:
:exc:`~pywbem.CIMError`: with status code appropriate for any
error encountered in the installation of the provider.
"""
# Determine if an interop namespace already exists and confirm that
# we are using a valid interop namespace name to add the
# new namespace.
if not self.find_interop_namespace():
self.add_namespace(interop_namespace)
provider = CIMNamespaceProvider(self.cimrepository)
self.register_provider(provider,
namespaces=interop_namespace,
schema_pragma_files=schema_pragma_file,
verbose=verbose)
###########################################################################
#
# Methods to compile mof files into repository
#
###########################################################################
def compile_mof_file(self, mof_file, namespace=None, search_paths=None,
verbose=None):
"""
Compile the MOF definitions in the specified file (and its included
files) and add the resulting CIM objects to the specified CIM namespace
of the CIM repository.
If the namespace does not exist, :exc:`~pywbem.CIMError` with status
CIM_ERR_INVALID_NAMESPACE is raised.
This method supports all MOF pragmas, and specifically the include
pragma.
If a CIM class or CIM qualifier type to be added already exists in the
target namespace with the same name (comparing case insensitively),
this method raises :exc:`~pywbem.CIMError`.
If a CIM instance to be added already exists in the target namespace
with the same keybinding values, this method raises
:exc:`~pywbem.CIMError`.
In all cases where this method raises an exception, the CIM repository
remains unchanged.
Parameters:
mof_file (:term:`string`):
Path name of the file containing the MOF definitions to be compiled.
namespace (:term:`string`):
The name of the target CIM namespace in the CIM repository. This
namespace is also used for lookup of any existing or dependent
CIM objects. If `None`, the default namespace of the connection is
used.
search_paths (:term:`py:iterable` of :term:`string`):
An iterable of directory path names where MOF dependent files will
be looked up.
See the description of the `search_path` init parameter of the
:class:`~pywbem.MOFCompiler` class for more information on MOF
dependent files.
verbose (:class:`py:bool`):
Controls whether to issue more detailed compiler messages.
Raises:
IOError: MOF file not found.
:exc:`~pywbem.MOFCompileError`: Compile error in the MOF.
"""
namespace = namespace or self.default_namespace
self._mainprovider.validate_namespace(namespace)
# issue #2063 refactor this so there is cleaner interface to
# WBEMConnection
mofcomp = MOFCompiler(self._mofwbemconnection,
search_paths=search_paths,
verbose=verbose, log_func=None)
mofcomp.compile_file(mof_file, namespace)
def compile_mof_string(self, mof_str, namespace=None, search_paths=None,
verbose=None):
"""
Compile the MOF definitions in the specified string and add the
resulting CIM objects to the specified CIM namespace of the
CIM repository.
If the namespace does not exist, :exc:`~pywbem.CIMError` with status
CIM_ERR_INVALID_NAMESPACE is raised.
This method supports all MOF pragmas, and specifically the include
pragma.
If a CIM class or CIM qualifier type to be added already exists in the
target namespace with the same name (comparing case insensitively),
this method raises :exc:`~pywbem.CIMError`.
If a CIM instance to be added already exists in the target namespace
with the same keybinding values, this method raises
:exc:`~pywbem.CIMError`.
In all cases where this method raises an exception, the CIM repository
remains unchanged.
Parameters:
mof_str (:term:`string`):
A string with the MOF definitions to be compiled.
namespace (:term:`string`):
The name of the target CIM namespace in the CIM repository. This
namespace is also used for lookup of any existing or dependent
CIM objects. If `None`, the default namespace of the connection is
used.
search_paths (:term:`py:iterable` of :term:`string`):
An iterable of directory path names where MOF dependent files will
be looked up.
See the description of the `search_path` init parameter of the
:class:`~pywbem.MOFCompiler` class for more information on MOF
dependent files.
verbose (:class:`py:bool`):
Controls whether to issue more detailed compiler messages.
Raises:
IOError: MOF file not found.
:exc:`~pywbem.MOFCompileError`: Compile error in the MOF.
"""
namespace = namespace or self.default_namespace
self._mainprovider.validate_namespace(namespace)
mofcomp = MOFCompiler(self._mofwbemconnection,
search_paths=search_paths,
verbose=verbose, log_func=None)
mofcomp.compile_string(mof_str, namespace)
def compile_schema_classes(self, class_names, schema_pragma_files,
namespace=None, verbose=False):
# pylint: disable=line-too-long
"""
Compile the classes defined by `class_names` and all of their
dependences. The class names must be classes in the defined schema and
with pragma statements in a schema pragma file. Each
schema pragma file in the `schema_pragma_files` parameter must be in a
directory that also encompasses the MOF files for all of the classes
defined in the schema pragma file and the dependencies of those
classes. While the relative paths of all of the CIM class files is
defined in the `schema_pragma_file` the pywbem MOF compiler may also
search for dependencies (ex. superclasses, references, etc.) that are
not specifically listed in the `class_names` and the path of the
`schema_pragma_file` is the top level directory for that search. The
mof schema directory must include:
1. Qualifier declarations defined in a single file with the name
`qualifiers.mof` defined within the directory defined by the
`schema_mof_dir` parameter
2. The file `schema_pragma_file` that defines the location of all
of the CIM class files within the a schema mof directory
hierarchy. This is the `schema_pragma_file` attribute of the
DMTFCIMSchema class.
3. The MOF files, one for each class, for the classes that could be
compiled within the directory hierarchy defined by `schema_mof_dir`.
Only the leaf class names need be included in the `class_names` list
since the compiler will find all dependencies as part of the dependency
resolution for the compile of each class in `class_names`.
Parameters:
class_names (:term:`string` or :class:`py:list` of :term:`string`):
Class names of the classes to be compiled. These class names must
be a subset of the classes defined in `schema_pragma_file`.
schema_pragma_files (:term:`string` or :class:`py:list` of :term:`string`):
Relative or absolute file path(s) of schema pragma files that
include a MOF pragma include statement for each CIM class to be
compiled. This file path is available from
:attr:`pywbem_mock.DMTFCIMSchema.schema_pragma_file`.
namespace (:term:`string`):
Namespace into which the classes and qualifier declarations will
be installed.
verbose (:class:`py:bool`):
If `True`, progress messages are output to stdout as the schema is
downloaded and expanded. Default is `False`.
Raises:
:class:`~pywbem.MOFCompileError`: For errors in MOF parsing, finding
MOF dependencies or issues with the CIM repository.
:exc:`~pywbem.CIMError`: Other errors relating to the target server
environment.
""" # noqa: E501
# pylint: enable:line-too-long
if isinstance(schema_pragma_files, six.string_types):
schema_pragma_files = [schema_pragma_files]
# Build the pragma file and compile for each pragma file in
# schema_pragma_files. The search path for each compile is the
# directory containing that schema_pragma_file
for schema_pragma_file in schema_pragma_files:
search_path = os.path.dirname(schema_pragma_file)
compile_pragma = build_schema_mof(class_names, schema_pragma_file)
self.compile_mof_string(compile_pragma,
namespace=namespace,
search_paths=search_path,
verbose=verbose)
######################################################################
#
# Add Pywbem CIM objects directly to the data store
#
######################################################################
def add_cimobjects(self, objects, namespace=None):
# pylint: disable=line-too-long
"""
Add CIM classes, instances and/or CIM qualifier types (declarations)
to the specified CIM namespace of the CIM repository.
This method adds a copy of the objects presented so that the user may
modify the objects without impacting the repository.
If the namespace does not exist, :exc:`~pywbem.CIMError` with status
CIM_ERR_INVALID_NAMESPACE is raised.
The method imposes very few limits on the objects added. It does
require that the superclass exist for any class added and that
instances added include a path component. If the qualifier flavor
attributes are not set, it sets them to those defined in the Qualifier
Declarations if those exist.
If a CIM class or CIM qualifier type to be added already exists in the
target namespace with the same name (comparing case insensitively),
this method fails, and the CIM repository remains unchanged.
If a CIM instance to be added already exists in the target namespace
with the same keybinding values, this method fails, and the
CIM repository remains unchanged.
Parameters:
objects (:class:`~pywbem.CIMClass` or :class:`~pywbem.CIMInstance` or :class:`~pywbem.CIMQualifierDeclaration`, or list of them):
CIM object or objects to be added to the CIM repository. The
list may contain different kinds of CIM objects.
namespace (:term:`string`):
The name of the target CIM namespace in the CIM repository. This
namespace is also used for lookup of any existing or dependent
CIM objects. If `None`, the default namespace of the connection is
used.
Raises:
ValueError: Invalid input CIM object in `objects` parameter.
TypeError: Invalid type in `objects` parameter.
:exc:`~pywbem.CIMError`: CIM_ERR_INVALID_NAMESPACE: Namespace does
not exist.
:exc:`~pywbem.CIMError`: Failure related to the CIM objects in the
CIM repository.
""" # noqa: E501
# pylint: enable=line-too-long
namespace = namespace or self.default_namespace
self._mainprovider.validate_namespace(namespace)
if isinstance(objects, list):
for obj in objects:
self.add_cimobjects(obj, namespace=namespace)
else:
obj = objects
if isinstance(obj, CIMClass):
cc = obj.copy()
if cc.superclass:
if not self._mainprovider.class_exists(namespace,
cc.superclass):
raise ValueError(
_format("Class {0!A} defines superclass {1!A} but "
"the superclass does not exist in the "
"repository.",
cc.classname, cc.superclass))
# pylint: disable=protected-access
cc1 = self._mainprovider._resolve_class(
cc, namespace,
self.cimrepository.get_qualifier_store(namespace),
verbose=False)
class_store = self.cimrepository.get_class_store(namespace)
class_store.create(cc.classname, cc1.copy())
elif isinstance(obj, CIMInstance):
inst = obj.copy()
if inst.path is None:
raise ValueError(
_format("Instances added must include a path. "
"Instance {0!A} does not include a path",
inst))
if inst.path.namespace is None:
inst.path.namespace = namespace
if inst.path.host is not None:
inst.path.host = None
instance_store = \
self.cimrepository.get_instance_store(namespace)
try:
if instance_store.object_exists(inst.path):
raise ValueError(
_format("Instance {0!A} already exists in "
"CIM repository", inst))
except CIMError as ce:
raise CIMError(
CIM_ERR_FAILED,
_format("Internal failure of add_cimobject operation. "
"Rcvd CIMError {0!A}", ce))
instance_store.create(inst.path, inst)
elif isinstance(obj, CIMQualifierDeclaration):
qual = obj.copy()
qualifier_store = \
self.cimrepository.get_qualifier_store(namespace)
qualifier_store.create(qual.name, qual)
else:
# Internal mocker error
assert False, \
_format("Object to add_cimobjects. {0} invalid type",
type(obj))
def display_repository(self, namespaces=None, dest=None, summary=False,
output_format='mof'):
"""
Display the namespaces and objects in the CIM repository in one of
multiple formats to a destination.
Parameters:
namespaces (:term:`string` or list of :term:`string`):
Limits display output to the specified CIM namespace or namespaces.
If `None`, all namespaces of the CIM repository are displayed.
dest (:term:`string`):
File path of an output file. If `None`, the output is written to
stdout.
summary (:class:`py:bool`):
Flag for summary mode. If `True`, only a summary count of CIM
objects in the specified namespaces of the CIM repository is
produced. If `False`, both the summary count and the details of
the CIM objects are produced.
output_format (:term:`string`):
Output format, one of: 'mof', 'xml', or 'repr'.
"""
# The comments are line oriented.
if output_format == 'mof':
cmt_begin = '// '
cmt_end = ''
elif output_format == 'xml':
cmt_begin = '<!-- '
cmt_end = ' ->'
else:
cmt_begin = ''
cmt_end = ''
if output_format not in OUTPUT_FORMATS:
raise ValueError(
_format("Invalid output format definition {0!A}. "
"{1!A} are valid.", output_format, OUTPUT_FORMATS))
_uprint(dest,
_format(u"{0}========Mock Repo Display fmt={1} "
u"namespaces={2} ========={3}\n",
cmt_begin, output_format,
('all' if namespaces is None
else _format("{0!A}", namespaces)),
cmt_end))
# get all namespace names
repo_ns = sorted(self.namespaces)
for ns in repo_ns:
_uprint(dest,
_format(u"\n{0}NAMESPACE {1!A}{2}\n",
cmt_begin, ns, cmt_end))
self._display_objects('Qualifier Declarations',
self.cimrepository.get_qualifier_store(ns),
ns, cmt_begin, cmt_end, dest=dest,
summary=summary, output_format=output_format)
self._display_objects('Classes',
self.cimrepository.get_class_store(ns), ns,
cmt_begin, cmt_end, dest=dest,
summary=summary, output_format=output_format)
self._display_objects('Instances',
self.cimrepository.get_instance_store(ns), ns,
cmt_begin, cmt_end, dest=dest,
summary=summary, output_format=output_format)
_uprint(dest,
_format(u'{0}============End Repository================={1}',
cmt_begin, cmt_end))
@staticmethod
def _display_objects(obj_type, object_repo, namespace, cmt_begin, cmt_end,
dest=None, summary=None, output_format=None):
"""
Display a set of objects of obj_type from the dictionary defined
by the parameter object_repo. obj_type is a string that defines the
type of object ('Classes', 'Instances', 'Qualifier Declarations',
'Methods').
"""
# Issue #2062: TODO/ks FUTURE Consider sorting to preserve order of
# compile/add. Make this part of refactor to separate repository and
# datastore because it may be data store dependent
if obj_type == 'Methods':
_uprint(dest,
_format(u"{0}Namespace {1!A}: contains {2} {3}:{4}\n",
cmt_begin, namespace,
len(object_repo),
obj_type, cmt_end))
else:
_uprint(dest,
_format(u"{0}Namespace {1!A}: contains {2} {3} {4}\n",
cmt_begin, namespace,
object_repo.len(),
obj_type, cmt_end))
if summary:
return
# instances are special because the inner struct is a list
if obj_type == 'Instances':
# Issue # 2062 - Consider sorting here in the future
for inst in object_repo.iter_values():
if output_format == 'xml':
_uprint(dest,
_format(u"{0} Path={1} {2}\n{3}",
cmt_begin, inst.path.to_wbem_uri(),
cmt_end,
_pretty_xml(inst.tocimxmlstr())))
elif output_format == 'repr':
_uprint(dest,
_format(u"Path:\n{0!A}\nInst:\n{1!A}\n",
inst.path, inst))
else:
_uprint(dest,
_format(u"{0} Path={1} {2}\n{3}",
cmt_begin, inst.path.to_wbem_uri(),
cmt_end, inst.tomof()))
elif obj_type == 'Methods':
try:
methods = object_repo
except KeyError:
return
for cln in methods:
for method in methods[cln]:
_uprint(dest,
_format(u"{0}Class: {1}, method: {2}, "
u"callback: {3} {4}",
cmt_begin, cln, method,
methods[cln][method].__name__,
cmt_end))
else:
# Display classes and qualifier declarations sorted
assert obj_type in ['Classes', 'Qualifier Declarations']
names = list(object_repo.iter_names())
if names:
for name in sorted(names):
obj = object_repo.get(name)
if output_format == 'xml':
_uprint(dest, _pretty_xml(obj.tocimxmlstr()))
elif output_format == 'repr':
_uprint(dest, _format(u"{0!A}", obj))
else:
_uprint(dest, obj.tomof())
def register_provider(self, provider, namespaces=None,
schema_pragma_files=None, verbose=None):
# pylint: disable=line-too-long
"""
Register the `provider` object for specific namespaces and CIM classes.
Registering a provider tells the FakedWBEMConnection that the provider
implementation provided with this method as the `provider` parameter is
to be executed as the request response method for the namespaces
defined in the `namespaces` parameter, the provider type defined in the
'provider_type` attribute of the `provider` and the class(es)
defined in the provider `provider_classnames` attribute of the
`provider`.
The provider registration process includes:
1. Validation that the namespaces defined for the provider exist.
2. Validation that the superclass of the provider is consistent with
the `provider_type` attribute defined in the provider.
3. Installation of any CIM classes defined by the provider
`provider_classnames` attribute including dependencies for these
classes using the `schema_pragma_files` parameter to define the MOF
compiler search directories for dependencies.
4. Adding the provider to the registry of user_providers so that any
of the request methods defined for the `provider_type` are
passed to this provider in place of the default request processors.
5. Execute post_register_setup() call to the provider to allow the
provider to perform any special setup functionality.
Providers can only be registered for the following request response
methods:
1. provider_type = 'instance': defines methods for CreateInstance,
ModifyInstance, and DeleteInstance requests within a subclass of
the `InstanceWriteProvider` class.
2. provider_type = 'method': defines a InvokeMethod method within
a subclass of the `MethodProvider` class.
Each classname in a particular namespace may have at most one provider
registered
Parameters:
provider (instance of subclass of :class:`pywbem_mock.InstanceWriteProvider` or :class:`pywbem_mock.MethodProvider`):
An instance of the user provider class which is a subclass of
:class:`pywbem_mock.InstanceWriteProvider`. The methods in this
subclass override the corresponding methods in
InstanceWriteProvider. The method call parameters must be the
same as the defult method in InstanceWriteProvider and it must
return data in the same format if the default method returns data.
namespaces (:term:`string` or :class:`py:list` of :term:`string`):
Namespace or namespaces for which the provider is to be registered.
If `None`, the default namespace of the connection will be used.