Skip to content
Open
103 changes: 49 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ default:
vsphere_user: "user"
vsphere_password: "password"
ignore_ssl: False
timeout: 120
specs_size: 5000
fetch_custom_attributes: True
fetch_tags: True
Expand All @@ -78,11 +79,12 @@ default:
hosts: True
snapshots: True

esx:
vcenter01:
vsphere_host: vc.example2.com
vsphere_user: 'root'
vsphere_password: 'password'
ignore_ssl: True
timeout: 120
specs_size: 5000
fetch_custom_attributes: True
fetch_tags: True
Expand All @@ -94,11 +96,12 @@ esx:
hosts: True
snapshots: True

limited:
vcenter02:
vsphere_host: slowvc.example.com
vsphere_user: '[email protected]'
vsphere_password: 'password'
ignore_ssl: True
timeout: 120
specs_size: 5000
fetch_custom_attributes: True
fetch_tags: True
Expand All @@ -114,39 +117,41 @@ limited:
Switching sections can be done by adding ?section=limited to the URL.

#### Environment Variables
| Variable | Precedence | Defaults | Description |
| --------------------------------------| ---------------------- | -------- | --------------------------------------------------------------------------|
| `VSPHERE_HOST` | config, env, get_param | n/a | vsphere server to connect to |
| `VSPHERE_USER` | config, env | n/a | User for connecting to vsphere |
| `VSPHERE_PASSWORD` | config, env | n/a | Password for connecting to vsphere |
| `VSPHERE_SPECS_SIZE` | config, env | 5000 | Size of specs list for query stats function |
| `VSPHERE_IGNORE_SSL` | config, env | False | Ignore the ssl cert on the connection to vsphere host |
| `VSPHERE_FETCH_CUSTOM_ATTRIBUTES` | config, env | False | Set to true to collect objects custom attributes as metric labels |
| `VSPHERE_FETCH_TAGS` | config, env | False | Set to true to collect objects tags as metric labels |
| `VSPHERE_FETCH_ALARMS` | config, env | False | Fetch objects triggered alarms, and in case of hosts hdw alarms as well |
| `VSPHERE_COLLECT_HOSTS` | config, env | True | Set to false to disable collection of host metrics |
| `VSPHERE_COLLECT_DATASTORES` | config, env | True | Set to false to disable collection of datastore metrics |
| `VSPHERE_COLLECT_VMS` | config, env | True | Set to false to disable collection of virtual machine metrics |
| `VSPHERE_COLLECT_VMGUESTS` | config, env | True | Set to false to disable collection of virtual machine guest metrics |
| `VSPHERE_COLLECT_SNAPSHOTS` | config, env | True | Set to false to disable collection of snapshot metrics |
| Variable | Precedence | Defaults | Description |
| ---------------------------------------------| ------------| -------- | ---------------------------------------------------------------------------------|
| `VSPHERE_HOST` | config, env | n/a | vsphere server to connect to |
| `VSPHERE_USER` | config, env | n/a | User for connecting to vsphere |
| `VSPHERE_PASSWORD` | config, env | n/a | Password for connecting to vsphere |
| `VSPHERE_SPECS_SIZE` | config, env | 5000 | Size of specs list for query stats function |
| `VSPHERE_IGNORE_SSL` | config, env | False | Ignore the ssl cert on the connection to vsphere host |
| `VSPHERE_TIMEOUT` | config, env | 120 | Set how long to wait before failing to collect |
| `VSPHERE_FETCH_CUSTOM_ATTRIBUTES` | config, env | False | Set to true to collect objects custom attributes as metric labels |
| `VSPHERE_FETCH_TAGS` | config, env | False | Set to true to collect objects tags as metric labels |
| `VSPHERE_FETCH_ALARMS` | config, env | False | Fetch objects triggered alarms, and in case of hosts hdw alarms as well |
| `VSPHERE_COLLECT_HOSTS` | config, env | True | Set to false to disable collection of host metrics |
| `VSPHERE_COLLECT_DATASTORES` | config, env | True | Set to false to disable collection of datastore metrics |
| `VSPHERE_COLLECT_VMS` | config, env | True | Set to false to disable collection of virtual machine metrics |
| `VSPHERE_COLLECT_VMGUESTS` | config, env | True | Set to false to disable collection of virtual machine guest metrics |
| `VSPHERE_COLLECT_SNAPSHOTS` | config, env | True | Set to false to disable collection of snapshot metrics |

You can create new sections as well, with very similiar variables. For example, to create a `limited` section you can set:

| Variable | Precedence | Defaults | Description |
| ----------------------------------------------| ---------------------- | -------- | --------------------------------------------------------------------------|
| `VSPHERE_LIMITED_HOST` | config, env, get_param | n/a | vsphere server to connect to |
| `VSPHERE_LIMITED_USER` | config, env | n/a | User for connecting to vsphere |
| `VSPHERE_LIMITED_PASSWORD` | config, env | n/a | Password for connecting to vsphere |
| `VSPHERE_LIMITED_SPECS_SIZE` | config, env | 5000 | Size of specs list for query stats function |
| `VSPHERE_LIMITED_IGNORE_SSL` | config, env | False | Ignore the ssl cert on the connection to vsphere host |
| `VSPHERE_LIMITED_FETCH_CUSTOM_ATTRIBUTES` | config, env | False | Set to true to collect objects custom attributes as metric labels |
| `VSPHERE_LIMITED_FETCH_TAGS` | config, env | False | Set to true to collect objects tags as metric labels |
| `VSPHERE_LIMITED_FETCH_ALARMS` | config, env | False | Fetch objects triggered alarms, and in case of hosts hdw alarms as well |
| `VSPHERE_LIMITED_COLLECT_HOSTS` | config, env | True | Set to false to disable collection of host metrics |
| `VSPHERE_LIMITED_COLLECT_DATASTORES` | config, env | True | Set to false to disable collection of datastore metrics |
| `VSPHERE_LIMITED_COLLECT_VMS` | config, env | True | Set to false to disable collection of virtual machine metrics |
| `VSPHERE_LIMITED_COLLECT_VMGUESTS` | config, env | True | Set to false to disable collection of virtual machine guest metrics |
| `VSPHERE_LIMITED_COLLECT_SNAPSHOTS` | config, env | True | Set to false to disable collection of snapshot metrics |
| Variable | Precedence | Defaults | Description |
| ---------------------------------------------| ----------- | -------- | ---------------------------------------------------------------------------------|
| `VSPHERE_LIMITED_HOST` | config, env | n/a | vsphere server to connect to |
| `VSPHERE_LIMITED_USER` | config, env | n/a | User for connecting to vsphere |
| `VSPHERE_LIMITED_PASSWORD` | config, env | n/a | Password for connecting to vsphere |
| `VSPHERE_LIMITED_SPECS_SIZE` | config, env | 5000 | Size of specs list for query stats function |
| `VSPHERE_LIMITED_IGNORE_SSL` | config, env | False | Ignore the ssl cert on the connection to vsphere host |
| `VSPHERE_LIMITED_TIMEOUT` | config, env | 120 | Set how long to wait before failing to collect |
| `VSPHERE_LIMITED_FETCH_CUSTOM_ATTRIBUTES` | config, env | False | Set to true to collect objects custom attributes as metric labels |
| `VSPHERE_LIMITED_FETCH_TAGS` | config, env | False | Set to true to collect objects tags as metric labels |
| `VSPHERE_LIMITED_FETCH_ALARMS` | config, env | False | Fetch objects triggered alarms, and in case of hosts hdw alarms as well |
| `VSPHERE_LIMITED_COLLECT_HOSTS` | config, env | True | Set to false to disable collection of host metrics |
| `VSPHERE_LIMITED_COLLECT_DATASTORES` | config, env | True | Set to false to disable collection of datastore metrics |
| `VSPHERE_LIMITED_COLLECT_VMS` | config, env | True | Set to false to disable collection of virtual machine metrics |
| `VSPHERE_LIMITED_COLLECT_VMGUESTS` | config, env | True | Set to false to disable collection of virtual machine guest metrics |
| `VSPHERE_LIMITED_COLLECT_SNAPSHOTS` | config, env | True | Set to false to disable collection of snapshot metrics |

You need to set at least `VSPHERE_SECTIONNAME_USER` for the section to be detected.

Expand All @@ -155,18 +160,24 @@ You need to set at least `VSPHERE_SECTIONNAME_USER` for the section to be detect
You can use the following parameters in the Prometheus configuration file. The `params` section is used to manage multiple login/passwords.

```
# Example of Multiple vCenter usage per #23

- job_name: 'vmware_vcenter'
metrics_path: '/metrics'
static_configs:
- targets:
- 'vcenter.company.com'
- default
- vcenter01
- vcenter02
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: __param_section
- source_labels: [__param_section]
target_label: instance
- target_label: __address__
replacement: localhost:9272
replacement: exporter_ip:9272

# Example using file service discovery

- job_name: 'vmware_esx'
metrics_path: '/metrics'
Expand All @@ -177,28 +188,12 @@ You can use the following parameters in the Prometheus configuration file. The `
section: [esx]
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: __param_section
- source_labels: [__param_section]
target_label: instance
- target_label: __address__
replacement: localhost:9272

# Example of Multiple vCenter usage per #23

- job_name: vmware_export
metrics_path: /metrics
static_configs:
- targets:
- vcenter01
- vcenter02
- vcenter03
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: exporter_ip:9272
```

## Current Status
Expand Down
22 changes: 21 additions & 1 deletion tests/unit/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from pyVmomi import vim

from vmware_exporter.helpers import batch_fetch_properties, get_bool_env
from vmware_exporter.helpers import batch_fetch_properties, get_bool_env, serialize, deserialize


class FakeView(vim.ManagedObject):
Expand All @@ -16,6 +16,26 @@ def Destroy(self):
pass


def test_serialize():

# Test basic usage
assert serialize('abc', 'def', g='1', h='2') == 'abc:def:g=1:h=2'

# Test escaping
assert serialize('\\\n\r,:=') == '\\\\\\n\\r\\,\\:\\='


def test_deserialize():

tests = [
'abc:def:g=1:h=2',
'\\\\\\n\\r\\,\\:\\=:\\\\\\n\\r\\,\\:\\==\\\\\\n\\r\\,\\:\\='
]
for value in tests:
arg, kwarg = deserialize(value)
assert serialize(*arg, **kwarg) == value


def test_get_bool_env():
# Expected behaviour
assert get_bool_env('NON_EXISTENT_ENV', True)
Expand Down
2 changes: 1 addition & 1 deletion vmware_exporter/defer.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class BranchingDeferred(defer.Deferred):
'''

def __init__(self):
self.callbacks = []
super().__init__()
self.result = None

def callback(self, result):
Expand Down
131 changes: 93 additions & 38 deletions vmware_exporter/helpers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,65 @@
# autopep8'd
import os
from pyVmomi import vmodl
import re


def serialize(*arg, **kwarg):
"""
Serialize into the format
item1:item2:key1=value1:key2:value2
"""
escape_dict = {'\\': '\\\\', '\n': '\\n', '\r': '\\r', ',': '\\,', ':': '\\:', '=': '\\='}
pattern = re.compile("|".join([re.escape(k) for k in sorted(escape_dict, key=len, reverse=True)]), flags=re.DOTALL)
items = []
for item in arg:
items.append(pattern.sub(lambda x: escape_dict[x.group(0)], str(item)))
for k, v in kwarg.items():
items.append("{}={}".format(
pattern.sub(lambda x: escape_dict[x.group(0)], str(k)),
pattern.sub(lambda x: escape_dict[x.group(0)], str(v))
))
return ':'.join(items)


def deserialize(s):
"""
Deserialize the format into a list and dict
item1:item2:key1=value1:key2:value2
"""
escape_dict = {'\\r': '\r', '\\\\': '\\', '\\=': '=', '\\:': ':', '\\n': '\n', '\\,': ','}
pattern = re.compile("|".join([re.escape(k) for k in sorted(escape_dict, key=len, reverse=True)]), flags=re.DOTALL)
arg = []
kwarg = {}
for name, eq, value in re.findall(r'((?:\\.|[^=:\\]+)+)(?:(=)((?:\\.|[^:\\]+)+))?', s):
if eq:
kwarg[pattern.sub(lambda x: escape_dict[x.group(0)], name)] = pattern.sub(
lambda x: escape_dict[x.group(0)], value)
else:
arg.append(pattern.sub(lambda x: escape_dict[x.group(0)], name))
return (arg, kwarg)


class TriggeredAlarm(object):
def __init__(self, name, status):
self.name = name
self.sensorStatus = status

def __str__(self):
return serialize('triggeredAlarm', self.name, self.sensorStatus)


class NumericSensorInfo(object):
def __init__(self, name, status, type="n/a", value="n/a", unitModifier="n/a", unit="n/a"):
self.name = name
self.type = type
self.sensorStatus = status
self.value = value
self.unitModifier = unitModifier
self.unit = unit

def __str__(self):
return serialize('numericSensorInfo', name=self.name, type=self.type, sensorStatus=self.sensorStatus, value=self.value, unitModifier=self.unitModifier, unit=self.unit)


def get_bool_env(key: str, default: bool):
Expand All @@ -26,13 +85,9 @@ def batch_fetch_properties(content, obj_type, properties):

if content.customFieldsManager and content.customFieldsManager.field:
allCustomAttributesNames.update(
dict(
[
(f.key, f.name)
for f in content.customFieldsManager.field
if f.managedObjectType in (obj_type, None)
]
)
(f.key, f.name)
for f in content.customFieldsManager.field
if f.managedObjectType in (obj_type, None)
)

try:
Expand Down Expand Up @@ -95,32 +150,37 @@ def batch_fetch_properties(content, obj_type, properties):
"""
triggered alarms
"""

try:
alarms = list(
'triggeredAlarm:{}:{}'.format(item.alarm.info.systemName.split('.')[1], item.overallStatus)
for item in prop.val
)
properties[prop.name] = [
TriggeredAlarm(
name=item.alarm.info.systemName.split('.')[1],
status=item.overallStatus
) for item in prop.val
]
except Exception:
alarms = ['triggeredAlarm:AlarmsUnavailable:yellow']

properties[prop.name] = ','.join(alarms)
properties[prop.name] = [
TriggeredAlarm(
name='AlarmsUnavailable',
status='yellow'
)
]

elif 'runtime.healthSystemRuntime.systemHealthInfo.numericSensorInfo' == prop.name:
"""
handle numericSensorInfo
"""
sensors = list(
'numericSensorInfo:name={}:type={}:sensorStatus={}:value={}:unitModifier={}:unit={}'.format(
item.name,
item.sensorType,
item.healthState.key,
item.currentReading,
item.unitModifier,
item.baseUnits.lower()
)
for item in prop.val
)
properties[prop.name] = ','.join(sensors)

properties[prop.name] = [
NumericSensorInfo(
name=item.name,
type=item.sensorType,
status=item.healthState.key,
value=item.currentReading,
unitModifier=item.unitModifier,
unit=item.baseUnits.lower()
) for item in prop.val
]

elif prop.name in [
'runtime.healthSystemRuntime.hardwareStatusInfo.cpuStatusInfo',
Expand All @@ -129,18 +189,13 @@ def batch_fetch_properties(content, obj_type, properties):
"""
handle hardwareStatusInfo
"""
sensors = list(
'numericSensorInfo:name={}:type={}:sensorStatus={}:value={}:unitModifier={}:unit={}'.format(
item.name,
"n/a",
item.status.key,
"n/a",
"n/a",
"n/a",
)
for item in prop.val
)
properties[prop.name] = ','.join(sensors)

properties[prop.name] = [
NumericSensorInfo(
name=item.name,
status=item.status.key
) for item in prop.val
]

else:
properties[prop.name] = prop.val
Expand Down
Loading