Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

get_properties: a property collector action #37

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ PYVMOMI 6.0 requires alternative connection coding and Python 2.7.9 minimum due
## Actions

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

## Known Bugs
Expand Down
137 changes: 137 additions & 0 deletions actions/get_properties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from pyVmomi import vim, vmodl # pylint: disable-msg=E0611
from vmwarelib.actions import BaseAction

import copy
import datetime
import json
import pyVmomi


class GetProperties(BaseAction):

def run(self, type, property, id, raw, vsphere=None):
"""
Leverage the Property Collector to retrieve properties from any
Managed Object.

Args:
- type: vimType
- properties: optional array of properties to get (default: all)
- ids: optional array of MOIDs to limit results (default: all)
- vsphere: pre-configured connection information

Returns:
- dict: key = moid, value = dict of properties
"""

self.establish_connection(vsphere)
return self.collect(self.si_content, type, property, id, raw)

def collect(self, content, type, properties, ids, raw):
"""
Leverage the Property Collector to retrieve properties from any
Managed Object.

Args:
- content: service instance content
- type: object type
- properties: optional array of properties to get (default: all)
- ids: optional array of MOIDs to limit results (default: all)

Returns:
- dict: key = moid, value = dict of properties
"""

vimtype = getattr(vim, type)

rootFolder = content.rootFolder
viewMgr = content.viewManager
if not ids:
view = viewMgr.CreateContainerView(container=rootFolder,
type=[vimtype],
recursive=True)
else:
view = viewMgr.CreateListView()
for id in ids:
view.ModifyListView(add=[
pyVmomi.VmomiSupport.GetWsdlType('urn:vim25', type)(id)])

traversal_spec = vmodl.query.PropertyCollector.TraversalSpec()
traversal_spec.name = 'traverseEntities'
traversal_spec.path = 'view'
traversal_spec.skip = False
traversal_spec.type = view.__class__

obj_spec = vmodl.query.PropertyCollector.ObjectSpec()
obj_spec.obj = view
obj_spec.skip = True
obj_spec.selectSet = [traversal_spec]

property_spec = vmodl.query.PropertyCollector.PropertySpec()
property_spec.type = vimtype
if not properties:
property_spec.all = True
else:
property_spec.pathSet = properties

filter_spec = vmodl.query.PropertyCollector.FilterSpec()
filter_spec.objectSet = [obj_spec]
filter_spec.propSet = [property_spec]

rawdata = content.propertyCollector.RetrieveContents([filter_spec])
return self.transform(ids, rawdata) if not raw else rawdata

def jsonify_vsphere_obj(self, obj):
"""JSONify a vSphere Managed/Data object."""
class PyVmomiObjectJSONEncoder(json.JSONEncoder):
"""Custom JSON encoder to encode vSphere object."""
def __init__(self, *args, **kwargs):
super(PyVmomiObjectJSONEncoder, self).__init__(*args, **kwargs)

def default(self, obj): # pylint: disable=method-hidden
if isinstance(obj, datetime.datetime):
return pyVmomi.Iso8601.ISO8601Format(obj)
elif isinstance(obj, pyVmomi.VmomiSupport.DataObject):
# eliminate the very annoying Dynamic fields if empty
if (obj.__dict__['dynamicType'] is None and
len(obj.__dict__['dynamicProperty']) == 0):
tmp = copy.deepcopy(obj.__dict__)
tmp.pop('dynamicType')
tmp.pop('dynamicProperty')
return tmp
return obj.__dict__
elif isinstance(obj, pyVmomi.VmomiSupport.ManagedObject):
return unquote(obj).split(':')[-1]
elif isinstance(obj, type):
return str(obj)
return json.JSONEncoder.default(self, obj)
return json.loads(PyVmomiObjectJSONEncoder().encode(obj))

def transform(self, ids, rawdata):
result = {}
for obj in rawdata:
objid = unquote(obj.obj).split(':')[-1]
ps = {}
for prop in obj.propSet:
ps[unquote(prop.name)] = self.jsonify_vsphere_obj(prop.val)
result[objid] = ps
return (not ids or sorted(result.keys()) == sorted(ids), result)


def unquote(item):
return str(item).strip("'")
44 changes: 44 additions & 0 deletions actions/get_properties.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
name: get_properties
runner_type: python-script
description: Get properties of managed objects through the PropertyCollector.
enabled: true
entry_point: get_properties.py
parameters:
type:
type: string
description: The type of object to get. This type must be compatible with CreateContainerView.
required: true
position: 0
enum:
- ComputeResource
- Datacenter
- Datastore
- Folder
- HostSystem
- ManagedEntity
- Network
- ResourcePool
- VirtualMachine
id:
type: array
description: MOIDs to restrict the results to. Omit to retrieve all objects.
required: false
position: 1
property:
type: array
description: Fully qualified property within the type (dot separated), such as 'config.hardware.memoryMB' for a VirtualMachine. Omit to retrieve all properties.
required: false
position: 2
raw:
type: boolean
description: If True, return the raw result of the PropertyCollector.RetrieveContents call.
required: false
default: false
position: 3
vsphere:
type: string
description: Pre-Configured vsphere connection details
required: false
position: 4
default: ~
151 changes: 151 additions & 0 deletions tests/test_action_get_properties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and

import copy
import datetime
import mock
import pyVmomi

from get_properties import GetProperties
from pyVmomi import vim # pylint: disable-msg=E0611
from vsphere_base_action_test_case import VsphereBaseActionTestCase

__all__ = [
'GetPropertiesTestCase'
]


class GetPropertiesTestCase(VsphereBaseActionTestCase):
__test__ = True
action_cls = GetProperties

transformed = {
"vm-22": {
"config.hardware.memoryMB": 10240,
"dataobj-dyn": {
"arguments": "arguments",
'dynamicType': "Hello",
'dynamicProperty': [],
"envVariables": ["A=B", "C=D"],
"programPath": "cmd.exe",
"workingDirectory": "/tmp"
},
"guest.ipAddress": "10.99.0.4",
"name": "VCSA",
"network": ["network-15"],
"time": "1992-01-12T01:01:22Z"
},
"vm-46": {
"config.hardware.memoryMB": 2048,
"dataobj-nodyn": {
"arguments": "arguments",
"envVariables": ["A=B", "C=D"],
"programPath": "cmd.exe",
"workingDirectory": "/tmp"
},
"guest.ipAddress": "fe80::250:56ff:feb4:cfb9",
"name": "testvm",
"network": [],
"time": "1992-01-17T01:01:46Z"
}
}

def setUp(self):
def mockDynamicProperty(name, val):
result = mock.Mock()
result.name = name
result.val = val
return result
super(GetPropertiesTestCase, self).setUp()
self._action = self.get_action_instance(self.new_config)
self._action.establish_connection = mock.Mock()
self._action.si_content = mock.Mock()
self._action.si_content.rootFolder = mock.Mock()
self._action.si_content.viewManager = mock.Mock()
self._action.si_content.viewManager.CreateContainerView = mock.Mock()
self._action.si_content.viewManager.CreateListView = mock.Mock()
self._action.si_content.propertyCollector = mock.Mock()
cmdspec22 = vim.vm.guest.ProcessManager.ProgramSpec(
arguments="arguments",
envVariables=["A=B", "C=D"],
programPath="cmd.exe",
workingDirectory='/tmp')
cmdspec46 = copy.deepcopy(cmdspec22)
cmdspec22.__dict__['dynamicType'] = 'Hello'
# mock up the raw objects used to test transform
vm22 = mock.Mock()
vm22.obj = 'vim.VirtualMachine:vm-22'
vm22.propSet = [mockDynamicProperty("config.hardware.memoryMB", 10240),
mockDynamicProperty("dataobj-dyn", cmdspec22),
mockDynamicProperty("guest.ipAddress", "10.99.0.4"),
mockDynamicProperty("name", "VCSA"),
mockDynamicProperty("network", [
pyVmomi.VmomiSupport.GetWsdlType(
'urn:vim25', 'Network')(
'network-15')]),
mockDynamicProperty("time",
datetime.datetime(
1992, 1, 12, 1, 1, 22))]
vm46 = mock.Mock()
vm46.obj = 'vim.VirtualMachine:vm-46'
vm46.propSet = [mockDynamicProperty("config.hardware.memoryMB", 2048),
mockDynamicProperty("dataobj-nodyn", cmdspec46),
mockDynamicProperty("guest.ipAddress",
"fe80::250:56ff:feb4:cfb9"),
mockDynamicProperty("name", "testvm"),
mockDynamicProperty("network", []),
mockDynamicProperty("time",
datetime.datetime(
1992, 1, 17, 1, 1, 46))]
self.raw = [vm22, vm46]

@mock.patch('pyVmomi.vmodl.query.PropertyCollector')
def test_simple_property_by_id(self, pc):
with mock.patch.object(
self._action.si_content.propertyCollector, 'RetrieveContents',
return_value=self.raw):
result = self._action.run(type='VirtualMachine',
id=['vm-22', 'vm-46'],
property=['does.not.exist'], raw=False)
assert self._action.si_content.viewManager.CreateListView.called
# status True because every object was found
self.assertEqual(result, (True, self.transformed))
with mock.patch.object(
self._action.si_content.propertyCollector, 'RetrieveContents',
return_value=self.raw):
result = self._action.run(type='VirtualMachine',
id=['vm-22', 'vm-46', 'vm-47'],
property=None, raw=False)
# status False because not every object was found
self.assertEqual(result, (False, self.transformed))

@mock.patch('pyVmomi.vmodl.query.PropertyCollector')
def test_simple_by_type(self, pc):
with mock.patch.object(
self._action.si_content.propertyCollector, 'RetrieveContents',
return_value=self.raw):
result = self._action.run(type='VirtualMachine', id=None,
property=None, raw=False)
assert\
self._action.si_content.viewManager.CreateContainerView.called
self.assertEqual(result, (True, self.transformed))

@mock.patch('pyVmomi.vmodl.query.PropertyCollector')
def test_raw_output(self, pc):
with mock.patch.object(
self._action.si_content.propertyCollector, 'RetrieveContents',
return_value=self.raw):
result = self._action.run(type='VirtualMachine', id=None,
property=None, raw=True)
self.assertNotEqual(result, self.transformed)