Remove instance action logging mechanism
* Remove InstanceActions db model * Remove relevant db api functions * Add migration 93 which drops the instance_actions * Remove server_action_list API extension * Fixes bug 994846 Change-Id: Ibbd787183034314460f41c84b9ad152655739209
This commit is contained in:
parent
7ad7e9049c
commit
99f3822fd3
|
@ -47,7 +47,6 @@
|
|||
"compute_extension:quota_classes": [],
|
||||
"compute_extension:rescue": [],
|
||||
"compute_extension:security_groups": [],
|
||||
"compute_extension:server_action_list": [["rule:admin_api"]],
|
||||
"compute_extension:server_diagnostics": [["rule:admin_api"]],
|
||||
"compute_extension:simple_tenant_usage:show": [["rule:admin_or_owner"]],
|
||||
"compute_extension:simple_tenant_usage:list": [["rule:admin_api"]],
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
# Copyright 2011 OpenStack LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 webob.exc
|
||||
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api.openstack import xmlutil
|
||||
from nova import compute
|
||||
from nova import exception
|
||||
|
||||
|
||||
sa_nsmap = {None: wsgi.XMLNS_V11}
|
||||
authorize = extensions.extension_authorizer('compute', 'server_action_list')
|
||||
|
||||
|
||||
class ServerActionsTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('actions')
|
||||
elem = xmlutil.SubTemplateElement(root, 'action', selector='actions')
|
||||
elem.set('created_at')
|
||||
elem.set('action')
|
||||
elem.set('error')
|
||||
return xmlutil.MasterTemplate(root, 1, nsmap=sa_nsmap)
|
||||
|
||||
|
||||
class ServerActionListController(object):
|
||||
@wsgi.serializers(xml=ServerActionsTemplate)
|
||||
def index(self, req, server_id):
|
||||
context = req.environ["nova.context"]
|
||||
authorize(context)
|
||||
compute_api = compute.API()
|
||||
|
||||
try:
|
||||
instance = compute_api.get(context, server_id)
|
||||
except exception.NotFound:
|
||||
raise webob.exc.HTTPNotFound(_("Instance not found"))
|
||||
|
||||
items = compute_api.get_actions(context, instance)
|
||||
|
||||
def _format_item(item):
|
||||
return {
|
||||
'created_at': str(item['created_at']),
|
||||
'action': item['action'],
|
||||
'error': item['error'],
|
||||
}
|
||||
|
||||
return {'actions': [_format_item(item) for item in items]}
|
||||
|
||||
|
||||
class Server_action_list(extensions.ExtensionDescriptor):
|
||||
"""Allow Admins to view pending server actions"""
|
||||
|
||||
name = "ServerActionList"
|
||||
alias = "os-server-action-list"
|
||||
namespace = ("http://docs.openstack.org/compute/ext/"
|
||||
"server-actions-list/api/v1.1")
|
||||
updated = "2011-12-21T00:00:00+00:00"
|
||||
|
||||
def get_resources(self):
|
||||
parent_def = {'member_name': 'server', 'collection_name': 'servers'}
|
||||
#NOTE(bcwaldon): This should be prefixed with 'os-'
|
||||
ext = extensions.ResourceExtension('actions',
|
||||
ServerActionListController(),
|
||||
parent=parent_def)
|
||||
return [ext]
|
|
@ -1515,11 +1515,6 @@ class API(BaseAPI):
|
|||
return self._call_compute_message("get_diagnostics", context,
|
||||
instance)
|
||||
|
||||
@wrap_check_policy
|
||||
def get_actions(self, context, instance):
|
||||
"""Retrieve actions for the given instance."""
|
||||
return self.db.instance_get_actions(context, instance['uuid'])
|
||||
|
||||
@wrap_check_policy
|
||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
||||
vm_states.RESCUED],
|
||||
|
|
|
@ -623,16 +623,6 @@ def instance_remove_security_group(context, instance_id, security_group_id):
|
|||
security_group_id)
|
||||
|
||||
|
||||
def instance_action_create(context, values):
|
||||
"""Create an instance action from the values dictionary."""
|
||||
return IMPL.instance_action_create(context, values)
|
||||
|
||||
|
||||
def instance_get_actions(context, instance_uuid):
|
||||
"""Get instance actions by instance uuid."""
|
||||
return IMPL.instance_get_actions(context, instance_uuid)
|
||||
|
||||
|
||||
def instance_get_id_to_uuid_mapping(context, ids):
|
||||
"""Return a dictionary containing 'ID: UUID' given the ids"""
|
||||
return IMPL.instance_get_id_to_uuid_mapping(context, ids)
|
||||
|
|
|
@ -1681,27 +1681,6 @@ def instance_remove_security_group(context, instance_uuid, security_group_id):
|
|||
'updated_at': literal_column('updated_at')})
|
||||
|
||||
|
||||
@require_context
|
||||
def instance_action_create(context, values):
|
||||
"""Create an instance action from the values dictionary."""
|
||||
action_ref = models.InstanceActions()
|
||||
action_ref.update(values)
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
action_ref.save(session=session)
|
||||
return action_ref
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def instance_get_actions(context, instance_uuid):
|
||||
"""Return the actions associated to the given instance id"""
|
||||
session = get_session()
|
||||
return session.query(models.InstanceActions).\
|
||||
filter_by(instance_uuid=instance_uuid).\
|
||||
all()
|
||||
|
||||
|
||||
@require_context
|
||||
def instance_get_id_to_uuid_mapping(context, ids):
|
||||
session = get_session()
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# Copyright 2012 Openstack, LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from sqlalchemy import Boolean, Column, DateTime, ForeignKey
|
||||
from sqlalchemy import Integer, MetaData, String, Table, Text
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
instance_actions = Table('instance_actions', meta, autoload=True)
|
||||
instance_actions.drop()
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
instances = Table('instances', meta, autoload=True,
|
||||
autoload_with=migrate_engine)
|
||||
|
||||
instance_actions = Table('instance_actions', meta,
|
||||
Column('created_at', DateTime(timezone=False)),
|
||||
Column('updated_at', DateTime(timezone=False)),
|
||||
Column('deleted_at', DateTime(timezone=False)),
|
||||
Column('deleted', Boolean(create_constraint=True, name=None)),
|
||||
Column('id', Integer(), primary_key=True, nullable=False),
|
||||
Column('instance_id',
|
||||
Integer(),
|
||||
ForeignKey('instances.id')),
|
||||
Column('action',
|
||||
String(length=255, convert_unicode=False,
|
||||
assert_unicode=None,
|
||||
unicode_error=None, _warn_on_bytestring=False)),
|
||||
Column('error',
|
||||
Text(length=None, convert_unicode=False,
|
||||
assert_unicode=None,
|
||||
unicode_error=None, _warn_on_bytestring=False)),
|
||||
)
|
||||
instance_actions.create()
|
|
@ -300,15 +300,6 @@ class InstanceInfoCache(BASE, NovaBase):
|
|||
primaryjoin=instance_id == Instance.uuid)
|
||||
|
||||
|
||||
class InstanceActions(BASE, NovaBase):
|
||||
"""Represents a guest VM's actions and results"""
|
||||
__tablename__ = "instance_actions"
|
||||
id = Column(Integer, primary_key=True)
|
||||
instance_uuid = Column(String(36), ForeignKey('instances.uuid'))
|
||||
action = Column(String(255))
|
||||
error = Column(Text)
|
||||
|
||||
|
||||
class InstanceTypes(BASE, NovaBase):
|
||||
"""Represent possible instance_types or flavor of VM offered"""
|
||||
__tablename__ = "instance_types"
|
||||
|
@ -1051,7 +1042,6 @@ def register_models():
|
|||
FixedIp,
|
||||
FloatingIp,
|
||||
Instance,
|
||||
InstanceActions,
|
||||
InstanceFault,
|
||||
InstanceMetadata,
|
||||
InstanceTypeExtraSpecs,
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
# Copyright 2011 Eldar Nugaev
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 datetime
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from nova.api.openstack import compute
|
||||
from nova.api.openstack.compute import extensions
|
||||
from nova.api.openstack.compute.contrib import server_action_list
|
||||
from nova.api.openstack import wsgi
|
||||
import nova.compute
|
||||
from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
import nova.utils
|
||||
|
||||
|
||||
dt = datetime.datetime.utcnow()
|
||||
|
||||
|
||||
def fake_get_actions(self, _context, instance_uuid):
|
||||
return [
|
||||
{'action': 'rebuild', 'error': None, 'created_at': dt},
|
||||
{'action': 'reboot', 'error': 'Failed!', 'created_at': dt},
|
||||
]
|
||||
|
||||
|
||||
def fake_instance_get(self, _context, instance_uuid):
|
||||
return {'uuid': instance_uuid}
|
||||
|
||||
|
||||
class ServerActionsTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ServerActionsTest, self).setUp()
|
||||
self.flags(verbose=True)
|
||||
self.stubs.Set(nova.compute.API, 'get_actions', fake_get_actions)
|
||||
self.stubs.Set(nova.compute.API, 'get', fake_instance_get)
|
||||
|
||||
self.router = compute.APIRouter()
|
||||
|
||||
def test_get_actions(self):
|
||||
uuid = nova.utils.gen_uuid()
|
||||
req = fakes.HTTPRequest.blank('/fake/servers/%s/actions' % uuid)
|
||||
res = req.get_response(self.router)
|
||||
output = json.loads(res.body)
|
||||
expected = {'actions': [
|
||||
{'action': 'rebuild', 'error': None, 'created_at': str(dt)},
|
||||
{'action': 'reboot', 'error': 'Failed!', 'created_at': str(dt)},
|
||||
]}
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
|
||||
class TestServerActionsXMLSerializer(unittest.TestCase):
|
||||
namespace = wsgi.XMLNS_V11
|
||||
|
||||
def _tag(self, elem):
|
||||
tagname = elem.tag
|
||||
self.assertEqual(tagname[0], '{')
|
||||
tmp = tagname.partition('}')
|
||||
namespace = tmp[0][1:]
|
||||
self.assertEqual(namespace, self.namespace)
|
||||
return tmp[2]
|
||||
|
||||
def test_index_serializer(self):
|
||||
serializer = server_action_list.ServerActionsTemplate()
|
||||
exemplar = [dict(
|
||||
created_at=datetime.datetime.now(),
|
||||
action='foo',
|
||||
error='quxx'),
|
||||
dict(
|
||||
created_at=datetime.datetime.now(),
|
||||
action='bar',
|
||||
error='xxuq')]
|
||||
text = serializer.serialize(dict(actions=exemplar))
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self.assertEqual('actions', self._tag(tree))
|
||||
self.assertEqual(len(tree), len(exemplar))
|
||||
for idx, child in enumerate(tree):
|
||||
self.assertEqual('action', self._tag(child))
|
||||
for field in ('created_at', 'action', 'error'):
|
||||
self.assertEqual(str(exemplar[idx][field]), child.get(field))
|
|
@ -180,7 +180,6 @@ class ExtensionControllerTest(ExtensionTestCase):
|
|||
"Rescue",
|
||||
"SchedulerHints",
|
||||
"SecurityGroups",
|
||||
"ServerActionList",
|
||||
"ServerDiagnostics",
|
||||
"ServerStartStop",
|
||||
"SimpleTenantUsage",
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
"compute:delete_instance_metadata": [],
|
||||
|
||||
"compute:get_instance_faults": [],
|
||||
"compute:get_actions": [],
|
||||
"compute:get_diagnostics": [],
|
||||
|
||||
"compute:get_lock": [],
|
||||
|
@ -104,7 +103,6 @@
|
|||
"compute_extension:quota_classes": [],
|
||||
"compute_extension:rescue": [],
|
||||
"compute_extension:security_groups": [],
|
||||
"compute_extension:server_action_list": [],
|
||||
"compute_extension:server_diagnostics": [],
|
||||
"compute_extension:simple_tenant_usage:show": [],
|
||||
"compute_extension:simple_tenant_usage:list": [],
|
||||
|
|
|
@ -3362,27 +3362,6 @@ class ComputeAPITestCase(BaseTestCase):
|
|||
self.compute_api.get_diagnostics(self.context, instance)
|
||||
self.compute_api.delete(self.context, instance)
|
||||
|
||||
def test_get_actions(self):
|
||||
|
||||
expected = [{'id': 1,
|
||||
'instance_id': 5,
|
||||
'action': 'rebuild',
|
||||
'error': '',
|
||||
}]
|
||||
|
||||
def fake_get_actions(context, instance):
|
||||
return expected
|
||||
|
||||
self.stubs.Set(nova.db, 'instance_get_actions',
|
||||
fake_get_actions)
|
||||
|
||||
instance = self._create_fake_instance()
|
||||
context = self.context.elevated()
|
||||
actual = self.compute_api.get_actions(context, instance)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
self.compute_api.delete(self.context, instance)
|
||||
|
||||
def test_inject_file(self):
|
||||
"""Ensure we can write a file to an instance"""
|
||||
instance = self._create_fake_instance()
|
||||
|
|
|
@ -92,10 +92,6 @@ def stub_out_db_instance_api(stubs):
|
|||
'vlan': 100}
|
||||
return FakeModel(fields)
|
||||
|
||||
def fake_instance_action_create(context, action):
|
||||
"""Stubs out the db.instance_action_create method."""
|
||||
pass
|
||||
|
||||
def fake_instance_type_get_all(context, inactive=0, filters=None):
|
||||
return INSTANCE_TYPES.values()
|
||||
|
||||
|
@ -104,6 +100,5 @@ def stub_out_db_instance_api(stubs):
|
|||
|
||||
stubs.Set(db, 'instance_create', fake_instance_create)
|
||||
stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance)
|
||||
stubs.Set(db, 'instance_action_create', fake_instance_action_create)
|
||||
stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all)
|
||||
stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name)
|
||||
|
|
|
@ -389,10 +389,6 @@ class VMWareAPISession(object):
|
|||
task_info = self._call_method(vim_util, "get_dynamic_property",
|
||||
task_ref, "Task", "info")
|
||||
task_name = task_info.name
|
||||
action = dict(
|
||||
instance_uuid=instance_uuid,
|
||||
action=task_name[0:255],
|
||||
error=None)
|
||||
if task_info.state in ['queued', 'running']:
|
||||
return
|
||||
elif task_info.state == 'success':
|
||||
|
@ -401,11 +397,9 @@ class VMWareAPISession(object):
|
|||
done.send("success")
|
||||
else:
|
||||
error_info = str(task_info.error.localizedMessage)
|
||||
action["error"] = error_info
|
||||
LOG.warn(_("Task [%(task_name)s] %(task_ref)s "
|
||||
"status: error %(error_info)s") % locals())
|
||||
done.send_exception(exception.NovaException(error_info))
|
||||
db.instance_action_create(context.get_admin_context(), action)
|
||||
except Exception, excep:
|
||||
LOG.warn(_("In vmwareapi:_poll_task, Got this error %s") % excep)
|
||||
done.send_exception(excep)
|
||||
|
|
Loading…
Reference in New Issue