Skip to content

Commit d78f9e5

Browse files
committed
get_properties: property collector action
1 parent 35e296c commit d78f9e5

File tree

4 files changed

+263
-3
lines changed

4 files changed

+263
-3
lines changed

README.md

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

150150
* `vsphere.get_moid` - Returns the MOID of vSphere managed entity corresponding to the specified parameters
151+
* `vsphere.get_properties` - Retrieves some or all properties of some or all objects through the PropertyCollector.
151152
* `vsphere.get_vmconsole_urls` - Retrieves urls of the virtual machines' consoles
152153
* `vsphere.get_vms` - Retrieves the virtual machines on a vCenter Server system. It computes the union of Virtual Machine sets based on each parameter.
153154
* `vsphere.hello_vsphere` - Wait for a Task to complete and returns its result.
@@ -156,8 +157,8 @@ PYVMOMI 6.0 requires alternative connection coding and Python 2.7.9 minimum due
156157
* `vsphere.set_vm` - Changes configuration of a Virtual Machine.
157158
* `vsphere.vm_check_tools` - Wait for a Task to complete and returns its result.
158159
* `vsphere.vm_create_from_template` - Create a new VM from existing template.
159-
* `vsphere.vm_env_items_get` - Retrieve list of Objects from VSphere
160-
* `vsphere.vm_guest_info_get` - Retrieve Guest details of a VM object
160+
* `vsphere.vm_env_items_get` - Retrieve list of Objects from VSphere (alternatively, use get_properties type=<type> property=name)
161+
* `vsphere.vm_guest_info_get` - Retrieve Guest details of a VM object (deprecated: use get_properties type=VirtualMachine property=guest)
161162
* `vsphere.vm_hw_barebones_create` - Create BareBones VM (CPU, Ram, Graphics Only)
162163
* `vsphere.vm_hw_basic_build` - Minstral Flow to Build Basic Server and power it on.
163164
* `vsphere.vm_hw_cpu_mem_edit` - Adjust the CPU and RAM values assigned to a Virtual Machine
@@ -171,7 +172,7 @@ PYVMOMI 6.0 requires alternative connection coding and Python 2.7.9 minimum due
171172
* `vsphere.vm_hw_remove` - Removes the Virtual Machine.
172173
* `vsphere.vm_hw_scsi_controller_add` - Add SCSI HDD Controller device to VM
173174
* `vsphere.vm_hw_uuid_get` - Retrieve VM UUID
174-
* `vsphere.vm_runtime_info_get` - Retrieves the Runtime information for a VM.
175+
* `vsphere.vm_runtime_info_get` - Retrieves the Runtime information for a VM (deprecated: use get_properties type=VirtualMachine property=runtime)
175176
* `vsphere.wait_task` - Wait for a Task to complete and returns its result.
176177

177178
## Known Bugs

actions/get_properties.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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+
import pyVmomi # pylint: disable-msg=E0611
17+
18+
from vmwarelib.actions import BaseAction
19+
20+
21+
class GetProperties(BaseAction):
22+
23+
def run(self, type, property, id, raw, vsphere=None):
24+
"""
25+
Leverage the Property Collector to retrieve properties from any
26+
Managed Object.
27+
28+
Args:
29+
- type: vimType
30+
- properties: optional array of properties to get (default: all)
31+
- ids: optional array of MOIDs to limit results (default: all)
32+
- vsphere: pre-configured connection information
33+
34+
Returns:
35+
- dict: key = moid, value = dict of properties
36+
"""
37+
38+
self.establish_connection(vsphere)
39+
return self.collect(self.si_content, type, property, id, raw)
40+
41+
def collect(self, content, type, properties, ids, raw):
42+
"""
43+
Leverage the Property Collector to retrieve properties from any
44+
Managed Object.
45+
46+
Args:
47+
- content: service instance content
48+
- type: object type
49+
- properties: optional array of properties to get (default: all)
50+
- ids: optional array of MOIDs to limit results (default: all)
51+
52+
Returns:
53+
- dict: key = moid, value = dict of properties
54+
"""
55+
56+
vimtype = getattr(pyVmomi.vim, type)
57+
58+
rootFolder = content.rootFolder
59+
viewMgr = content.viewManager
60+
if not ids:
61+
view = viewMgr.CreateContainerView(container=rootFolder,
62+
type=[vimtype],
63+
recursive=True)
64+
else:
65+
view = viewMgr.CreateListView()
66+
for id in ids:
67+
view.ModifyListView(add=[
68+
pyVmomi.VmomiSupport.GetWsdlType('urn:vim25', type)(id)])
69+
70+
traversal_spec = pyVmomi.vmodl.query.PropertyCollector.TraversalSpec()
71+
traversal_spec.name = 'traverseEntities'
72+
traversal_spec.path = 'view'
73+
traversal_spec.skip = False
74+
traversal_spec.type = view.__class__
75+
76+
obj_spec = pyVmomi.vmodl.query.PropertyCollector.ObjectSpec()
77+
obj_spec.obj = view
78+
obj_spec.skip = True
79+
obj_spec.selectSet = [traversal_spec]
80+
81+
property_spec = pyVmomi.vmodl.query.PropertyCollector.PropertySpec()
82+
property_spec.type = vimtype
83+
if not properties:
84+
property_spec.all = True
85+
else:
86+
property_spec.pathSet = properties
87+
88+
filter_spec = pyVmomi.vmodl.query.PropertyCollector.FilterSpec()
89+
filter_spec.objectSet = [obj_spec]
90+
filter_spec.propSet = [property_spec]
91+
92+
rawdata = content.propertyCollector.RetrieveContents([filter_spec])
93+
return self.transform(ids, rawdata) if not raw else rawdata
94+
95+
def transform(self, ids, rawdata):
96+
result = {}
97+
for obj in rawdata:
98+
objid = self.unquote(obj.obj).split(':')[-1]
99+
ps = {}
100+
for prop in obj.propSet:
101+
ps[self.unquote(prop.name)] = prop.val
102+
result[objid] = ps
103+
return (not ids or sorted(result.keys()) == sorted(ids), result)
104+
105+
def unquote(self, quoted):
106+
return str(quoted).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: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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 mock
16+
17+
from get_properties import GetProperties
18+
from vsphere_base_action_test_case import VsphereBaseActionTestCase
19+
20+
__all__ = [
21+
'GetPropertiesTestCase'
22+
]
23+
24+
25+
class GetPropertiesTestCase(VsphereBaseActionTestCase):
26+
__test__ = True
27+
action_cls = GetProperties
28+
29+
transformed = {
30+
"vm-22": {
31+
"config.hardware.memoryMB": 10240,
32+
"guest.ipAddress": "10.99.0.4",
33+
"name": "VCSA"
34+
},
35+
"vm-46": {
36+
"config.hardware.memoryMB": 2048,
37+
"guest.ipAddress": "fe80::250:56ff:feb4:cfb9",
38+
"name": "testvm"
39+
}
40+
}
41+
42+
def setUp(self):
43+
def mockDynamicProperty(name, val):
44+
result = mock.Mock()
45+
result.name = name
46+
result.val = val
47+
return result
48+
super(GetPropertiesTestCase, self).setUp()
49+
self._action = self.get_action_instance(self.new_config)
50+
self._action.establish_connection = mock.Mock()
51+
self._action.si_content = mock.Mock()
52+
self._action.si_content.rootFolder = mock.Mock()
53+
self._action.si_content.viewManager = mock.Mock()
54+
self._action.si_content.viewManager.CreateContainerView = mock.Mock()
55+
self._action.si_content.viewManager.CreateListView = mock.Mock()
56+
self._action.si_content.propertyCollector = mock.Mock()
57+
# mock up the raw objects used to test transform
58+
vm22 = mock.Mock()
59+
vm22.obj = 'vim.VirtualMachine:vm-22'
60+
vm22.propSet = [mockDynamicProperty("config.hardware.memoryMB", 10240),
61+
mockDynamicProperty("guest.ipAddress", "10.99.0.4"),
62+
mockDynamicProperty("name", "VCSA")]
63+
vm46 = mock.Mock()
64+
vm46.obj = 'vim.VirtualMachine:vm-46'
65+
vm46.propSet = [mockDynamicProperty("config.hardware.memoryMB", 2048),
66+
mockDynamicProperty("guest.ipAddress",
67+
"fe80::250:56ff:feb4:cfb9"),
68+
mockDynamicProperty("name", "testvm")]
69+
self.raw = [vm22, vm46]
70+
71+
@mock.patch('pyVmomi.vmodl.query.PropertyCollector')
72+
def test_simple_property_by_id(self, pc):
73+
with mock.patch.object(
74+
self._action.si_content.propertyCollector, 'RetrieveContents',
75+
return_value=self.raw):
76+
result = self._action.run(type='VirtualMachine',
77+
id=['vm-22', 'vm-46'],
78+
property=['does.not.exist'], raw=False)
79+
assert self._action.si_content.viewManager.CreateListView.called
80+
# status True because every object was found
81+
self.assertEqual(result, (True, self.transformed))
82+
with mock.patch.object(
83+
self._action.si_content.propertyCollector, 'RetrieveContents',
84+
return_value=self.raw):
85+
result = self._action.run(type='VirtualMachine',
86+
id=['vm-22', 'vm-46', 'vm-47'],
87+
property=None, raw=False)
88+
# status False because not every object was found
89+
self.assertEqual(result, (False, self.transformed))
90+
91+
@mock.patch('pyVmomi.vmodl.query.PropertyCollector')
92+
def test_simple_by_type(self, pc):
93+
with mock.patch.object(
94+
self._action.si_content.propertyCollector, 'RetrieveContents',
95+
return_value=self.raw):
96+
result = self._action.run(type='VirtualMachine', id=None,
97+
property=None, raw=False)
98+
assert\
99+
self._action.si_content.viewManager.CreateContainerView.called
100+
self.assertEqual(result, (True, self.transformed))
101+
102+
@mock.patch('pyVmomi.vmodl.query.PropertyCollector')
103+
def test_raw_output(self, pc):
104+
with mock.patch.object(
105+
self._action.si_content.propertyCollector, 'RetrieveContents',
106+
return_value=self.raw):
107+
result = self._action.run(type='VirtualMachine', id=None,
108+
property=None, raw=True)
109+
self.assertNotEqual(result, self.transformed)

0 commit comments

Comments
 (0)