Compare commits

..

9 Commits

Author SHA1 Message Date
Alexander Chadin
3640fc4a0b Rename TRIGGERED state as PENDING
Following the change in Watcher, this patchset updates the
Watcher CLI to support this change. Also it updates README.rst
to pass python 3.4 ascii-tests.

Change-Id: Idfcf99ee3d6b5b7342f1bbc4f726e6edd82fc77c
Closes-Bug: #1548377
Related-Bug: #1554347
2016-03-21 12:17:18 +00:00
OpenStack Proposal Bot
15511203d8 Updated from global requirements
Change-Id: Ife3d32cf3d7054ce9e6e5f7c8f849652a6ba2267
2016-02-20 22:00:57 +00:00
OpenStack Proposal Bot
f390f3526f Updated from global requirements
Change-Id: Ia6f12d87f376841faebcab42ac70a72f6f222985
2016-02-19 02:37:27 +00:00
Jenkins
0ce454f3b3 Merge "Replace deprecated LOG.warn with LOG.warning" 2016-02-18 08:21:48 +00:00
Jenkins
5b4729dcbe Merge "Removed host_aggregate filter for Audit Template" 2016-02-15 09:29:58 +00:00
Tin Lam
a36b42afef Replace deprecated LOG.warn with LOG.warning
LOG.warn is deprecated. It is still used in a few places.
This replaces it with the non-deprecated LOG.warning.

Change-Id: I7ed615a24dcb4eff919d747d8f30625d6d4db0ee
Partial-Bug:#1508442
2016-02-14 17:45:11 -06:00
Vincent Francoise
9699fc5f70 Removed host_aggregate filter for Audit Template
Although it was proposed via python-watcherclient, the feature was not
implemented on the Watcher API. As the notion of host aggregate is
currently unused in Watcher, decision was made to only implement the
filtering of goal within the Watcher API whilst removing the
host_aggregate filter from the Watcher client.
Thus, this patchset removes the 'host_aggregate' parameter from the
client.

Change-Id: I014e4b0d5e734ea9ef869c3ad33908a2c0d2aa40
Related-Bug: #1510189
2016-02-12 15:49:26 +00:00
Vincent Francoise
51a3b3fe3f Removed useless '--name' in audit-template-list
This bug outlined that a --name option of the audit-template-list
subcommand was not working properly. But the goal of this command
would have been equivalent to using 'audit-template-show'. That's
why I simply removed it from our client.

Change-Id: I57bb0c88bcf373304e59109cd7557052ef7a980b
Closes-Bug: #1510190
2016-02-11 15:21:42 +01:00
Taylor Peoples
b30fad9965 Sync with openstack/requirements master branch
In order to accept the requirements contract defined in
openstack/requirements, we need to sync with the master branch of that
project's global-requirements.txt and test-requirements.txt files.

Most of the changes are just version changes and nothing major.  The
only major changes are:

1) Removing httpretty and therefore needing to update the test_shell.py
file which uses httpretty.  It seems like the code that was dependent on
httpretty is not even being ran as part of the UTs, so this code was
removed.

2) The twine dependency is removed, and therefore the pypi tox
environment was also removed.

Change-Id: I939856e9b0a32792bea45b42c489dc9bbbe62782
Closes-Bug: #1533283
2016-01-26 07:39:44 +01:00
13 changed files with 54 additions and 196 deletions

View File

@@ -6,12 +6,12 @@ Client for resource optimization service for OpenStack.
OpenStack Watcher provides a flexible and scalable resource optimization
service for multi-tenant OpenStack-based clouds.
Watcher provides a complete optimization loopincluding everything from a
Watcher provides a complete optimization loop-including everything from a
metrics receiver, complex event processor and profiler, optimization processor
and an action plan applier. This provides a robust framework to realize a wide
range of cloud optimization goals, including the reduction of data center
operating costs, increased system performance via intelligent virtual machine
migration, increased energy efficiencyand more!
migration, increased energy efficiency-and more!
* Free software: Apache license
* Wiki: http://wiki.openstack.org/wiki/Watcher

View File

@@ -2,9 +2,9 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
Babel>=1.3
oslo.i18n>=1.5.0 # Apache-2.0
oslo.utils>=2.0.0,!=2.6.0 # Apache-2.0
pbr>=1.6
python-keystoneclient>=1.6.0,!=1.8.0
six>=1.9.0
Babel>=1.3 # BSD
oslo.i18n>=2.1.0 # Apache-2.0
oslo.utils>=3.5.0 # Apache-2.0
pbr>=1.6 # Apache-2.0
python-keystoneclient!=1.8.0,!=2.1.0,>=1.6.0 # Apache-2.0
six>=1.9.0 # MIT

3
setup.py Executable file → Normal file
View File

@@ -1,4 +1,3 @@
#!/usr/bin/env python
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,5 +25,5 @@ except ImportError:
pass
setuptools.setup(
setup_requires=['pbr'],
setup_requires=['pbr>=1.8'],
pbr=True)

View File

@@ -2,19 +2,17 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
coverage>=3.6
discover
hacking<0.11,>=0.10
httpretty>=0.8.4,<0.8.7
mock>=1.2
oslosphinx>=2.5.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
python-subunit>=0.0.18
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
testrepository>=0.0.18
testscenarios>=0.4
testtools>=1.4.0
coverage>=3.6 # Apache-2.0
discover # BSD
hacking<0.11,>=0.10.2
mock>=1.2 # BSD
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
python-subunit>=0.0.18 # Apache-2.0/BSD
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=1.4.0 # MIT
# Needed for pypi packaging
wheel
twine
wheel # MIT

View File

@@ -34,11 +34,6 @@ ignore = E123,E125
builtins = _
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
[testenv:pypi]
commands =
python setup.py sdist bdist_wheel
twine upload --config-file .pypirc {posargs} dist/*
[testenv:wheel]
commands = python setup.py bdist_wheel

View File

@@ -178,7 +178,7 @@ class HTTPClient(object):
self.log_http_response(resp)
if 400 <= resp.status < 600:
LOG.warn(_LW("Request returned failure status."))
LOG.warning(_LW("Request returned failure status."))
error_json = _extract_error_json(body_str)
raise exc.from_response(
resp, error_json.get('faultstring'),

View File

@@ -12,24 +12,17 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import os
import re
import sys
import fixtures
import httpretty
from keystoneclient import exceptions as keystone_exc
from keystoneclient.fixture import v2 as ks_v2_fixture
from keystoneclient.fixture import v3 as ks_v3_fixture
import mock
import six
import testtools
from testtools import matchers
from watcherclient import exceptions as exc
from watcherclient import shell as watcher_shell
from watcherclient.tests import keystone_client_fixtures
from watcherclient.tests import utils
FAKE_ENV = {'OS_USERNAME': 'username',
@@ -37,22 +30,6 @@ FAKE_ENV = {'OS_USERNAME': 'username',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': 'http://no.where/v2.0/'}
FAKE_ENV_KEYSTONE_V2 = {
'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': keystone_client_fixtures.BASE_URL,
}
FAKE_ENV_KEYSTONE_V3 = {
'OS_USERNAME': 'username',
'OS_PASSWORD': 'password',
'OS_TENANT_NAME': 'tenant_name',
'OS_AUTH_URL': keystone_client_fixtures.BASE_URL,
'OS_USER_DOMAIN_ID': 'default',
'OS_PROJECT_DOMAIN_ID': 'default',
}
class ShellTest(utils.BaseTestCase):
re_options = re.DOTALL | re.MULTILINE
@@ -175,132 +152,3 @@ class ShellTest(utils.BaseTestCase):
for r in required:
self.assertThat(stdout,
matchers.MatchesRegex(r, self.re_options))
class TestCase(testtools.TestCase):
tokenid = keystone_client_fixtures.TOKENID
def set_fake_env(self, fake_env):
client_env = ('OS_USERNAME', 'OS_PASSWORD', 'OS_TENANT_ID',
'OS_TENANT_NAME', 'OS_AUTH_URL', 'OS_REGION_NAME',
'OS_AUTH_TOKEN', 'OS_NO_CLIENT_AUTH', 'OS_SERVICE_TYPE',
'OS_ENDPOINT_TYPE')
for key in client_env:
self.useFixture(
fixtures.EnvironmentVariable(key, fake_env.get(key)))
# required for testing with Python 2.6
def assertRegexpMatches(self, text, expected_regexp, msg=None):
"""Fail the test unless the text matches the regular expression."""
if isinstance(expected_regexp, six.string_types):
expected_regexp = re.compile(expected_regexp)
if not expected_regexp.search(text):
msg = msg or "Regexp didn't match"
msg = '%s: %r not found in %r' % (
msg, expected_regexp.pattern, text)
raise self.failureException(msg)
def register_keystone_v2_token_fixture(self):
v2_token = ks_v2_fixture.Token(token_id=self.tokenid)
service = v2_token.add_service('baremetal')
service.add_endpoint('http://watcher.example.com', region='RegionOne')
httpretty.register_uri(
httpretty.POST,
'%s/tokens' % (keystone_client_fixtures.V2_URL),
body=json.dumps(v2_token))
def register_keystone_v3_token_fixture(self):
v3_token = ks_v3_fixture.Token()
service = v3_token.add_service('baremetal')
service.add_standard_endpoints(public='http://watcher.example.com')
httpretty.register_uri(
httpretty.POST,
'%s/auth/tokens' % (keystone_client_fixtures.V3_URL),
body=json.dumps(v3_token),
adding_headers={'X-Subject-Token': self.tokenid})
def register_keystone_auth_fixture(self):
self.register_keystone_v2_token_fixture()
self.register_keystone_v3_token_fixture()
httpretty.register_uri(
httpretty.GET,
keystone_client_fixtures.BASE_URL,
body=keystone_client_fixtures.keystone_request_callback)
class ShellTestNoMox(TestCase):
def setUp(self):
super(ShellTestNoMox, self).setUp()
# httpretty doesn't work as expected if http proxy environment
# variable is set.
os.environ = dict((k, v) for (k, v) in os.environ.items()
if k.lower() not in ('http_proxy', 'https_proxy'))
self.set_fake_env(FAKE_ENV_KEYSTONE_V2)
def shell(self, argstr):
orig = sys.stdout
try:
sys.stdout = six.StringIO()
_shell = watcher_shell.WatcherShell()
_shell.main(argstr.split())
self.subcommands = _shell.subcommands.keys()
except SystemExit:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.assertEqual(0, exc_value.code)
finally:
out = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = orig
return out
# @httpretty.activate
# def test_action_list(self):
# self.register_keystone_auth_fixture()
# resp_dict = {"dummies": [
# {"instance_uuid": "null",
# "uuid": "351a82d6-9f04-4c36-b79a-a38b9e98ff71",
# "links": [{"href": "http://watcher.example.com:6385/"
# "v1/dummies/foo",
# "rel": "self"},
# {"href": "http://watcher.example.com:6385/"
# "dummies/foo",
# "rel": "bookmark"}],
# "maintenance": "false",
# "provision_state": "null",
# "power_state": "power off"},
# {"instance_uuid": "null",
# "uuid": "66fbba13-29e8-4b8a-9e80-c655096a40d3",
# "links": [{"href": "http://watcher.example.com:6385/"
# "v1/dummies/foo2",
# "rel": "self"},
# {"href": "http://watcher.example.com:6385/"
# "dummies/foo2",
# "rel": "bookmark"}],
# "maintenance": "false",
# "provision_state": "null",
# "power_state": "power off"}]}
# httpretty.register_uri(
# httpretty.GET,
# 'http://watcher.example.com/v1/dummies',
# status=200,
# content_type='application/json; charset=UTF-8',
# body=json.dumps(resp_dict))
# event_list_text = self.shell('action-list')
# required = [
# '351a82d6-9f04-4c36-b79a-a38b9e98ff71',
# '66fbba13-29e8-4b8a-9e80-c655096a40d3',
# ]
# for r in required:
# self.assertRegexpMatches(event_list_text, r)
class ShellTestNoMoxV3(ShellTestNoMox):
def _set_fake_env(self):
self.set_fake_env(FAKE_ENV_KEYSTONE_V3)

View File

@@ -38,7 +38,7 @@ ACTION_PLAN2 = {
}
UPDATED_ACTION_PLAN = copy.deepcopy(ACTION_PLAN1)
NEW_STATE = 'TRIGGERED'
NEW_STATE = 'PENDING'
UPDATED_ACTION_PLAN['state'] = NEW_STATE
fake_responses = {

View File

@@ -137,7 +137,7 @@ class ActionPlanShellTest(utils.BaseTestCase):
ap_shell.do_action_plan_start(client_mock, args)
patch = commonutils.args_array_to_patch(
'replace', ['state=TRIGGERED'])
'replace', ['state=PENDING'])
client_mock.action_plan.update.assert_called_once_with(
'a5199d0e-0702-4613-9234-5ae2af8dafea', patch)

View File

@@ -19,7 +19,7 @@ import copy
from six.moves.urllib import parse as urlparse
import testtools
from testtools.matchers import HasLength
from testtools import matchers
from watcherclient.tests import utils
import watcherclient.v1.audit_template
@@ -41,7 +41,7 @@ AUDIT_TMPL2 = {
'description': 'Audit Template 2 description',
'host_aggregate': 8,
'extra': {'automatic': True},
'goal': 'SERVERS_CONSOLIDATION'
'goal': 'BASIC_CONSOLIDATION'
}
AUDIT_TMPL3 = {
@@ -176,6 +176,16 @@ fake_responses_sorting = {
},
}
fake_responses_filter_by_goal = {
'/v1/audit_templates/?goal=BASIC_CONSOLIDATION':
{
'GET': (
{},
{"audit_templates": [AUDIT_TMPL2]}
),
},
}
class AuditTemplateManagerTest(testtools.TestCase):
@@ -202,6 +212,18 @@ class AuditTemplateManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(audit_templates))
def test_audit_templates_list_by_goal(self):
self.api = utils.FakeAPI(fake_responses_filter_by_goal)
self.mgr = watcherclient.v1.audit_template.AuditTemplateManager(
self.api)
audit_templates = self.mgr.list(goal="BASIC_CONSOLIDATION")
expect = [
('GET', '/v1/audit_templates/?goal=%s' % AUDIT_TMPL2['goal'],
{}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(audit_templates))
def test_audit_templates_list_detail(self):
audit_templates = self.mgr.list(detail=True)
expect = [
@@ -230,7 +252,7 @@ class AuditTemplateManagerTest(testtools.TestCase):
('GET', '/v1/audit_templates/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(audit_templates, HasLength(1))
self.assertThat(audit_templates, matchers.HasLength(1))
def test_audit_templates_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
@@ -242,7 +264,7 @@ class AuditTemplateManagerTest(testtools.TestCase):
('GET', '/v1/audit_templates/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(audit_templates, HasLength(2))
self.assertThat(audit_templates, matchers.HasLength(2))
def test_audit_templates_list_sort_key(self):
self.api = utils.FakeAPI(fake_responses_sorting)

View File

@@ -145,7 +145,7 @@ def do_action_plan_start(cc, args):
action_plan_uuid = getattr(args, 'action-plan')
if uuidutils.is_uuid_like(action_plan_uuid):
args.op = 'replace'
args.attributes = [['state=TRIGGERED']]
args.attributes = [['state=PENDING']]
patch = utils.args_array_to_patch(
args.op,

View File

@@ -34,7 +34,7 @@ class AuditTemplateManager(base.Manager):
def _path(id=None):
return '/v1/audit_templates/%s' % id if id else '/v1/audit_templates'
def list(self, name=None, limit=None, sort_key=None,
def list(self, name=None, goal=None, limit=None, sort_key=None,
sort_dir=None, detail=False):
"""Retrieve a list of audit template.
@@ -65,6 +65,8 @@ class AuditTemplateManager(base.Manager):
filters = utils.common_filters(limit, sort_key, sort_dir)
if name is not None:
filters.append('name=%s' % name)
if goal is not None:
filters.append("goal=%s" % goal)
path = ''
if detail:

View File

@@ -45,10 +45,6 @@ def do_audit_template_show(cc, args):
action='store_true',
default=False,
help="Show detailed information about audit templates.")
@cliutils.arg(
'--name',
metavar='<name>',
help='Only show information for the audit template with this name.')
@cliutils.arg(
'--goal',
metavar='<goal>',
@@ -73,8 +69,6 @@ def do_audit_template_list(cc, args):
"""List the audit templates."""
params = {}
if args.name is not None:
params['name'] = args.name
if args.goal is not None:
params['goal'] = args.goal
if args.detail: