Added VM instances used and quotas (#6)
The following plugins add a basic OpenStack API framework which is used to pull metrics for nova vm quotas and usage for RAM, vCPUs, disk, and instance count. added codecov.yml Signed-off-by: Kevin Carter <kevin.carter@rackspace.com>
This commit is contained in:
parent
f943e47f70
commit
37a9950ab2
1
AUTHORS
1
AUTHORS
|
@ -1,2 +1,3 @@
|
|||
Kevin Carter <kcarter@linux.com>
|
||||
Kevin Carter <kevin.carter@rackspace.com>
|
||||
Major Hayden <major@mhtx.net>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
CHANGES
|
||||
=======
|
||||
|
||||
* Improve test coverage
|
||||
* Added VM instances used and quotas
|
||||
* Improve test coverage (#4)
|
||||
* Remove sys from uptime test (#3)
|
||||
* Increase test coverage for uptime
|
||||
* Added KVM Metric plugin (#2)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
coverage:
|
||||
round: down
|
||||
range: 70..80
|
||||
precision: 5
|
|
@ -0,0 +1,47 @@
|
|||
# Store the authentication credentials needed to query a given OpenStack Service.
|
||||
# All sections are overrides for the defaults. If you only need to connect to a
|
||||
# single cloud simply store the credentials needd in the DEFAULT section and
|
||||
# override whatever is needed within the local sections.
|
||||
|
||||
[DEFAULT]
|
||||
insecure = false
|
||||
auth_url = https://example.com:5000/v3
|
||||
|
||||
# NOTE(cloudnull):
|
||||
# When using keystone V3 you will need the .*domain_name configuration options.
|
||||
user_domain_name = default # This is required when Keystone V3 is being used
|
||||
project_domain_name = default # This is required when Keystone V3 is being used
|
||||
|
||||
# If you're using keystone V2 you will need the tenant_name option.
|
||||
tenant_name = admin # This is required when Keystone V2 is being used
|
||||
|
||||
# NEVER Mix and match the options tenant name and domain_name options.
|
||||
# You are be required to run either V2 or V3 as it pertains to this config.
|
||||
# If you provide both tenant_name and .*domain_name options at the same time
|
||||
# the plugins will fail API version negotiation.
|
||||
|
||||
username = admin
|
||||
password = Secrete
|
||||
# The verify option is for SSL. If your SSL certificate is not
|
||||
# valid set this option to false else omit it or set it true.
|
||||
verify = false
|
||||
|
||||
[keystone]
|
||||
|
||||
[glance]
|
||||
|
||||
[nova]
|
||||
project_name = nova
|
||||
|
||||
[neutron]
|
||||
|
||||
[heat]
|
||||
|
||||
[cinder]
|
||||
|
||||
[ironic]
|
||||
auth_url = https://example2.com:5000/v3
|
||||
project_name = ironic
|
||||
user_domain_name = users
|
||||
project_domain_name = projects
|
||||
password = SuperSecrete
|
|
@ -51,6 +51,7 @@ class MonitorStackCLI(click.MultiCommand):
|
|||
@property
|
||||
def cmd_folder(self):
|
||||
"""Get the path to the plugin directory."""
|
||||
|
||||
return os.path.abspath(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
|
@ -60,6 +61,7 @@ class MonitorStackCLI(click.MultiCommand):
|
|||
|
||||
def list_commands(self, ctx):
|
||||
"""Get a list of all available commands."""
|
||||
|
||||
rv = list()
|
||||
for _, pkg_name, _ in pkgutil.iter_modules([self.cmd_folder]):
|
||||
rv.append(pkg_name)
|
||||
|
@ -68,6 +70,7 @@ class MonitorStackCLI(click.MultiCommand):
|
|||
|
||||
def get_command(self, ctx, name):
|
||||
"""Load a command and run it."""
|
||||
|
||||
for _, pkg_name, _ in pkgutil.iter_modules([self.cmd_folder]):
|
||||
if pkg_name == name:
|
||||
mod = importlib.import_module(
|
||||
|
@ -99,6 +102,7 @@ VALID_OUTPUT_FORMATS = [
|
|||
@pass_context
|
||||
def cli(ctx, output_format, verbose):
|
||||
"""A complex command line interface."""
|
||||
|
||||
ctx.verbose = verbose
|
||||
pass
|
||||
|
||||
|
@ -106,6 +110,7 @@ def cli(ctx, output_format, verbose):
|
|||
@cli.resultcallback(replace=True)
|
||||
def process_result(result, output_format, verbose):
|
||||
"""Render the output into the proper format."""
|
||||
|
||||
module_name = 'monitorstack.common.formatters'
|
||||
method_name = 'write_{}'.format(output_format)
|
||||
output_formatter = getattr(
|
||||
|
|
|
@ -18,13 +18,9 @@ import time
|
|||
import click
|
||||
|
||||
|
||||
def current_time():
|
||||
"""Return the current time in nanoseconds."""
|
||||
return int(time.time() * 1000000000)
|
||||
|
||||
|
||||
def write_json(result):
|
||||
"""Output in raw JSON format."""
|
||||
|
||||
output = json.dumps(result, indent=2)
|
||||
click.echo(output)
|
||||
return True
|
||||
|
@ -32,36 +28,52 @@ def write_json(result):
|
|||
|
||||
def write_line(result):
|
||||
"""Output in line format."""
|
||||
|
||||
for key, value in result['variables'].items():
|
||||
click.echo("{} {}".format(key, value))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _current_time():
|
||||
"""Return the current time in nanoseconds."""
|
||||
|
||||
return int(time.time() * 1000000000)
|
||||
|
||||
|
||||
def _telegraf_line_format(sets, quote=False):
|
||||
"""Return a comma separated string."""
|
||||
|
||||
store = list()
|
||||
for k, v in sets.items():
|
||||
k = k.replace(' ', '_')
|
||||
for v_type in [int, float]:
|
||||
try:
|
||||
v = v_type(v)
|
||||
except ValueError:
|
||||
pass # v was not a int, float, or long
|
||||
else:
|
||||
break
|
||||
if not isinstance(v, (int, float, bool)) and quote:
|
||||
store.append('{}="{}"'.format(k, v))
|
||||
else:
|
||||
store.append('{}={}'.format(k, v))
|
||||
return ','.join(store).rstrip(',')
|
||||
|
||||
|
||||
def write_telegraf(result):
|
||||
"""Output in telegraf format."""
|
||||
def line_format(sets, quote=False):
|
||||
store = list()
|
||||
for k, v in sets.items():
|
||||
k = k.replace(' ', '_')
|
||||
for v_type in [int, float]:
|
||||
try:
|
||||
v = v_type(v)
|
||||
except ValueError:
|
||||
pass # v was not a int, float, or long
|
||||
else:
|
||||
break
|
||||
if not isinstance(v, (int, float, bool)) and quote:
|
||||
store.append('{}="{}"'.format(k, v))
|
||||
else:
|
||||
store.append('{}={}'.format(k, v))
|
||||
return ','.join(store).rstrip(',')
|
||||
|
||||
resultant = [result['measurement_name']]
|
||||
if 'meta' in result:
|
||||
resultant.append(line_format(sets=result['meta']))
|
||||
resultant.append(line_format(sets=result['variables'], quote=True))
|
||||
resultant.append(str(current_time()))
|
||||
resultant.append(_telegraf_line_format(sets=result['meta']))
|
||||
resultant.append(
|
||||
_telegraf_line_format(
|
||||
sets=result['variables'],
|
||||
quote=True
|
||||
)
|
||||
)
|
||||
resultant.append(str(_current_time()))
|
||||
click.echo(' '.join(resultant))
|
||||
|
||||
return True
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
import click
|
||||
|
||||
from monitorstack import utils
|
||||
from monitorstack.cli import pass_context
|
||||
from monitorstack.utils import os_utils as ost
|
||||
|
||||
|
||||
DOC = """Get nova cores quotas."""
|
||||
COMMAND_NAME = 'os_vm_quota_cores'
|
||||
|
||||
|
||||
@click.command(COMMAND_NAME, short_help=DOC)
|
||||
@click.option('--config-file',
|
||||
help='OpenStack configuration file',
|
||||
default='openstack.ini')
|
||||
@pass_context
|
||||
def cli(ctx, config_file):
|
||||
setattr(cli, '__doc__', DOC)
|
||||
|
||||
output = {
|
||||
'measurement_name': COMMAND_NAME,
|
||||
'meta': {
|
||||
'quotas': 'cores'
|
||||
},
|
||||
'variables': {}
|
||||
}
|
||||
nova_config = utils.read_config(config_file=config_file)['nova']
|
||||
interface = nova_config.pop('interface', 'internal')
|
||||
_ost = ost.OpenStack(os_auth_args=nova_config)
|
||||
try:
|
||||
variables = output['variables']
|
||||
for project in _ost.get_projects():
|
||||
limits = _ost.get_compute_limits(
|
||||
project_id=project.id,
|
||||
interface=interface
|
||||
)
|
||||
variables[project.name] = int(limits['quota_set']['cores'])
|
||||
except Exception as exp:
|
||||
output['exit_code'] = 1
|
||||
output['message'] = '{} failed -- Error: {}'.format(COMMAND_NAME, exp)
|
||||
else:
|
||||
output['exit_code'] = 0
|
||||
output['message'] = '{} is ok'.format(COMMAND_NAME)
|
||||
finally:
|
||||
return output
|
|
@ -0,0 +1,59 @@
|
|||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
import click
|
||||
|
||||
from monitorstack import utils
|
||||
from monitorstack.cli import pass_context
|
||||
from monitorstack.utils import os_utils as ost
|
||||
|
||||
|
||||
DOC = """Get nova instance quotas."""
|
||||
COMMAND_NAME = 'os_vm_quota_instance'
|
||||
|
||||
|
||||
@click.command(COMMAND_NAME, short_help=DOC)
|
||||
@click.option('--config-file',
|
||||
help='OpenStack configuration file',
|
||||
default='openstack.ini')
|
||||
@pass_context
|
||||
def cli(ctx, config_file):
|
||||
setattr(cli, '__doc__', DOC)
|
||||
|
||||
output = {
|
||||
'measurement_name': COMMAND_NAME,
|
||||
'meta': {
|
||||
'quotas': 'instances'
|
||||
},
|
||||
'variables': {}
|
||||
}
|
||||
nova_config = utils.read_config(config_file=config_file)['nova']
|
||||
interface = nova_config.pop('interface', 'internal')
|
||||
_ost = ost.OpenStack(os_auth_args=nova_config)
|
||||
try:
|
||||
variables = output['variables']
|
||||
for project in _ost.get_projects():
|
||||
limits = _ost.get_compute_limits(
|
||||
project_id=project.id,
|
||||
interface=interface
|
||||
)
|
||||
variables[project.name] = int(limits['quota_set']['instances'])
|
||||
except Exception as exp:
|
||||
output['exit_code'] = 1
|
||||
output['message'] = '{} failed -- Error: {}'.format(COMMAND_NAME, exp)
|
||||
else:
|
||||
output['exit_code'] = 0
|
||||
output['message'] = '{} is ok'.format(COMMAND_NAME)
|
||||
finally:
|
||||
return output
|
|
@ -0,0 +1,59 @@
|
|||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
import click
|
||||
|
||||
from monitorstack import utils
|
||||
from monitorstack.cli import pass_context
|
||||
from monitorstack.utils import os_utils as ost
|
||||
|
||||
|
||||
DOC = """Get nova ram quotas."""
|
||||
COMMAND_NAME = 'os_vm_quota_ram'
|
||||
|
||||
|
||||
@click.command(COMMAND_NAME, short_help=DOC)
|
||||
@click.option('--config-file',
|
||||
help='OpenStack configuration file',
|
||||
default='openstack.ini')
|
||||
@pass_context
|
||||
def cli(ctx, config_file):
|
||||
setattr(cli, '__doc__', DOC)
|
||||
|
||||
output = {
|
||||
'measurement_name': COMMAND_NAME,
|
||||
'meta': {
|
||||
'quotas': 'ram'
|
||||
},
|
||||
'variables': {}
|
||||
}
|
||||
nova_config = utils.read_config(config_file=config_file)['nova']
|
||||
interface = nova_config.pop('interface', 'internal')
|
||||
_ost = ost.OpenStack(os_auth_args=nova_config)
|
||||
try:
|
||||
variables = output['variables']
|
||||
for project in _ost.get_projects():
|
||||
limits = _ost.get_compute_limits(
|
||||
project_id=project.id,
|
||||
interface=interface
|
||||
)
|
||||
variables[project.name] = int(limits['quota_set']['ram'])
|
||||
except Exception as exp:
|
||||
output['exit_code'] = 1
|
||||
output['message'] = '{} failed -- Error: {}'.format(COMMAND_NAME, exp)
|
||||
else:
|
||||
output['exit_code'] = 0
|
||||
output['message'] = '{} is ok'.format(COMMAND_NAME)
|
||||
finally:
|
||||
return output
|
|
@ -0,0 +1,63 @@
|
|||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
import collections
|
||||
|
||||
import click
|
||||
|
||||
from monitorstack import utils
|
||||
from monitorstack.cli import pass_context
|
||||
from monitorstack.utils import os_utils as ost
|
||||
|
||||
|
||||
DOC = """Get nova used cores."""
|
||||
COMMAND_NAME = 'os_vm_used_cores'
|
||||
|
||||
|
||||
@click.command(COMMAND_NAME, short_help=DOC)
|
||||
@click.option('--config-file',
|
||||
help='OpenStack configuration file',
|
||||
default='openstack.ini')
|
||||
@pass_context
|
||||
def cli(ctx, config_file):
|
||||
setattr(cli, '__doc__', DOC)
|
||||
|
||||
output = {
|
||||
'measurement_name': COMMAND_NAME,
|
||||
'meta': {
|
||||
'used': 'cores'
|
||||
},
|
||||
'variables': {}
|
||||
}
|
||||
|
||||
used_collection = collections.Counter()
|
||||
nova_config = utils.read_config(config_file=config_file)['nova']
|
||||
_ost = ost.OpenStack(os_auth_args=nova_config)
|
||||
flavors = _ost.get_flavors()
|
||||
try:
|
||||
variables = output['variables']
|
||||
for used in _ost.get_consumer_usage():
|
||||
flavor = flavors[used['flavor']['id']]
|
||||
used_collection[used['name']] += int(flavor['vcpus'])
|
||||
output['meta'][used['flavor']['id']] = True
|
||||
output['meta'][used['flavor']['name']] = True
|
||||
variables.update(used_collection)
|
||||
except Exception as exp:
|
||||
output['exit_code'] = 1
|
||||
output['message'] = '{} failed -- Error: {}'.format(COMMAND_NAME, exp)
|
||||
else:
|
||||
output['exit_code'] = 0
|
||||
output['message'] = '{} is ok'.format(COMMAND_NAME)
|
||||
finally:
|
||||
return output
|
|
@ -0,0 +1,63 @@
|
|||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
import collections
|
||||
|
||||
import click
|
||||
|
||||
from monitorstack import utils
|
||||
from monitorstack.cli import pass_context
|
||||
from monitorstack.utils import os_utils as ost
|
||||
|
||||
|
||||
DOC = """Get nova used disk."""
|
||||
COMMAND_NAME = 'os_vm_used_disk'
|
||||
|
||||
|
||||
@click.command(COMMAND_NAME, short_help=DOC)
|
||||
@click.option('--config-file',
|
||||
help='OpenStack configuration file',
|
||||
default='openstack.ini')
|
||||
@pass_context
|
||||
def cli(ctx, config_file):
|
||||
setattr(cli, '__doc__', DOC)
|
||||
|
||||
output = {
|
||||
'measurement_name': COMMAND_NAME,
|
||||
'meta': {
|
||||
'used': 'disk'
|
||||
},
|
||||
'variables': {}
|
||||
}
|
||||
|
||||
used_collection = collections.Counter()
|
||||
nova_config = utils.read_config(config_file=config_file)['nova']
|
||||
_ost = ost.OpenStack(os_auth_args=nova_config)
|
||||
flavors = _ost.get_flavors()
|
||||
try:
|
||||
variables = output['variables']
|
||||
for used in _ost.get_consumer_usage():
|
||||
flavor = flavors[used['flavor']['id']]
|
||||
used_collection[used['name']] += int(flavor['disk'])
|
||||
output['meta'][used['flavor']['id']] = True
|
||||
output['meta'][used['flavor']['name']] = True
|
||||
variables.update(used_collection)
|
||||
except Exception as exp:
|
||||
output['exit_code'] = 1
|
||||
output['message'] = '{} failed -- Error: {}'.format(COMMAND_NAME, exp)
|
||||
else:
|
||||
output['exit_code'] = 0
|
||||
output['message'] = '{} is ok'.format(COMMAND_NAME)
|
||||
finally:
|
||||
return output
|
|
@ -0,0 +1,59 @@
|
|||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
import collections
|
||||
|
||||
import click
|
||||
|
||||
from monitorstack import utils
|
||||
from monitorstack.cli import pass_context
|
||||
from monitorstack.utils import os_utils as ost
|
||||
|
||||
|
||||
DOC = """Get nova used instances."""
|
||||
COMMAND_NAME = 'os_vm_used_instance'
|
||||
|
||||
|
||||
@click.command(COMMAND_NAME, short_help=DOC)
|
||||
@click.option('--config-file',
|
||||
help='OpenStack configuration file',
|
||||
default='openstack.ini')
|
||||
@pass_context
|
||||
def cli(ctx, config_file):
|
||||
setattr(cli, '__doc__', DOC)
|
||||
|
||||
output = {
|
||||
'measurement_name': COMMAND_NAME,
|
||||
'meta': {
|
||||
'used': 'instances'
|
||||
},
|
||||
'variables': {}
|
||||
}
|
||||
|
||||
used_collection = collections.Counter()
|
||||
nova_config = utils.read_config(config_file=config_file)['nova']
|
||||
_ost = ost.OpenStack(os_auth_args=nova_config)
|
||||
try:
|
||||
variables = output['variables']
|
||||
for used in _ost.get_consumer_usage():
|
||||
used_collection[used['name']] += 1
|
||||
variables.update(used_collection)
|
||||
except Exception as exp:
|
||||
output['exit_code'] = 1
|
||||
output['message'] = '{} failed -- Error: {}'.format(COMMAND_NAME, exp)
|
||||
else:
|
||||
output['exit_code'] = 0
|
||||
output['message'] = '{} is ok'.format(COMMAND_NAME)
|
||||
finally:
|
||||
return output
|
|
@ -0,0 +1,63 @@
|
|||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
import collections
|
||||
|
||||
import click
|
||||
|
||||
from monitorstack import utils
|
||||
from monitorstack.cli import pass_context
|
||||
from monitorstack.utils import os_utils as ost
|
||||
|
||||
|
||||
DOC = """Get nova used ram."""
|
||||
COMMAND_NAME = 'os_vm_used_ram'
|
||||
|
||||
|
||||
@click.command(COMMAND_NAME, short_help=DOC)
|
||||
@click.option('--config-file',
|
||||
help='OpenStack configuration file',
|
||||
default='openstack.ini')
|
||||
@pass_context
|
||||
def cli(ctx, config_file):
|
||||
setattr(cli, '__doc__', DOC)
|
||||
|
||||
output = {
|
||||
'measurement_name': COMMAND_NAME,
|
||||
'meta': {
|
||||
'used': 'ram'
|
||||
},
|
||||
'variables': {}
|
||||
}
|
||||
|
||||
used_collection = collections.Counter()
|
||||
nova_config = utils.read_config(config_file=config_file)['nova']
|
||||
_ost = ost.OpenStack(os_auth_args=nova_config)
|
||||
flavors = _ost.get_flavors()
|
||||
try:
|
||||
variables = output['variables']
|
||||
for used in _ost.get_consumer_usage():
|
||||
flavor = flavors[used['flavor']['id']]
|
||||
used_collection[used['name']] += int(flavor['ram'])
|
||||
output['meta'][used['flavor']['id']] = True
|
||||
output['meta'][used['flavor']['name']] = True
|
||||
variables.update(used_collection)
|
||||
except Exception as exp:
|
||||
output['exit_code'] = 1
|
||||
output['message'] = '{} failed -- Error: {}'.format(COMMAND_NAME, exp)
|
||||
else:
|
||||
output['exit_code'] = 0
|
||||
output['message'] = '{} is ok'.format(COMMAND_NAME)
|
||||
finally:
|
||||
return output
|
|
@ -24,6 +24,7 @@ from monitorstack.cli import pass_context
|
|||
@pass_context
|
||||
def cli(ctx):
|
||||
"""Get system uptime."""
|
||||
|
||||
uptime = get_uptime()
|
||||
output = {
|
||||
'exit_code': 0,
|
||||
|
@ -41,6 +42,7 @@ def cli(ctx):
|
|||
|
||||
def get_uptime():
|
||||
"""Read the uptime from the proc filesystem."""
|
||||
|
||||
with open('/proc/uptime', 'r') as f:
|
||||
output = f.read()
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
import os
|
||||
import shelve
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
# Lower import to support conditional configuration parser
|
||||
try:
|
||||
if sys.version_info > (3, 2, 0):
|
||||
import configparser as ConfigParser
|
||||
else:
|
||||
import ConfigParser
|
||||
except ImportError:
|
||||
raise SystemExit('No configparser module was found.')
|
||||
|
||||
|
||||
def is_int(value):
|
||||
for v_type in [int, float]:
|
||||
try:
|
||||
value = v_type(value)
|
||||
except ValueError:
|
||||
pass # v was not a int, float, or long
|
||||
else:
|
||||
return value
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
class LocalCache(object):
|
||||
"""Context Manager for opening and closing access to the DBM."""
|
||||
|
||||
def __init__(self):
|
||||
"""Set the Path to the DBM to create/Open."""
|
||||
|
||||
self.db_cache = os.path.join(
|
||||
tempfile.gettempdir(),
|
||||
'monitorstack.openstack.dbm'
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
"""Open the DBM in r/w mode.
|
||||
|
||||
:return: Open DBM
|
||||
"""
|
||||
|
||||
return self.open_shelve
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
"""Close DBM Connection."""
|
||||
|
||||
self.close_shelve()
|
||||
|
||||
def _open_shelve(self):
|
||||
return shelve.open(self.db_cache)
|
||||
|
||||
@property
|
||||
def open_shelve(self):
|
||||
return self._open_shelve()
|
||||
|
||||
def close_shelve(self):
|
||||
self.open_shelve.close()
|
||||
|
||||
|
||||
def read_config(config_file):
|
||||
cfg = os.path.abspath(os.path.expanduser(config_file))
|
||||
if not os.path.isfile(cfg):
|
||||
raise IOError('Config file "{}" was not found'.format(cfg))
|
||||
|
||||
parser = ConfigParser.ConfigParser()
|
||||
parser.optionxform = str
|
||||
parser.read([cfg])
|
||||
args = dict()
|
||||
defaults = dict([(k, v) for k, v in parser.items(section='DEFAULT')])
|
||||
for section in parser.sections():
|
||||
if section == 'DEFAULT':
|
||||
continue
|
||||
|
||||
sec = args[section] = defaults
|
||||
for key, value in parser.items(section):
|
||||
sec[key] = is_int(value=value)
|
||||
|
||||
return args
|
|
@ -0,0 +1,90 @@
|
|||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
try:
|
||||
from openstack import connection as os_conn
|
||||
from openstack import exceptions as os_exp
|
||||
except ImportError as e:
|
||||
raise SystemExit('OpenStack plugins require access to the OpenStackSDK.'
|
||||
' Please install "python-openstacksdk".'
|
||||
' ERROR: %s' % str(e))
|
||||
|
||||
from monitorstack import utils
|
||||
|
||||
|
||||
class OpenStack(object):
|
||||
def __init__(self, os_auth_args):
|
||||
self.os_auth_args = os_auth_args
|
||||
|
||||
@property
|
||||
def conn(self):
|
||||
return os_conn.Connection(**self.os_auth_args)
|
||||
|
||||
def get_consumer_usage(self, servers=None, marker=None, limit=512):
|
||||
tenant_kwargs = {'details': True, 'all_tenants': True, 'limit': limit}
|
||||
if not servers:
|
||||
servers = list()
|
||||
|
||||
if marker:
|
||||
tenant_kwargs['marker'] = marker
|
||||
|
||||
count = 0
|
||||
for server in self.conn.compute.servers(**tenant_kwargs):
|
||||
servers.append(server)
|
||||
count += 1
|
||||
|
||||
if count == limit:
|
||||
return self.get_consumer_usage(
|
||||
servers=servers,
|
||||
marker=servers[-1].id
|
||||
)
|
||||
|
||||
return servers
|
||||
|
||||
def get_compute_limits(self, project_id, interface='internal'):
|
||||
url = self.conn.compute.session.get_endpoint(
|
||||
interface=interface,
|
||||
service_type='compute'
|
||||
)
|
||||
quota_data = self.conn.compute.session.get(
|
||||
url + '/os-quota-sets/' + project_id
|
||||
)
|
||||
return quota_data.json()
|
||||
|
||||
def get_project_name(self, project_id):
|
||||
with utils.LocalCache() as c:
|
||||
try:
|
||||
project_name = c.get(project_id)
|
||||
if not project_name:
|
||||
project_info = self.conn.identity.get_project(project_id)
|
||||
project_name = c[project_info.id] = project_info.name
|
||||
except os_exp.ResourceNotFound:
|
||||
return None
|
||||
else:
|
||||
return project_name
|
||||
|
||||
def get_projects(self):
|
||||
_consumers = list()
|
||||
with utils.LocalCache() as c:
|
||||
for project in self.conn.identity.projects():
|
||||
_consumers.append(project)
|
||||
c[project.id] = project.name
|
||||
return _consumers
|
||||
|
||||
def get_flavors(self):
|
||||
flavor_cache = dict()
|
||||
for flavor in self.conn.compute.flavors():
|
||||
entry = flavor_cache[flavor['id']] = dict()
|
||||
entry.update(flavor)
|
||||
return flavor_cache
|
|
@ -1,2 +1,3 @@
|
|||
pytest
|
||||
tox
|
||||
mock>=2.0.0
|
|
@ -35,7 +35,7 @@ class TestFormatters(object):
|
|||
|
||||
def test_current_time(self):
|
||||
"""Test current_time()."""
|
||||
result = formatters.current_time()
|
||||
result = formatters._current_time()
|
||||
assert isinstance(result, int)
|
||||
assert result > 0
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
"""Tests for the KVM plugin."""
|
||||
|
||||
import json
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
from monitorstack.cli import cli
|
||||
|
||||
|
||||
def _runner(module):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ['-f', 'json', module])
|
||||
return json.loads(result.output)
|
||||
|
||||
|
||||
class TestOs(object):
|
||||
"""Tests for the os_vm.* monitors."""
|
||||
|
||||
def test_os_vm_quota_cores(self):
|
||||
"""Ensure the run() method works."""
|
||||
|
||||
# result_json = _runner(module='os_vm_quota_cores')
|
||||
# variables = result_json['variables']
|
||||
# meta = result_json['meta']
|
||||
pass
|
||||
|
||||
def test_os_vm_quota_instances(self):
|
||||
"""Ensure the run() method works."""
|
||||
pass
|
||||
|
||||
def test_os_vm_quota_ram(self):
|
||||
"""Ensure the run() method works."""
|
||||
pass
|
||||
|
||||
def test_os_vm_used_cores(self):
|
||||
"""Ensure the run() method works."""
|
||||
pass
|
||||
|
||||
def test_os_vm_used_disk(self):
|
||||
"""Ensure the run() method works."""
|
||||
pass
|
||||
|
||||
def test_os_vm_used_instances(self):
|
||||
"""Ensure the run() method works."""
|
||||
pass
|
||||
|
||||
def test_os_vm_used_ram(self):
|
||||
"""Ensure the run() method works."""
|
||||
pass
|
|
@ -46,7 +46,7 @@ class LibvirtStub(object):
|
|||
|
||||
|
||||
class TestKvm(object):
|
||||
"""Tests for the uptime monitor class."""
|
||||
"""Tests for the kvm monitor."""
|
||||
|
||||
def test_run(self):
|
||||
"""Ensure the run() method works."""
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
# Copyright 2017, Major Hayden <major@mhtx.net>
|
||||
#
|
||||
# Licensed 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.
|
||||
"""Tests for the uptime plugin."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from monitorstack import utils
|
||||
|
||||
|
||||
class TestUptime(unittest.TestCase):
|
||||
"""Tests for the utilities."""
|
||||
|
||||
def setUp(self):
|
||||
os_config_file = os.path.expanduser(
|
||||
os.path.abspath(__file__ + '/../../etc/openstack.ini')
|
||||
)
|
||||
self.config = utils.read_config(os_config_file)
|
||||
conf = utils.ConfigParser.RawConfigParser()
|
||||
conf.read([os_config_file])
|
||||
self.config_defaults = conf.defaults()
|
||||
|
||||
def tearDown(self):
|
||||
local_cache = os.path.join(
|
||||
tempfile.gettempdir(),
|
||||
'monitorstack.openstack.dbm'
|
||||
)
|
||||
if os.path.exists(local_cache):
|
||||
os.remove(local_cache)
|
||||
|
||||
def test_is_int_is_int(self):
|
||||
self.assertTrue(isinstance(utils.is_int(value=1), int))
|
||||
|
||||
def test_is_int_is_int_str(self):
|
||||
self.assertTrue(isinstance(utils.is_int(value='1'), int))
|
||||
|
||||
def test_is_int_is_not_int(self):
|
||||
self.assertTrue(isinstance(utils.is_int(value='a'), str))
|
||||
|
||||
def test_read_config_not_found(self):
|
||||
self.assertRaises(
|
||||
IOError,
|
||||
utils.read_config,
|
||||
'not-a-file'
|
||||
)
|
||||
|
||||
def test_read_config_found_dict_return(self):
|
||||
self.assertTrue(isinstance(self.config, dict))
|
||||
|
||||
def test_read_config_found_defaults_in_sections(self):
|
||||
for k, v in self.config.items():
|
||||
for key in self.config_defaults.keys():
|
||||
self.assertTrue(key in v.keys())
|
||||
|
||||
def test_local_cache(self):
|
||||
with utils.LocalCache() as c:
|
||||
c['test_key'] = True
|
||||
self.assertTrue('test_key' in c)
|
Loading…
Reference in New Issue