diff --git a/lib/ansible/modules/cloud/vmware/vmware_vcenter_statistics.py b/lib/ansible/modules/cloud/vmware/vmware_vcenter_statistics.py new file mode 100644 index 00000000000..c59aa2d6e9f --- /dev/null +++ b/lib/ansible/modules/cloud/vmware/vmware_vcenter_statistics.py @@ -0,0 +1,426 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2018, Christian Kotte +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: vmware_vcenter_statistics +short_description: Configures statistics on a vCenter server +description: +- This module can be used to configure the vCenter server statistics. +- The remaining settings can be configured with the module C(vmware_vcenter_settings). +version_added: 2.8 +author: +- Christian Kotte (@ckotte) +notes: +- Tested with vCenter Server Appliance (vCSA) 6.5 and 6.7 +requirements: +- python >= 2.6 +- PyVmomi +options: + interval_past_day: + description: + - Settings for vCenter server past day statistic collection. + - 'Valid attributes are:' + - '- C(enabled) (bool): Past day statistics collection enabled. (default: True)' + - '- C(interval_minutes) (int): Interval duration (minutes). (choices: [1, 2, 3, 4, 5]) (default: 5)' + - '- C(save_for_days) (int): Save for (days). (choices: [1, 2, 3, 4, 5]) (default: 1)' + - '- C(level) (int): Statistics level. (choices: [1, 2, 3, 4]) (default: 1)' + type: dict + interval_past_week: + description: + - Settings for vCenter server past week statistic collection. + - 'Valid attributes are:' + - '- C(enabled) (bool): Past week statistics collection enabled. (default: True)' + - '- C(interval_minutes) (int): Interval duration (minutes). (choices: [30]) (default: 30)' + - '- C(save_for_weeks) (int): Save for (weeks). (choices: [1]) (default: 1)' + - '- C(level) (int): Statistics level. (choices: [1, 2, 3, 4]) (default: 1)' + type: dict + interval_past_month: + description: + - Settings for vCenter server past month statistic collection. + - 'Valid attributes are:' + - '- C(enabled) (bool): Past month statistics collection enabled. (default: True)' + - '- C(interval_hours) (int): Interval duration (hours). (choices: [2]) (default: 2)' + - '- C(save_for_months) (int): Save for (months). (choices: [1]) (default: 1)' + - '- C(level) (int): Statistics level. (choices: [1, 2, 3, 4]) (default: 1)' + type: dict + interval_past_year: + description: + - Settings for vCenter server past month statistic collection. + - 'Valid attributes are:' + - '- C(enabled) (bool): Past month statistics collection enabled. (default: True)' + - '- C(interval_days) (int): Interval duration (days). (choices: [1]) (default: 1)' + - '- C(save_for_years) (int): Save for (years). (choices: [1, 2, 3, 4, 5]) (default: 1)' + - '- C(level) (int): Statistics level. (choices: [1, 2, 3, 4]) (default: 1)' + type: dict +extends_documentation_fragment: vmware.documentation +''' + +EXAMPLES = r''' +- name: Configure vCenter statistics + vmware_vcenter_statistics: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + interval_past_day: + enabled: true + interval_minutes: 5 + save_for_days: 1 + level: 1 + interval_past_week: + enabled: true + level: 1 + interval_past_month: + enabled: true + level: 1 + interval_past_year: + enabled: true + save_for_years: 1 + level: 1 + validate_certs: no + delegate_to: localhost +''' + +RETURN = r''' +results: + description: metadata about vCenter statistics settings + returned: always + type: dict + sample: { + "changed": false, + "msg": "vCenter statistics already configured properly", + "past_day_enabled": true, + "past_day_interval": 5, + "past_day_level": 1, + "past_day_save_for": 1, + "past_month_enabled": true, + "past_month_interval": 2, + "past_month_level": 1, + "past_month_save_for": 1, + "past_week_enabled": true, + "past_week_interval": 30, + "past_week_level": 1, + "past_week_save_for": 1, + "past_year_enabled": true, + "past_year_interval": 1, + "past_year_level": 1, + "past_year_save_for": 1 + } +''' + +try: + from pyVmomi import vim, vmodl +except ImportError: + pass + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.vmware import PyVmomi, vmware_argument_spec +from ansible.module_utils._text import to_native + + +class VmwareVcenterStatistics(PyVmomi): + """Manage statistics for a vCenter server""" + + def __init__(self, module): + super(VmwareVcenterStatistics, self).__init__(module) + + if not self.is_vcenter(): + self.module.fail_json(msg="You have to connect to a vCenter server!") + + def ensure(self): + """Manage statistics for a vCenter server""" + result = dict(changed=False, msg='') + past_day_enabled = self.params['interval_past_day'].get('enabled', True) + past_day_seconds = self.params['interval_past_day'].get('interval_minutes', 5) * 60 + past_day_save_for_seconds = self.params['interval_past_day'].get('save_for_days', 1) * 86400 + past_day_level = self.params['interval_past_day'].get('level', 1) + past_week_enabled = self.params['interval_past_week'].get('enabled', True) + past_week_seconds = self.params['interval_past_week'].get('interval_minutes', 30) * 60 + past_week_save_for_seconds = self.params['interval_past_week'].get('save_for_weeks', 1) * 604800 + past_week_level = self.params['interval_past_week'].get('level', 1) + past_month_enabled = self.params['interval_past_month'].get('enabled', True) + past_month_seconds = self.params['interval_past_month'].get('interval_hours', 2) * 3600 + past_month_save_for_seconds = self.params['interval_past_month'].get('save_for_months', 1) * 2592000 + past_month_level = self.params['interval_past_month'].get('level', 1) + past_year_enabled = self.params['interval_past_year'].get('enabled', True) + past_year_seconds = self.params['interval_past_year'].get('interval_days', 1) * 86400 + past_year_save_for_seconds = self.params['interval_past_year'].get('save_for_years', 1) * 31536000 + past_year_level = self.params['interval_past_year'].get('level', 1) + + # Check if level options are valid + if past_year_level > past_month_level: + self.module.fail_json(msg="The statistics level for past year can't be higher than past month!") + if past_month_level > past_week_level: + self.module.fail_json(msg="The statistics level for past month can't be higher than past week!") + if past_week_level > past_day_level: + self.module.fail_json(msg="The statistics level for past week can't be higher than past day!") + + # Check if state options are valid + if not past_day_enabled and (past_week_enabled or past_month_enabled or past_year_enabled): + self.module.fail_json(msg="The intervals past week, month, and year need to be disabled as well!") + if not past_week_enabled and (past_month_enabled or past_year_enabled): + self.module.fail_json(msg="The intervals past month, and year need to be disabled as well!") + if not past_month_enabled and past_year_enabled: + self.module.fail_json(msg="The interval past year need to be disabled as well!") + if past_year_enabled and (not past_day_enabled or not past_week_enabled or not past_month_enabled): + self.module.fail_json(msg="The intervals past day, week, and month need to be enabled as well!") + if past_month_enabled and (not past_day_enabled or not past_week_enabled): + self.module.fail_json(msg="The intervals past day, and week need to be enabled as well!") + if past_week_enabled and (not past_day_enabled): + self.module.fail_json(msg="The intervals past day need to be enabled as well!") + + changed = False + changed_list = [] + + # Check statistics + result['past_day_enabled'] = past_day_enabled + result['past_day_interval'] = int(past_day_seconds / 60) + result['past_day_save_for'] = int(past_day_save_for_seconds / 86400) + result['past_day_level'] = past_day_level + result['past_week_enabled'] = past_week_enabled + result['past_week_interval'] = int(past_week_seconds / 60) + result['past_week_save_for'] = int(past_week_save_for_seconds / 604800) + result['past_week_level'] = past_week_level + result['past_month_enabled'] = past_month_enabled + result['past_month_interval'] = int(past_month_seconds / 3600) + result['past_month_save_for'] = int(past_month_save_for_seconds / 2592000) + result['past_month_level'] = past_month_level + result['past_year_enabled'] = past_year_enabled + result['past_year_interval'] = int(past_year_seconds / 86400) + result['past_year_save_for'] = int(past_year_save_for_seconds / 31536000) + result['past_year_level'] = past_year_level + change_statistics_list = [] + # We need to loop in a different order if we increase or decrease the statistic levels + increase_level = decrease_level = False + perf_manager = self.content.perfManager + for historical_interval in perf_manager.historicalInterval: + # Statistics for past day + if historical_interval.name == 'Past day' and ( + historical_interval.samplingPeriod != past_day_seconds or + historical_interval.length != past_day_save_for_seconds or + historical_interval.level != past_day_level or + historical_interval.enabled != past_day_enabled + ): + changed = True + changed_list.append("Past day interval") + if historical_interval.enabled != past_day_enabled: + result['past_day_enabled_previous'] = historical_interval.enabled + if historical_interval.samplingPeriod != past_day_seconds: + result['past_day_interval_previous'] = int(historical_interval.samplingPeriod / 60) + if historical_interval.length != past_day_save_for_seconds: + result['past_day_save_for_previous'] = int(historical_interval.length / 86400) + if historical_interval.level != past_day_level: + result['past_day_level_previous'] = historical_interval.level + if historical_interval.level < past_day_level: + increase_level = True + elif historical_interval.level > past_day_level: + decrease_level = True + change_statistics_list.append( + vim.HistoricalInterval( + key=1, + samplingPeriod=past_day_seconds, + name='Past day', + length=past_day_save_for_seconds, + level=past_day_level, + enabled=past_day_enabled + ) + ) + # Statistics for past week + if historical_interval.name == 'Past week' and ( + historical_interval.samplingPeriod != past_week_seconds or + historical_interval.length != past_week_save_for_seconds or + historical_interval.level != past_week_level or + historical_interval.enabled != past_week_enabled + ): + changed = True + changed_list.append("Past week interval") + if historical_interval.enabled != past_week_enabled: + result['past_week_enabled_previous'] = historical_interval.enabled + if historical_interval.samplingPeriod != past_week_seconds: + result['past_week_interval_previous'] = int(historical_interval.samplingPeriod / 60) + if historical_interval.length != past_week_save_for_seconds: + result['past_week_save_for_previous'] = int(historical_interval.length / 604800) + if historical_interval.level != past_week_level: + result['past_week_level_previous'] = historical_interval.level + if historical_interval.level < past_week_level: + increase_level = True + elif historical_interval.level > past_week_level: + decrease_level = True + change_statistics_list.append( + vim.HistoricalInterval( + key=2, + samplingPeriod=past_week_seconds, + name='Past week', + length=past_week_save_for_seconds, + level=past_week_level, + enabled=past_week_enabled + ) + ) + # Statistics for past month + if historical_interval.name == 'Past month' and ( + historical_interval.samplingPeriod != past_month_seconds or + historical_interval.length != past_month_save_for_seconds or + historical_interval.level != past_month_level or + historical_interval.enabled != past_month_enabled + ): + changed = True + changed_list.append("Past month interval") + if historical_interval.enabled != past_month_enabled: + result['past_month_enabled_previous'] = historical_interval.enabled + if historical_interval.samplingPeriod != past_month_seconds: + result['past_month_interval_previous'] = int(historical_interval.samplingPeriod / 3600) + if historical_interval.length != past_month_save_for_seconds: + result['past_month_save_for_previous'] = int(historical_interval.length / 2592000) + if historical_interval.level != past_month_level: + result['past_month_level_previous'] = historical_interval.level + if historical_interval.level < past_month_level: + increase_level = True + elif historical_interval.level > past_month_level: + decrease_level = True + change_statistics_list.append( + vim.HistoricalInterval( + key=3, + samplingPeriod=past_month_seconds, + name='Past month', + length=past_month_save_for_seconds, + level=past_month_level, + enabled=past_month_enabled + ) + ) + # Statistics for past year + if historical_interval.name == 'Past year' and ( + historical_interval.samplingPeriod != past_year_seconds or + historical_interval.length != past_year_save_for_seconds or + historical_interval.level != past_year_level or + historical_interval.enabled != past_year_enabled + ): + changed = True + changed_list.append("Past year interval") + if historical_interval.enabled != past_year_enabled: + result['past_year_enabled_previous'] = historical_interval.enabled + if historical_interval.samplingPeriod != past_year_seconds: + result['past_year_interval_previous'] = int(historical_interval.samplingPeriod / 86400) + if historical_interval.length != past_year_save_for_seconds: + result['past_year_save_for_previous'] = int(historical_interval.length / 31536000) + if historical_interval.level != past_year_level: + result['past_year_level_previous'] = historical_interval.level + if historical_interval.level < past_year_level: + increase_level = True + elif historical_interval.level > past_year_level: + decrease_level = True + change_statistics_list.append( + vim.HistoricalInterval( + key=4, + samplingPeriod=past_year_seconds, + name='Past year', + length=past_year_save_for_seconds, + level=past_year_level, + enabled=past_year_enabled + ) + ) + message = "vCenter statistics already configured properly" + if changed: + if self.module.check_mode: + changed_suffix = ' would be changed' + else: + changed_suffix = ' changed' + if len(changed_list) > 2: + message = ', '.join(changed_list[:-1]) + ', and ' + str(changed_list[-1]) + elif len(changed_list) == 2: + message = ' and '.join(changed_list) + elif len(changed_list) == 1: + message = changed_list[0] + message += changed_suffix + if not self.module.check_mode: + if increase_level: + # Loop in forward order (start with past day interval) + for statistic in change_statistics_list: + self.update_perf_interval(perf_manager, statistic) + if decrease_level: + # Loop in reverse order (start with past year interval) + for statistic in change_statistics_list[::-1]: + self.update_perf_interval(perf_manager, statistic) + + result['changed'] = changed + result['msg'] = message + + self.module.exit_json(**result) + + def update_perf_interval(self, perf_manager, statistic): + """Update statistics interval""" + try: + perf_manager.UpdatePerfInterval(statistic) + except vmodl.fault.InvalidArgument as invalid_argument: + self.module.fail_json( + msg="The set of arguments passed to the function is not specified correctly or " + "the update does not conform to the rules: %s" % to_native(invalid_argument.msg) + ) + + +def main(): + """Main""" + argument_spec = vmware_argument_spec() + argument_spec.update( + interval_past_day=dict( + type='dict', + options=dict( + enabled=dict(type='bool', default=True), + interval_minutes=dict(type='int', choices=[1, 2, 3, 4, 5], default=5), + save_for_days=dict(type='int', choices=[1, 2, 3, 4, 5], default=1), + level=dict(type='int', choices=[1, 2, 3, 4], default=1), + ), + ), + interval_past_week=dict( + type='dict', + options=dict( + enabled=dict(type='bool', default=True), + interval_minutes=dict(type='int', choices=[30], default=30), + save_for_weeks=dict(type='int', choices=[1], default=1), + level=dict(type='int', choices=[1, 2, 3, 4], default=1), + ), + ), + interval_past_month=dict( + type='dict', + options=dict( + enabled=dict(type='bool', default=True), + interval_hours=dict(type='int', choices=[2], default=2), + save_for_months=dict(type='int', choices=[1], default=1), + level=dict(type='int', choices=[1, 2, 3, 4], default=1), + ), + ), + interval_past_year=dict( + type='dict', + options=dict( + enabled=dict(type='bool', default=True), + interval_days=dict(type='int', choices=[1], default=1), + save_for_years=dict(type='int', choices=[1, 2, 3, 4, 5], default=1), + level=dict(type='int', choices=[1, 2, 3, 4], default=1), + ), + ), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + + host_snmp = VmwareVcenterStatistics(module) + host_snmp.ensure() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/vmware_vcenter_statistics/aliases b/test/integration/targets/vmware_vcenter_statistics/aliases new file mode 100644 index 00000000000..32dd9d53456 --- /dev/null +++ b/test/integration/targets/vmware_vcenter_statistics/aliases @@ -0,0 +1,2 @@ +shippable/vcenter/group1 +cloud/vcenter diff --git a/test/integration/targets/vmware_vcenter_statistics/tasks/main.yml b/test/integration/targets/vmware_vcenter_statistics/tasks/main.yml new file mode 100644 index 00000000000..c6961b27e79 --- /dev/null +++ b/test/integration/targets/vmware_vcenter_statistics/tasks/main.yml @@ -0,0 +1,115 @@ +# Test code for the vmware_vcenter_statistics module. +# Copyright: (c) 2018, Christian Kotte +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: store the vcenter container ip + set_fact: + vcsim: "{{ lookup('env', 'vcenter_host') }}" + +- debug: var=vcsim + +- name: Wait for Flask controller to come up online + wait_for: + host: "{{ vcsim }}" + port: 5000 + state: started + +- name: kill vcsim + uri: + url: http://{{ vcsim }}:5000/killall + +- name: start vcsim + uri: + url: http://{{ vcsim }}:5000/spawn?cluster=2 + register: vcsim_instance + +- debug: + var: vcsim_instance + +- name: Wait for vcsim server to come up online + wait_for: + host: "{{ vcsim }}" + port: 443 + state: started + +- name: get datacenter + uri: + url: http://{{ vcsim }}:5000/govc_find?filter=DC + register: datacenters + +- name: get a datacenter + set_fact: + dc1: "{{ datacenters.json[0] | basename }}" + +- name: Configure statistics in check mode + vmware_vcenter_statistics: + hostname: "{{ vcsim }}" + username: "{{ vcsim_instance.json.username }}" + password: "{{ vcsim_instance.json.password }}" + interval_past_day: + enabled: true + interval_minutes: 5 + save_for_days: 3 + level: 2 + interval_past_week: + enabled: true + interval_minutes: 30 + save_for_weeks: 1 + level: 2 + interval_past_month: + enabled: true + interval_hours: 2 + save_for_months: 1 + level: 1 + interval_past_year: + enabled: true + interval_days: 1 + save_for_years: 1 + level: 1 + validate_certs: no + register: statistics_results_check_mode + check_mode: yes + +- debug: msg="{{ statistics_results_check_mode }}" + +- name: ensure statistics were configured + assert: + that: + # Doesn't really work with vcsim. No matter which settings are used; they are always shown as already configured!? + - statistics_results_check_mode.changed == False + +- name: Configure statistics + vmware_vcenter_statistics: + hostname: "{{ vcsim }}" + username: "{{ vcsim_instance.json.username }}" + password: "{{ vcsim_instance.json.password }}" + interval_past_day: + enabled: true + interval_minutes: 5 + save_for_days: 3 + level: 2 + interval_past_week: + enabled: true + interval_minutes: 30 + save_for_weeks: 1 + level: 2 + interval_past_month: + enabled: true + interval_hours: 2 + save_for_months: 1 + level: 1 + interval_past_year: + enabled: true + interval_days: 1 + save_for_years: 1 + level: 1 + validate_certs: no + register: statistics_results + +- debug: msg="{{ statistics_results }}" + +- name: ensure statistics were configured + assert: + that: + # Doesn't really work with vcsim. No matter which settings are used; they are always shown as already configured!? + - statistics_results.changed == False