/
server_external_events.py
138 lines (115 loc) · 5.23 KB
/
server_external_events.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# Copyright 2014 Red Hat, Inc.
#
# 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 oslo_log import log as logging
import webob
from nova.api.openstack.compute.schemas import server_external_events
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api import validation
from nova import compute
from nova import exception
from nova.i18n import _
from nova.i18n import _LI
from nova import objects
from nova.policies import server_external_events as see_policies
LOG = logging.getLogger(__name__)
ALIAS = 'os-server-external-events'
class ServerExternalEventsController(wsgi.Controller):
def __init__(self):
self.compute_api = compute.API()
super(ServerExternalEventsController, self).__init__()
@extensions.expected_errors((400, 403, 404))
@wsgi.response(200)
@validation.schema(server_external_events.create)
def create(self, req, body):
"""Creates a new instance event."""
context = req.environ['nova.context']
context.can(see_policies.POLICY_ROOT % 'create')
response_events = []
accepted_events = []
accepted_instances = set()
instances = {}
result = 200
body_events = body['events']
for _event in body_events:
client_event = dict(_event)
event = objects.InstanceExternalEvent(context)
event.instance_uuid = client_event.pop('server_uuid')
event.name = client_event.pop('name')
event.status = client_event.pop('status', 'completed')
event.tag = client_event.pop('tag', None)
instance = instances.get(event.instance_uuid)
if not instance:
try:
# Load migration_context here in a single DB operation
# because we need it later on
instance = objects.Instance.get_by_uuid(
context, event.instance_uuid,
expected_attrs='migration_context')
instances[event.instance_uuid] = instance
except exception.InstanceNotFound:
LOG.debug('Dropping event %(name)s:%(tag)s for unknown '
'instance %(instance_uuid)s',
{'name': event.name, 'tag': event.tag,
'instance_uuid': event.instance_uuid})
_event['status'] = 'failed'
_event['code'] = 404
result = 207
# NOTE: before accepting the event, make sure the instance
# for which the event is sent is assigned to a host; otherwise
# it will not be possible to dispatch the event
if instance:
if instance.host:
accepted_events.append(event)
accepted_instances.add(instance)
LOG.info(_LI('Creating event %(name)s:%(tag)s for '
'instance %(instance_uuid)s on %(host)s'),
{'name': event.name, 'tag': event.tag,
'instance_uuid': event.instance_uuid,
'host': instance.host})
# NOTE: as the event is processed asynchronously verify
# whether 202 is a more suitable response code than 200
_event['status'] = 'completed'
_event['code'] = 200
else:
LOG.debug("Unable to find a host for instance "
"%(instance)s. Dropping event %(event)s",
{'instance': event.instance_uuid,
'event': event.name})
_event['status'] = 'failed'
_event['code'] = 422
result = 207
response_events.append(_event)
if accepted_events:
self.compute_api.external_instance_event(
context, accepted_instances, accepted_events)
else:
msg = _('No instances found for any event')
raise webob.exc.HTTPNotFound(explanation=msg)
# FIXME(cyeoh): This needs some infrastructure support so that
# we have a general way to do this
robj = wsgi.ResponseObject({'events': response_events})
robj._code = result
return robj
class ServerExternalEvents(extensions.V21APIExtensionBase):
"""Server External Event Triggers."""
name = "ServerExternalEvents"
alias = ALIAS
version = 1
def get_resources(self):
resource = extensions.ResourceExtension(ALIAS,
ServerExternalEventsController())
return [resource]
def get_controller_extensions(self):
return []