/
random_objects.py
executable file
·340 lines (293 loc) · 10.8 KB
/
random_objects.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
"""
Utility functions for creating random CIM objects.
"""
from collections import namedtuple
import string
import random
from datetime import datetime
from pywbem import CIMQualifier, CIMProperty, CIMClass, CIMInstance, \
CIMInstanceName, CIMDateTime
from pywbem_mock import FakedWBEMConnection
ASCII_CHARS = [chr(i) for i in range(0x0020, 0x007e + 1)]
#: The CIM integer type names
INTEGER_TYPES = (
'uint8', 'uint16', 'uint32', 'uint64',
'sint8', 'sint16', 'sint32', 'sint64',
)
#: The CIM floating point type names
REAL_TYPES = ('real32', 'real64')
#: Range of integer values, for various purposes
#: Attributes:
#: - min (int): Minimum value (inclusive)
#: - min (int): Maximum value (inclusive)
Range = namedtuple(
'Range',
['min', 'max']
)
#: Profile for generating typed CIM values
#: Attributes:
#: - types (tuple of string): CIM type names
#: - string_len (Range): Lengths of string values
ValueProfile = namedtuple(
'ValueProfile',
['types', 'string_len']
)
#: A meaningful set of CIM qualifier values
#: Attributes:
#: - names (tuple of string): Names of the qualifiers in the set
#: - types (tuple of string: CIM type names for the rs in the set
#: - values (ValueProfile or value): Qualifier values
QualifierSet = namedtuple(
'QualifierSet',
['names', 'types', 'values']
)
#: Profile for picking a random set of CIM qualifier values
#: Attributes:
#: - qualifier_sets (tuple of QualifierSet): Qualifier sets to pick from
QualifierSetProfile = namedtuple(
'QualifierSetProfile',
['qualifier_sets']
)
#: Profile for generating multiple CIM properties for use in CIM classes
#: Attributes:
#: - name_len (Range): Lengths of property names
#: - value_profile (ValueProfile): Property value profile
#: - value_ratio (float): Ratio of properties that have a value
#: - description_len (Range): Lengths of property Description qualifier;
#: None for no Description qualifier
#: - qualifier_set_profile (QualifierSetProfile): Qualifier set profile;
#: None for no additional qualifiers
PropertyProfile = namedtuple(
'PropertyProfile',
['name_len', 'value_profile', 'value_ratio', 'description_len',
'qualifier_set_profile']
)
def random_name(length_range):
"""
Return a random valid CIM name of a length that is randomly in a length
range.
Parameters:
length_range (Range): Length range.
"""
first_chars = string.ascii_uppercase
other_chars = string.ascii_uppercase + string.ascii_lowercase + \
string.digits
length = random.randint(length_range.min, length_range.max)
name = random.choice(first_chars) + ''.join(
random.choice(other_chars) for _ in range(length))
return name
def random_string(length_range, charset='ascii'):
"""
Return a random Unicode string value of a length that is randomly in a
length range, and the string characters are in a character set.
Parameters:
length_range (Range): Length range.
charset (string): CHaracter set for the values. Valid character sets
are:
- 'ascii' - Printable 7-bit ASCII characters (U+0020 .. U+007E)
"""
if charset == 'ascii':
chars = ASCII_CHARS
else:
raise ValueError(f"Invalid charset: {charset}")
length = random.randint(length_range.min, length_range.max)
value = ''.join(random.choice(chars) for _ in range(length))
return value
def random_type_value(values, type=None):
# pylint: disable=redefined-builtin
"""
Return a random valid tuple of CIM type name and value according to
a value profile.
Parameters:
values (ValueProfile or value): Values
type (string): Use this type instead of a random type from the profile
"""
if not isinstance(values, ValueProfile):
if type is None:
raise ValueError("Specifying a value directly requires specifying "
"a type")
return type, values
if type is None:
type = random.choice(values.types)
if type == 'string':
value = random_string(values.string_len)
elif type == 'char16':
value = random_string(Range(1, 1))
elif type in INTEGER_TYPES:
# TODO: Consider value range of type
value = random.randint(0, 127)
elif type in REAL_TYPES:
# TODO: Consider value range of type
value = float(random.randint(-1000, 1000))
elif type in 'boolean':
value = random.choice((True, False))
elif type in 'datetime':
year = random.randint(1, 3000)
month = random.randint(1, 12)
day = random.randint(1, 31)
hour = random.randint(0, 23)
minute = random.randint(0, 59)
second = random.randint(0, 59)
value = CIMDateTime(datetime(year, month, day, hour, minute, second))
else:
# The 'reference' type is intentionally not supported here
raise AssertionError(f'Invalid CIM type name: {type}')
return type, value
def make_properties(number, property_profile):
"""
Construct and return a list of CIMProperty objects for use on CIM classes,
that has the specified number of properties according to the specified
property profile.
"""
props = []
for _ in range(0, number):
pname = random_name(property_profile.name_len)
ptype, pvalue = random_type_value(property_profile.value_profile)
if random.random() > property_profile.value_ratio:
pvalue = None
pquals = []
if property_profile.description_len:
pquals.append(CIMQualifier(
'Description', type='string',
value=random_string(property_profile.description_len)))
if property_profile.qualifier_set_profile:
qset = random.choice(
property_profile.qualifier_set_profile.qualifier_sets)
for j, qname in enumerate(qset.names):
qtype = qset.types[j]
_, qvalue = random_type_value(qset.values, type=qtype)
pquals.append(CIMQualifier(qname, type=qtype, value=qvalue))
prop = CIMProperty(pname, type=ptype, value=pvalue, qualifiers=pquals)
props.append(prop)
return props
def make_class(properties):
"""
Construct and return a CIMClass object from the specified properties.
"""
cname = random_name(Range(8, 16))
cls = CIMClass(cname, properties=properties)
return cls
def make_instances(cls, number, value_profile):
"""
Construct and return a number of CIMInstance objects with path, with
random values for the instance properties.
"""
insts = []
for _ in range(0, number):
inst = CIMInstance(cls.classname)
# TODO: Make namespace flexible
inst.path = CIMInstanceName(cls.classname, namespace='root/cimv2')
for pname, cls_prop in cls.properties.items():
ptype = cls_prop.type
_, pvalue = random_type_value(value_profile, type=ptype)
inst_prop = CIMProperty(pname, type=ptype, value=pvalue)
inst.properties[pname] = inst_prop
if cls_prop.qualifiers.get('Key', False):
inst.path.keybindings[pname] = pvalue
insts.append(inst)
return insts
def tst_random_class(num_props):
"""
Return a CIMClass object with a random class definition that has the
specified number of properties.
"""
prop_qset_key = QualifierSet(
('Key',),
('boolean',),
True)
prop_qset_string = QualifierSet(
('MinLen', 'MaxLen'),
('uint32', 'uint32'),
ValueProfile(('uint32',), None))
prop_qset_uint32 = QualifierSet(
('MinValue', 'MaxValue'),
('sint64', 'sint64'),
ValueProfile(('sint64',), None))
prop_profile_key = PropertyProfile(
Range(4, 16), # name_len
ValueProfile(('string',), Range(8, 64)),
0, # value_ratio
Range(16, 256), # description_len
QualifierSetProfile((prop_qset_key,))
)
prop_profile_string = PropertyProfile(
Range(4, 16), # name_len
ValueProfile(('string',), Range(8, 64)),
0.2, # value_ratio
Range(16, 256), # description_len
QualifierSetProfile((prop_qset_string,))
)
prop_profile_uint32 = PropertyProfile(
Range(4, 16), # name_len
ValueProfile(('uint32',), None),
0.2, # value_ratio
Range(16, 256), # description_len
QualifierSetProfile((prop_qset_uint32,))
)
prop_profile_boolean = PropertyProfile(
Range(4, 16), # name_len
ValueProfile(('boolean',), None),
0.2, # value_ratio
Range(16, 256), # description_len
None # no qualifiers
)
rem_props = num_props
num_key = 1
rem_props -= num_key
num_string = int(rem_props * 0.4)
rem_props -= num_string
num_uint32 = int(rem_props * 0.6)
rem_props -= num_uint32
num_boolean = rem_props
props = []
props.extend(make_properties(num_key, prop_profile_key))
props.extend(make_properties(num_string, prop_profile_string))
props.extend(make_properties(num_uint32, prop_profile_uint32))
props.extend(make_properties(num_boolean, prop_profile_boolean))
cls = make_class(props)
return cls
def tst_random_instances(cls, num_insts):
"""
Return a list of the specified number of CIMInstance objects of the
specified class with random property values.
"""
value_profile_all = ValueProfile(
('string', 'uint32', 'boolean'),
Range(8, 64))
instances = make_instances(cls, num_insts, value_profile_all)
return instances
def tst_random_conn(num_insts, num_props):
"""
Build a FakedWBEMConnection with a mock environment that contains:
- the qualifiers needed for the class
- a class with the specified number of properties of types string, uint32,
boolean, with one string-typed key property
- the specified number of instances of that class, with random property
values
Returns:
tuple(FakedWBEMConnection, CIMClass, list(CIMInstance))
"""
qualifiers_mof = """
Qualifier Description : string = null,
Scope(any),
Flavor(EnableOverride, ToSubclass, Translatable);
Qualifier Key : boolean = false,
Scope(property, reference),
Flavor(DisableOverride, ToSubclass);
Qualifier MaxLen : uint32 = null,
Scope(property, method, parameter);
Qualifier MaxValue : sint64 = null,
Scope(property, method, parameter);
Qualifier MinLen : uint32 = 0,
Scope(property, method, parameter);
Qualifier MinValue : sint64 = null,
Scope(property, method, parameter);
"""
cls = tst_random_class(num_props)
instances = tst_random_instances(cls, num_insts)
# Build mock environment (using default namespace)
conn = FakedWBEMConnection(disable_pull_operations=True)
conn.compile_mof_string(qualifiers_mof)
conn.add_cimobjects(cls)
conn.add_cimobjects(instances)
return conn, cls, instances