Skip to content

Commit a1ea3db

Browse files
committed
get_properties: property collector action
1 parent 3163bd2 commit a1ea3db

File tree

4 files changed

+336
-3
lines changed

4 files changed

+336
-3
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ PYVMOMI 6.0 requires alternative connection coding and Python 2.7.9 minimum due
207207
## Actions
208208

209209
* `vsphere.get_moid` - Returns the MOID of vSphere managed entity corresponding to the specified parameters
210+
* `vsphere.get_properties` - Retrieves some or all properties of some or all objects through the PropertyCollector.
210211
* `vsphere.get_vmconsole_urls` - Retrieves urls of the virtual machines' consoles
211212
* `vsphere.get_vms` - Retrieves the virtual machines on a vCenter Server system. It computes the union of Virtual Machine sets based on each parameter.
212213
* `vsphere.guest_dir_create` - Create a directory inside the guest.
@@ -224,8 +225,8 @@ PYVMOMI 6.0 requires alternative connection coding and Python 2.7.9 minimum due
224225
* `vsphere.set_vm` - Changes configuration of a Virtual Machine.
225226
* `vsphere.vm_check_tools` - Wait for a Task to complete and returns its result.
226227
* `vsphere.vm_create_from_template` - Create a new VM from existing template.
227-
* `vsphere.vm_env_items_get` - Retrieve list of Objects from VSphere
228-
* `vsphere.vm_guest_info_get` - Retrieve Guest details of a VM object
228+
* `vsphere.vm_env_items_get` - Retrieve list of Objects from VSphere (alternatively, use get_properties type=<type> property=name)
229+
* `vsphere.vm_guest_info_get` - Retrieve Guest details of a VM object (deprecated: use get_properties type=VirtualMachine property=guest)
229230
* `vsphere.vm_hw_barebones_create` - Create BareBones VM (CPU, Ram, Graphics Only)
230231
* `vsphere.vm_hw_basic_build` - Minstral Flow to Build Basic Server and power it on.
231232
* `vsphere.vm_hw_cpu_mem_edit` - Adjust the CPU and RAM values assigned to a Virtual Machine
@@ -239,7 +240,7 @@ PYVMOMI 6.0 requires alternative connection coding and Python 2.7.9 minimum due
239240
* `vsphere.vm_hw_remove` - Removes the Virtual Machine.
240241
* `vsphere.vm_hw_scsi_controller_add` - Add SCSI HDD Controller device to VM
241242
* `vsphere.vm_hw_uuid_get` - Retrieve VM UUID
242-
* `vsphere.vm_runtime_info_get` - Retrieves the Runtime information for a VM.
243+
* `vsphere.vm_runtime_info_get` - Retrieves the Runtime information for a VM (deprecated: use get_properties type=VirtualMachine property=runtime)
243244
* `vsphere.wait_task` - Wait for a Task to complete and returns its result.
244245

245246
## Known Bugs

actions/get_properties.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2+
# contributor license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright ownership.
4+
# The ASF licenses this file to You under the Apache License, Version 2.0
5+
# (the "License"); you may not use this file except in compliance with
6+
# the License. You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
from pyVmomi import vim, vmodl # pylint: disable-msg=E0611
17+
from vmwarelib.actions import BaseAction
18+
19+
import copy
20+
import datetime
21+
import json
22+
import pyVmomi
23+
24+
25+
class GetProperties(BaseAction):
26+
27+
def run(self, type, property, id, raw, vsphere=None):
28+
"""
29+
Leverage the Property Collector to retrieve properties from any
30+
Managed Object.
31+
32+
Args:
33+
- type: vimType
34+
- properties: optional array of properties to get (default: all)
35+
- ids: optional array of MOIDs to limit results (default: all)
36+
- vsphere: pre-configured connection information
37+
38+
Returns:
39+
- dict: key = moid, value = dict of properties
40+
"""
41+
42+
self.establish_connection(vsphere)
43+
return self.collect(self.si_content, type, property, id, raw)
44+
45+
def collect(self, content, type, properties, ids, raw):
46+
"""
47+
Leverage the Property Collector to retrieve properties from any
48+
Managed Object.
49+
50+
Args:
51+
- content: service instance content
52+
- type: object type
53+
- properties: optional array of properties to get (default: all)
54+
- ids: optional array of MOIDs to limit results (default: all)
55+
56+
Returns:
57+
- dict: key = moid, value = dict of properties
58+
"""
59+
60+
vimtype = getattr(vim, type)
61+
62+
rootFolder = content.rootFolder
63+
viewMgr = content.viewManager
64+
if not ids:
65+
view = viewMgr.CreateContainerView(container=rootFolder,
66+
type=[vimtype],
67+
recursive=True)
68+
else:
69+
view = viewMgr.CreateListView()
70+
for id in ids:
71+
view.ModifyListView(add=[
72+
pyVmomi.VmomiSupport.GetWsdlType('urn:vim25', type)(id)])
73+
74+
traversal_spec = vmodl.query.PropertyCollector.TraversalSpec()
75+
traversal_spec.name = 'traverseEntities'
76+
traversal_spec.path = 'view'
77+
traversal_spec.skip = False
78+
traversal_spec.type = view.__class__
79+
80+
obj_spec = vmodl.query.PropertyCollector.ObjectSpec()
81+
obj_spec.obj = view
82+
obj_spec.skip = True
83+
obj_spec.selectSet = [traversal_spec]
84+
85+
property_spec = vmodl.query.PropertyCollector.PropertySpec()
86+
property_spec.type = vimtype
87+
if not properties:
88+
property_spec.all = True
89+
else:
90+
property_spec.pathSet = properties
91+
92+
filter_spec = vmodl.query.PropertyCollector.FilterSpec()
93+
filter_spec.objectSet = [obj_spec]
94+
filter_spec.propSet = [property_spec]
95+
96+
rawdata = content.propertyCollector.RetrieveContents([filter_spec])
97+
return self.transform(ids, rawdata) if not raw else rawdata
98+
99+
def jsonify_vsphere_obj(self, obj):
100+
"""JSONify a vSphere Managed/Data object."""
101+
class PyVmomiObjectJSONEncoder(json.JSONEncoder):
102+
"""Custom JSON encoder to encode vSphere object."""
103+
def __init__(self, *args, **kwargs):
104+
super(PyVmomiObjectJSONEncoder, self).__init__(*args, **kwargs)
105+
106+
def default(self, obj): # pylint: disable=method-hidden
107+
if isinstance(obj, datetime.datetime):
108+
return pyVmomi.Iso8601.ISO8601Format(obj)
109+
elif isinstance(obj, pyVmomi.VmomiSupport.DataObject):
110+
# eliminate the very annoying Dynamic fields if empty
111+
if (obj.__dict__['dynamicType'] is None and
112+
len(obj.__dict__['dynamicProperty']) == 0):
113+
tmp = copy.deepcopy(obj.__dict__)
114+
tmp.pop('dynamicType')
115+
tmp.pop('dynamicProperty')
116+
return tmp
117+
return obj.__dict__
118+
elif isinstance(obj, pyVmomi.VmomiSupport.ManagedObject):
119+
return unquote(obj).split(':')[-1]
120+
elif isinstance(obj, type):
121+
return str(obj)
122+
return json.JSONEncoder.default(self, obj)
123+
return json.loads(PyVmomiObjectJSONEncoder().encode(obj))
124+
125+
def transform(self, ids, rawdata):
126+
result = {}
127+
for obj in rawdata:
128+
objid = unquote(obj.obj).split(':')[-1]
129+
ps = {}
130+
for prop in obj.propSet:
131+
ps[unquote(prop.name)] = self.jsonify_vsphere_obj(prop.val)
132+
result[objid] = ps
133+
return (not ids or sorted(result.keys()) == sorted(ids), result)
134+
135+
136+
def unquote(item):
137+
return str(item).strip("'")

actions/get_properties.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
name: get_properties
3+
runner_type: python-script
4+
description: Get properties of managed objects through the PropertyCollector.
5+
enabled: true
6+
entry_point: get_properties.py
7+
parameters:
8+
type:
9+
type: string
10+
description: The type of object to get. This type must be compatible with CreateContainerView.
11+
required: true
12+
position: 0
13+
enum:
14+
- ComputeResource
15+
- Datacenter
16+
- Datastore
17+
- Folder
18+
- HostSystem
19+
- ManagedEntity
20+
- Network
21+
- ResourcePool
22+
- VirtualMachine
23+
id:
24+
type: array
25+
description: MOIDs to restrict the results to. Omit to retrieve all objects.
26+
required: false
27+
position: 1
28+
property:
29+
type: array
30+
description: Fully qualified property within the type (dot separated), such as 'config.hardware.memoryMB' for a VirtualMachine. Omit to retrieve all properties.
31+
required: false
32+
position: 2
33+
raw:
34+
type: boolean
35+
description: If True, return the raw result of the PropertyCollector.RetrieveContents call.
36+
required: false
37+
default: false
38+
position: 3
39+
vsphere:
40+
type: string
41+
description: Pre-Configured vsphere connection details
42+
required: false
43+
position: 4
44+
default: ~
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2+
# contributor license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright ownership.
4+
# The ASF licenses this file to You under the Apache License, Version 2.0
5+
# (the "License"); you may not use this file except in compliance with
6+
# the License. You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
15+
import copy
16+
import datetime
17+
import mock
18+
import pyVmomi
19+
20+
from get_properties import GetProperties
21+
from pyVmomi import vim # pylint: disable-msg=E0611
22+
from vsphere_base_action_test_case import VsphereBaseActionTestCase
23+
24+
__all__ = [
25+
'GetPropertiesTestCase'
26+
]
27+
28+
29+
class GetPropertiesTestCase(VsphereBaseActionTestCase):
30+
__test__ = True
31+
action_cls = GetProperties
32+
33+
transformed = {
34+
"vm-22": {
35+
"config.hardware.memoryMB": 10240,
36+
"dataobj-dyn": {
37+
"arguments": "arguments",
38+
'dynamicType': "Hello",
39+
'dynamicProperty': [],
40+
"envVariables": ["A=B", "C=D"],
41+
"programPath": "cmd.exe",
42+
"workingDirectory": "/tmp"
43+
},
44+
"guest.ipAddress": "10.99.0.4",
45+
"name": "VCSA",
46+
"network": ["network-15"],
47+
"time": "1992-01-12T01:01:22Z"
48+
},
49+
"vm-46": {
50+
"config.hardware.memoryMB": 2048,
51+
"dataobj-nodyn": {
52+
"arguments": "arguments",
53+
"envVariables": ["A=B", "C=D"],
54+
"programPath": "cmd.exe",
55+
"workingDirectory": "/tmp"
56+
},
57+
"guest.ipAddress": "fe80::250:56ff:feb4:cfb9",
58+
"name": "testvm",
59+
"network": [],
60+
"time": "1992-01-17T01:01:46Z"
61+
}
62+
}
63+
64+
def setUp(self):
65+
def mockDynamicProperty(name, val):
66+
result = mock.Mock()
67+
result.name = name
68+
result.val = val
69+
return result
70+
super(GetPropertiesTestCase, self).setUp()
71+
self._action = self.get_action_instance(self.new_config)
72+
self._action.establish_connection = mock.Mock()
73+
self._action.si_content = mock.Mock()
74+
self._action.si_content.rootFolder = mock.Mock()
75+
self._action.si_content.viewManager = mock.Mock()
76+
self._action.si_content.viewManager.CreateContainerView = mock.Mock()
77+
self._action.si_content.viewManager.CreateListView = mock.Mock()
78+
self._action.si_content.propertyCollector = mock.Mock()
79+
cmdspec22 = vim.vm.guest.ProcessManager.ProgramSpec(
80+
arguments="arguments",
81+
envVariables=["A=B", "C=D"],
82+
programPath="cmd.exe",
83+
workingDirectory='/tmp')
84+
cmdspec46 = copy.deepcopy(cmdspec22)
85+
cmdspec22.__dict__['dynamicType'] = 'Hello'
86+
# mock up the raw objects used to test transform
87+
vm22 = mock.Mock()
88+
vm22.obj = 'vim.VirtualMachine:vm-22'
89+
vm22.propSet = [mockDynamicProperty("config.hardware.memoryMB", 10240),
90+
mockDynamicProperty("dataobj-dyn", cmdspec22),
91+
mockDynamicProperty("guest.ipAddress", "10.99.0.4"),
92+
mockDynamicProperty("name", "VCSA"),
93+
mockDynamicProperty("network", [
94+
pyVmomi.VmomiSupport.GetWsdlType(
95+
'urn:vim25', 'Network')(
96+
'network-15')]),
97+
mockDynamicProperty("time",
98+
datetime.datetime(
99+
1992, 1, 12, 1, 1, 22))]
100+
vm46 = mock.Mock()
101+
vm46.obj = 'vim.VirtualMachine:vm-46'
102+
vm46.propSet = [mockDynamicProperty("config.hardware.memoryMB", 2048),
103+
mockDynamicProperty("dataobj-nodyn", cmdspec46),
104+
mockDynamicProperty("guest.ipAddress",
105+
"fe80::250:56ff:feb4:cfb9"),
106+
mockDynamicProperty("name", "testvm"),
107+
mockDynamicProperty("network", []),
108+
mockDynamicProperty("time",
109+
datetime.datetime(
110+
1992, 1, 17, 1, 1, 46))]
111+
self.raw = [vm22, vm46]
112+
113+
@mock.patch('pyVmomi.vmodl.query.PropertyCollector')
114+
def test_simple_property_by_id(self, pc):
115+
with mock.patch.object(
116+
self._action.si_content.propertyCollector, 'RetrieveContents',
117+
return_value=self.raw):
118+
result = self._action.run(type='VirtualMachine',
119+
id=['vm-22', 'vm-46'],
120+
property=['does.not.exist'], raw=False)
121+
assert self._action.si_content.viewManager.CreateListView.called
122+
# status True because every object was found
123+
self.assertEqual(result, (True, self.transformed))
124+
with mock.patch.object(
125+
self._action.si_content.propertyCollector, 'RetrieveContents',
126+
return_value=self.raw):
127+
result = self._action.run(type='VirtualMachine',
128+
id=['vm-22', 'vm-46', 'vm-47'],
129+
property=None, raw=False)
130+
# status False because not every object was found
131+
self.assertEqual(result, (False, self.transformed))
132+
133+
@mock.patch('pyVmomi.vmodl.query.PropertyCollector')
134+
def test_simple_by_type(self, pc):
135+
with mock.patch.object(
136+
self._action.si_content.propertyCollector, 'RetrieveContents',
137+
return_value=self.raw):
138+
result = self._action.run(type='VirtualMachine', id=None,
139+
property=None, raw=False)
140+
assert\
141+
self._action.si_content.viewManager.CreateContainerView.called
142+
self.assertEqual(result, (True, self.transformed))
143+
144+
@mock.patch('pyVmomi.vmodl.query.PropertyCollector')
145+
def test_raw_output(self, pc):
146+
with mock.patch.object(
147+
self._action.si_content.propertyCollector, 'RetrieveContents',
148+
return_value=self.raw):
149+
result = self._action.run(type='VirtualMachine', id=None,
150+
property=None, raw=True)
151+
self.assertNotEqual(result, self.transformed)

0 commit comments

Comments
 (0)