Merge lp:~sandy-walsh/nova/zones into lp:~hudson-openstack/nova/trunk
- zones
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Matt Dietz |
Approved revision: | 651 |
Merged at revision: | 696 |
Proposed branch: | lp:~sandy-walsh/nova/zones |
Merge into: | lp:~hudson-openstack/nova/trunk |
Diff against target: |
487 lines (+374/-9) 11 files modified
nova/api/openstack/__init__.py (+4/-0) nova/api/openstack/auth.py (+1/-0) nova/api/openstack/servers.py (+0/-2) nova/api/openstack/zones.py (+80/-0) nova/auth/novarc.template (+3/-4) nova/db/api.py (+28/-0) nova/db/sqlalchemy/api.py (+44/-0) nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py (+61/-0) nova/db/sqlalchemy/migration.py (+2/-2) nova/db/sqlalchemy/models.py (+10/-1) nova/tests/api/openstack/test_zones.py (+141/-0) |
To merge this branch: | bzr merge lp:~sandy-walsh/nova/zones |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Matt Dietz (community) | Approve | ||
Eric Day (community) | Approve | ||
Jay Pipes (community) | Needs Fixing | ||
Review via email: mp+49730@code.launchpad.net |
Commit message
Added http://
Description of the change
This is the first phase of the multi-zone clustering. Right now it's just the API/DB stuff for adding/
Zones are just URL + username/password credentials of child zones.
python-novatools (on github) was also updated to support these changes.
Next we'll get these zones talking to each other.
Todd Willey (xtoddx) wrote : | # |
Eric Day (eday) wrote : | # |
I would s/ChildZone/Zone/ for consistent naming. Having the API deal with 'zones' and the DB deal with 'child zones' may make someone think they are not the same.
Sandy Walsh (sandy-walsh) wrote : | # |
Thanks for the catch todd. Missed that on my diff.
Eric, yeah, I debated that one. I agree & will change. Thanks.
- 646. By Sandy Walsh
-
fixed nova-combined debug hack and renamed ChildZone to Zone
- 647. By Sandy Walsh
-
better filtering
- 648. By Sandy Walsh
-
trunk merge
Jay Pipes (jaypipes) wrote : | # |
Hi! Great work so far, Sandy.
A couple notes:
1) 223 === added file 'nova/db/
I think we wanted to go with >1 migrate scripts per release, with a migration script for each patch that makes changes to the underlying database schema.
So, I'd recommend s/003_cactus.
2) Looks like you are still calling things child zones in the migration script and in the nova.db.api docstrings...
Other than those little things, lgtm.
- 649. By Sandy Walsh
-
fixed / renamed migration scripts
- 650. By Sandy Walsh
-
changed from 003-004 migration
Matt Dietz (cerberus) wrote : | # |
Whoops, missed the NASA copyright in the migration file. Should be the Openstack one
- 651. By Sandy Walsh
-
copyright notice
Preview Diff
1 | === modified file 'nova/api/openstack/__init__.py' | |||
2 | --- nova/api/openstack/__init__.py 2011-02-16 20:12:54 +0000 | |||
3 | +++ nova/api/openstack/__init__.py 2011-02-17 21:54:13 +0000 | |||
4 | @@ -34,6 +34,7 @@ | |||
5 | 34 | from nova.api.openstack import images | 34 | from nova.api.openstack import images |
6 | 35 | from nova.api.openstack import servers | 35 | from nova.api.openstack import servers |
7 | 36 | from nova.api.openstack import shared_ip_groups | 36 | from nova.api.openstack import shared_ip_groups |
8 | 37 | from nova.api.openstack import zones | ||
9 | 37 | 38 | ||
10 | 38 | 39 | ||
11 | 39 | LOG = logging.getLogger('nova.api.openstack') | 40 | LOG = logging.getLogger('nova.api.openstack') |
12 | @@ -81,6 +82,9 @@ | |||
13 | 81 | server_members['resume'] = 'POST' | 82 | server_members['resume'] = 'POST' |
14 | 82 | server_members['reset_network'] = 'POST' | 83 | server_members['reset_network'] = 'POST' |
15 | 83 | 84 | ||
16 | 85 | mapper.resource("zone", "zones", controller=zones.Controller(), | ||
17 | 86 | collection={'detail': 'GET'}) | ||
18 | 87 | |||
19 | 84 | mapper.resource("server", "servers", controller=servers.Controller(), | 88 | mapper.resource("server", "servers", controller=servers.Controller(), |
20 | 85 | collection={'detail': 'GET'}, | 89 | collection={'detail': 'GET'}, |
21 | 86 | member=server_members) | 90 | member=server_members) |
22 | 87 | 91 | ||
23 | === modified file 'nova/api/openstack/auth.py' | |||
24 | --- nova/api/openstack/auth.py 2011-01-06 18:57:48 +0000 | |||
25 | +++ nova/api/openstack/auth.py 2011-02-17 21:54:13 +0000 | |||
26 | @@ -19,6 +19,7 @@ | |||
27 | 19 | import hashlib | 19 | import hashlib |
28 | 20 | import json | 20 | import json |
29 | 21 | import time | 21 | import time |
30 | 22 | import logging | ||
31 | 22 | 23 | ||
32 | 23 | import webob.exc | 24 | import webob.exc |
33 | 24 | import webob.dec | 25 | import webob.dec |
34 | 25 | 26 | ||
35 | === modified file 'nova/api/openstack/servers.py' | |||
36 | --- nova/api/openstack/servers.py 2011-02-17 19:38:11 +0000 | |||
37 | +++ nova/api/openstack/servers.py 2011-02-17 21:54:13 +0000 | |||
38 | @@ -1,5 +1,3 @@ | |||
39 | 1 | # vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
40 | 2 | |||
41 | 3 | # Copyright 2010 OpenStack LLC. | 1 | # Copyright 2010 OpenStack LLC. |
42 | 4 | # All Rights Reserved. | 2 | # All Rights Reserved. |
43 | 5 | # | 3 | # |
44 | 6 | 4 | ||
45 | === added file 'nova/api/openstack/zones.py' | |||
46 | --- nova/api/openstack/zones.py 1970-01-01 00:00:00 +0000 | |||
47 | +++ nova/api/openstack/zones.py 2011-02-17 21:54:13 +0000 | |||
48 | @@ -0,0 +1,80 @@ | |||
49 | 1 | # Copyright 2010 OpenStack LLC. | ||
50 | 2 | # All Rights Reserved. | ||
51 | 3 | # | ||
52 | 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
53 | 5 | # not use this file except in compliance with the License. You may obtain | ||
54 | 6 | # a copy of the License at | ||
55 | 7 | # | ||
56 | 8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
57 | 9 | # | ||
58 | 10 | # Unless required by applicable law or agreed to in writing, software | ||
59 | 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
60 | 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
61 | 13 | # License for the specific language governing permissions and limitations | ||
62 | 14 | # under the License. | ||
63 | 15 | |||
64 | 16 | import common | ||
65 | 17 | import logging | ||
66 | 18 | |||
67 | 19 | from nova import flags | ||
68 | 20 | from nova import wsgi | ||
69 | 21 | from nova import db | ||
70 | 22 | |||
71 | 23 | |||
72 | 24 | FLAGS = flags.FLAGS | ||
73 | 25 | |||
74 | 26 | |||
75 | 27 | def _filter_keys(item, keys): | ||
76 | 28 | """ | ||
77 | 29 | Filters all model attributes except for keys | ||
78 | 30 | item is a dict | ||
79 | 31 | |||
80 | 32 | """ | ||
81 | 33 | return dict((k, v) for k, v in item.iteritems() if k in keys) | ||
82 | 34 | |||
83 | 35 | |||
84 | 36 | def _scrub_zone(zone): | ||
85 | 37 | return _filter_keys(zone, ('id', 'api_url')) | ||
86 | 38 | |||
87 | 39 | |||
88 | 40 | class Controller(wsgi.Controller): | ||
89 | 41 | |||
90 | 42 | _serialization_metadata = { | ||
91 | 43 | 'application/xml': { | ||
92 | 44 | "attributes": { | ||
93 | 45 | "zone": ["id", "api_url"]}}} | ||
94 | 46 | |||
95 | 47 | def index(self, req): | ||
96 | 48 | """Return all zones in brief""" | ||
97 | 49 | items = db.zone_get_all(req.environ['nova.context']) | ||
98 | 50 | items = common.limited(items, req) | ||
99 | 51 | items = [_scrub_zone(item) for item in items] | ||
100 | 52 | return dict(zones=items) | ||
101 | 53 | |||
102 | 54 | def detail(self, req): | ||
103 | 55 | """Return all zones in detail""" | ||
104 | 56 | return self.index(req) | ||
105 | 57 | |||
106 | 58 | def show(self, req, id): | ||
107 | 59 | """Return data about the given zone id""" | ||
108 | 60 | zone_id = int(id) | ||
109 | 61 | zone = db.zone_get(req.environ['nova.context'], zone_id) | ||
110 | 62 | return dict(zone=_scrub_zone(zone)) | ||
111 | 63 | |||
112 | 64 | def delete(self, req, id): | ||
113 | 65 | zone_id = int(id) | ||
114 | 66 | db.zone_delete(req.environ['nova.context'], zone_id) | ||
115 | 67 | return {} | ||
116 | 68 | |||
117 | 69 | def create(self, req): | ||
118 | 70 | context = req.environ['nova.context'] | ||
119 | 71 | env = self._deserialize(req.body, req) | ||
120 | 72 | zone = db.zone_create(context, env["zone"]) | ||
121 | 73 | return dict(zone=_scrub_zone(zone)) | ||
122 | 74 | |||
123 | 75 | def update(self, req, id): | ||
124 | 76 | context = req.environ['nova.context'] | ||
125 | 77 | env = self._deserialize(req.body, req) | ||
126 | 78 | zone_id = int(id) | ||
127 | 79 | zone = db.zone_update(context, zone_id, env["zone"]) | ||
128 | 80 | return dict(zone=_scrub_zone(zone)) | ||
129 | 0 | 81 | ||
130 | === modified file 'nova/auth/novarc.template' | |||
131 | --- nova/auth/novarc.template 2011-01-04 01:06:50 +0000 | |||
132 | +++ nova/auth/novarc.template 2011-02-17 21:54:13 +0000 | |||
133 | @@ -10,7 +10,6 @@ | |||
134 | 10 | export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this set | 10 | export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this set |
135 | 11 | alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}" | 11 | alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}" |
136 | 12 | alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}" | 12 | alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}" |
141 | 13 | export CLOUD_SERVERS_API_KEY="%(access)s" | 13 | export NOVA_API_KEY="%(access)s" |
142 | 14 | export CLOUD_SERVERS_USERNAME="%(user)s" | 14 | export NOVA_USERNAME="%(user)s" |
143 | 15 | export CLOUD_SERVERS_URL="%(os)s" | 15 | export NOVA_URL="%(os)s" |
140 | 16 | |||
144 | 17 | 16 | ||
145 | === modified file 'nova/db/api.py' | |||
146 | --- nova/db/api.py 2011-02-17 19:32:30 +0000 | |||
147 | +++ nova/db/api.py 2011-02-17 21:54:13 +0000 | |||
148 | @@ -1000,3 +1000,31 @@ | |||
149 | 1000 | def console_get(context, console_id, instance_id=None): | 1000 | def console_get(context, console_id, instance_id=None): |
150 | 1001 | """Get a specific console (possibly on a given instance).""" | 1001 | """Get a specific console (possibly on a given instance).""" |
151 | 1002 | return IMPL.console_get(context, console_id, instance_id) | 1002 | return IMPL.console_get(context, console_id, instance_id) |
152 | 1003 | |||
153 | 1004 | |||
154 | 1005 | #################### | ||
155 | 1006 | |||
156 | 1007 | |||
157 | 1008 | def zone_create(context, values): | ||
158 | 1009 | """Create a new child Zone entry.""" | ||
159 | 1010 | return IMPL.zone_create(context, values) | ||
160 | 1011 | |||
161 | 1012 | |||
162 | 1013 | def zone_update(context, zone_id, values): | ||
163 | 1014 | """Update a child Zone entry.""" | ||
164 | 1015 | return IMPL.zone_update(context, values) | ||
165 | 1016 | |||
166 | 1017 | |||
167 | 1018 | def zone_delete(context, zone_id): | ||
168 | 1019 | """Delete a child Zone.""" | ||
169 | 1020 | return IMPL.zone_delete(context, zone_id) | ||
170 | 1021 | |||
171 | 1022 | |||
172 | 1023 | def zone_get(context, zone_id): | ||
173 | 1024 | """Get a specific child Zone.""" | ||
174 | 1025 | return IMPL.zone_get(context, zone_id) | ||
175 | 1026 | |||
176 | 1027 | |||
177 | 1028 | def zone_get_all(context): | ||
178 | 1029 | """Get all child Zones.""" | ||
179 | 1030 | return IMPL.zone_get_all(context) | ||
180 | 1003 | 1031 | ||
181 | === modified file 'nova/db/sqlalchemy/api.py' | |||
182 | --- nova/db/sqlalchemy/api.py 2011-02-17 19:32:30 +0000 | |||
183 | +++ nova/db/sqlalchemy/api.py 2011-02-17 21:54:13 +0000 | |||
184 | @@ -2058,3 +2058,47 @@ | |||
185 | 2058 | raise exception.NotFound(_("No console with id %(console_id)s" | 2058 | raise exception.NotFound(_("No console with id %(console_id)s" |
186 | 2059 | " %(idesc)s") % locals()) | 2059 | " %(idesc)s") % locals()) |
187 | 2060 | return result | 2060 | return result |
188 | 2061 | |||
189 | 2062 | |||
190 | 2063 | #################### | ||
191 | 2064 | |||
192 | 2065 | |||
193 | 2066 | @require_admin_context | ||
194 | 2067 | def zone_create(context, values): | ||
195 | 2068 | zone = models.Zone() | ||
196 | 2069 | zone.update(values) | ||
197 | 2070 | zone.save() | ||
198 | 2071 | return zone | ||
199 | 2072 | |||
200 | 2073 | |||
201 | 2074 | @require_admin_context | ||
202 | 2075 | def zone_update(context, zone_id, values): | ||
203 | 2076 | zone = session.query(models.Zone).filter_by(id=zone_id).first() | ||
204 | 2077 | if not zone: | ||
205 | 2078 | raise exception.NotFound(_("No zone with id %(zone_id)s") % locals()) | ||
206 | 2079 | zone.update(values) | ||
207 | 2080 | zone.save() | ||
208 | 2081 | return zone | ||
209 | 2082 | |||
210 | 2083 | |||
211 | 2084 | @require_admin_context | ||
212 | 2085 | def zone_delete(context, zone_id): | ||
213 | 2086 | session = get_session() | ||
214 | 2087 | with session.begin(): | ||
215 | 2088 | session.execute('delete from zones ' | ||
216 | 2089 | 'where id=:id', {'id': zone_id}) | ||
217 | 2090 | |||
218 | 2091 | |||
219 | 2092 | @require_admin_context | ||
220 | 2093 | def zone_get(context, zone_id): | ||
221 | 2094 | session = get_session() | ||
222 | 2095 | result = session.query(models.Zone).filter_by(id=zone_id).first() | ||
223 | 2096 | if not result: | ||
224 | 2097 | raise exception.NotFound(_("No zone with id %(zone_id)s") % locals()) | ||
225 | 2098 | return result | ||
226 | 2099 | |||
227 | 2100 | |||
228 | 2101 | @require_admin_context | ||
229 | 2102 | def zone_get_all(context): | ||
230 | 2103 | session = get_session() | ||
231 | 2104 | return session.query(models.Zone).all() | ||
232 | 2061 | 2105 | ||
233 | === added file 'nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py' | |||
234 | --- nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py 1970-01-01 00:00:00 +0000 | |||
235 | +++ nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py 2011-02-17 21:54:13 +0000 | |||
236 | @@ -0,0 +1,61 @@ | |||
237 | 1 | # Copyright 2010 OpenStack LLC. | ||
238 | 2 | # All Rights Reserved. | ||
239 | 3 | # | ||
240 | 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
241 | 5 | # not use this file except in compliance with the License. You may obtain | ||
242 | 6 | # a copy of the License at | ||
243 | 7 | # | ||
244 | 8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
245 | 9 | # | ||
246 | 10 | # Unless required by applicable law or agreed to in writing, software | ||
247 | 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
248 | 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
249 | 13 | # License for the specific language governing permissions and limitations | ||
250 | 14 | # under the License. | ||
251 | 15 | |||
252 | 16 | from sqlalchemy import * | ||
253 | 17 | from migrate import * | ||
254 | 18 | |||
255 | 19 | from nova import log as logging | ||
256 | 20 | |||
257 | 21 | |||
258 | 22 | meta = MetaData() | ||
259 | 23 | |||
260 | 24 | |||
261 | 25 | # | ||
262 | 26 | # New Tables | ||
263 | 27 | # | ||
264 | 28 | zones = Table('zones', meta, | ||
265 | 29 | Column('created_at', DateTime(timezone=False)), | ||
266 | 30 | Column('updated_at', DateTime(timezone=False)), | ||
267 | 31 | Column('deleted_at', DateTime(timezone=False)), | ||
268 | 32 | Column('deleted', Boolean(create_constraint=True, name=None)), | ||
269 | 33 | Column('id', Integer(), primary_key=True, nullable=False), | ||
270 | 34 | Column('api_url', | ||
271 | 35 | String(length=255, convert_unicode=False, assert_unicode=None, | ||
272 | 36 | unicode_error=None, _warn_on_bytestring=False)), | ||
273 | 37 | Column('username', | ||
274 | 38 | String(length=255, convert_unicode=False, assert_unicode=None, | ||
275 | 39 | unicode_error=None, _warn_on_bytestring=False)), | ||
276 | 40 | Column('password', | ||
277 | 41 | String(length=255, convert_unicode=False, assert_unicode=None, | ||
278 | 42 | unicode_error=None, _warn_on_bytestring=False)), | ||
279 | 43 | ) | ||
280 | 44 | |||
281 | 45 | |||
282 | 46 | # | ||
283 | 47 | # Tables to alter | ||
284 | 48 | # | ||
285 | 49 | |||
286 | 50 | # (none currently) | ||
287 | 51 | |||
288 | 52 | |||
289 | 53 | def upgrade(migrate_engine): | ||
290 | 54 | # Upgrade operations go here. Don't create your own engine; | ||
291 | 55 | # bind migrate_engine to your metadata | ||
292 | 56 | meta.bind = migrate_engine | ||
293 | 57 | for table in (zones, ): | ||
294 | 58 | try: | ||
295 | 59 | table.create() | ||
296 | 60 | except Exception: | ||
297 | 61 | logging.info(repr(table)) | ||
298 | 0 | 62 | ||
299 | === modified file 'nova/db/sqlalchemy/migration.py' | |||
300 | --- nova/db/sqlalchemy/migration.py 2011-02-16 18:24:46 +0000 | |||
301 | +++ nova/db/sqlalchemy/migration.py 2011-02-17 21:54:13 +0000 | |||
302 | @@ -55,8 +55,8 @@ | |||
303 | 55 | engine = sqlalchemy.create_engine(FLAGS.sql_connection, echo=False) | 55 | engine = sqlalchemy.create_engine(FLAGS.sql_connection, echo=False) |
304 | 56 | meta.reflect(bind=engine) | 56 | meta.reflect(bind=engine) |
305 | 57 | try: | 57 | try: |
308 | 58 | for table in ('auth_tokens', 'export_devices', 'fixed_ips', | 58 | for table in ('auth_tokens', 'zones', 'export_devices', |
309 | 59 | 'floating_ips', 'instances', | 59 | 'fixed_ips', 'floating_ips', 'instances', |
310 | 60 | 'key_pairs', 'networks', 'projects', 'quotas', | 60 | 'key_pairs', 'networks', 'projects', 'quotas', |
311 | 61 | 'security_group_instance_association', | 61 | 'security_group_instance_association', |
312 | 62 | 'security_group_rules', 'security_groups', | 62 | 'security_group_rules', 'security_groups', |
313 | 63 | 63 | ||
314 | === modified file 'nova/db/sqlalchemy/models.py' | |||
315 | --- nova/db/sqlalchemy/models.py 2011-02-14 23:18:59 +0000 | |||
316 | +++ nova/db/sqlalchemy/models.py 2011-02-17 21:54:13 +0000 | |||
317 | @@ -536,6 +536,15 @@ | |||
318 | 536 | pool = relationship(ConsolePool, backref=backref('consoles')) | 536 | pool = relationship(ConsolePool, backref=backref('consoles')) |
319 | 537 | 537 | ||
320 | 538 | 538 | ||
321 | 539 | class Zone(BASE, NovaBase): | ||
322 | 540 | """Represents a child zone of this zone.""" | ||
323 | 541 | __tablename__ = 'zones' | ||
324 | 542 | id = Column(Integer, primary_key=True) | ||
325 | 543 | api_url = Column(String(255)) | ||
326 | 544 | username = Column(String(255)) | ||
327 | 545 | password = Column(String(255)) | ||
328 | 546 | |||
329 | 547 | |||
330 | 539 | def register_models(): | 548 | def register_models(): |
331 | 540 | """Register Models and create metadata. | 549 | """Register Models and create metadata. |
332 | 541 | 550 | ||
333 | @@ -548,7 +557,7 @@ | |||
334 | 548 | Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, | 557 | Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, |
335 | 549 | Network, SecurityGroup, SecurityGroupIngressRule, | 558 | Network, SecurityGroup, SecurityGroupIngressRule, |
336 | 550 | SecurityGroupInstanceAssociation, AuthToken, User, | 559 | SecurityGroupInstanceAssociation, AuthToken, User, |
338 | 551 | Project, Certificate, ConsolePool, Console) # , Image, Host | 560 | Project, Certificate, ConsolePool, Console, Zone) |
339 | 552 | engine = create_engine(FLAGS.sql_connection, echo=False) | 561 | engine = create_engine(FLAGS.sql_connection, echo=False) |
340 | 553 | for model in models: | 562 | for model in models: |
341 | 554 | model.metadata.create_all(engine) | 563 | model.metadata.create_all(engine) |
342 | 555 | 564 | ||
343 | === added file 'nova/tests/api/openstack/test_zones.py' | |||
344 | --- nova/tests/api/openstack/test_zones.py 1970-01-01 00:00:00 +0000 | |||
345 | +++ nova/tests/api/openstack/test_zones.py 2011-02-17 21:54:13 +0000 | |||
346 | @@ -0,0 +1,141 @@ | |||
347 | 1 | # Copyright 2010 OpenStack LLC. | ||
348 | 2 | # All Rights Reserved. | ||
349 | 3 | # | ||
350 | 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
351 | 5 | # not use this file except in compliance with the License. You may obtain | ||
352 | 6 | # a copy of the License at | ||
353 | 7 | # | ||
354 | 8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
355 | 9 | # | ||
356 | 10 | # Unless required by applicable law or agreed to in writing, software | ||
357 | 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
358 | 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
359 | 13 | # License for the specific language governing permissions and limitations | ||
360 | 14 | # under the License. | ||
361 | 15 | |||
362 | 16 | import unittest | ||
363 | 17 | |||
364 | 18 | import stubout | ||
365 | 19 | import webob | ||
366 | 20 | import json | ||
367 | 21 | |||
368 | 22 | import nova.db | ||
369 | 23 | from nova import context | ||
370 | 24 | from nova import flags | ||
371 | 25 | from nova.api.openstack import zones | ||
372 | 26 | from nova.tests.api.openstack import fakes | ||
373 | 27 | |||
374 | 28 | |||
375 | 29 | FLAGS = flags.FLAGS | ||
376 | 30 | FLAGS.verbose = True | ||
377 | 31 | |||
378 | 32 | |||
379 | 33 | def zone_get(context, zone_id): | ||
380 | 34 | return dict(id=1, api_url='http://foo.com', username='bob', | ||
381 | 35 | password='xxx') | ||
382 | 36 | |||
383 | 37 | |||
384 | 38 | def zone_create(context, values): | ||
385 | 39 | zone = dict(id=1) | ||
386 | 40 | zone.update(values) | ||
387 | 41 | return zone | ||
388 | 42 | |||
389 | 43 | |||
390 | 44 | def zone_update(context, zone_id, values): | ||
391 | 45 | zone = dict(id=zone_id, api_url='http://foo.com', username='bob', | ||
392 | 46 | password='xxx') | ||
393 | 47 | zone.update(values) | ||
394 | 48 | return zone | ||
395 | 49 | |||
396 | 50 | |||
397 | 51 | def zone_delete(context, zone_id): | ||
398 | 52 | pass | ||
399 | 53 | |||
400 | 54 | |||
401 | 55 | def zone_get_all(context): | ||
402 | 56 | return [ | ||
403 | 57 | dict(id=1, api_url='http://foo.com', username='bob', | ||
404 | 58 | password='xxx'), | ||
405 | 59 | dict(id=2, api_url='http://blah.com', username='alice', | ||
406 | 60 | password='qwerty') | ||
407 | 61 | ] | ||
408 | 62 | |||
409 | 63 | |||
410 | 64 | class ZonesTest(unittest.TestCase): | ||
411 | 65 | def setUp(self): | ||
412 | 66 | self.stubs = stubout.StubOutForTesting() | ||
413 | 67 | fakes.FakeAuthManager.auth_data = {} | ||
414 | 68 | fakes.FakeAuthDatabase.data = {} | ||
415 | 69 | fakes.stub_out_networking(self.stubs) | ||
416 | 70 | fakes.stub_out_rate_limiting(self.stubs) | ||
417 | 71 | fakes.stub_out_auth(self.stubs) | ||
418 | 72 | |||
419 | 73 | self.allow_admin = FLAGS.allow_admin_api | ||
420 | 74 | FLAGS.allow_admin_api = True | ||
421 | 75 | |||
422 | 76 | self.stubs.Set(nova.db, 'zone_get', zone_get) | ||
423 | 77 | self.stubs.Set(nova.db, 'zone_get_all', zone_get_all) | ||
424 | 78 | self.stubs.Set(nova.db, 'zone_update', zone_update) | ||
425 | 79 | self.stubs.Set(nova.db, 'zone_create', zone_create) | ||
426 | 80 | self.stubs.Set(nova.db, 'zone_delete', zone_delete) | ||
427 | 81 | |||
428 | 82 | def tearDown(self): | ||
429 | 83 | self.stubs.UnsetAll() | ||
430 | 84 | FLAGS.allow_admin_api = self.allow_admin | ||
431 | 85 | |||
432 | 86 | def test_get_zone_list(self): | ||
433 | 87 | req = webob.Request.blank('/v1.0/zones') | ||
434 | 88 | res = req.get_response(fakes.wsgi_app()) | ||
435 | 89 | res_dict = json.loads(res.body) | ||
436 | 90 | |||
437 | 91 | self.assertEqual(res.status_int, 200) | ||
438 | 92 | self.assertEqual(len(res_dict['zones']), 2) | ||
439 | 93 | |||
440 | 94 | def test_get_zone_by_id(self): | ||
441 | 95 | req = webob.Request.blank('/v1.0/zones/1') | ||
442 | 96 | res = req.get_response(fakes.wsgi_app()) | ||
443 | 97 | res_dict = json.loads(res.body) | ||
444 | 98 | |||
445 | 99 | self.assertEqual(res_dict['zone']['id'], 1) | ||
446 | 100 | self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') | ||
447 | 101 | self.assertFalse('password' in res_dict['zone']) | ||
448 | 102 | self.assertEqual(res.status_int, 200) | ||
449 | 103 | |||
450 | 104 | def test_zone_delete(self): | ||
451 | 105 | req = webob.Request.blank('/v1.0/zones/1') | ||
452 | 106 | res = req.get_response(fakes.wsgi_app()) | ||
453 | 107 | |||
454 | 108 | self.assertEqual(res.status_int, 200) | ||
455 | 109 | |||
456 | 110 | def test_zone_create(self): | ||
457 | 111 | body = dict(zone=dict(api_url='http://blah.zoo', username='fred', | ||
458 | 112 | password='fubar')) | ||
459 | 113 | req = webob.Request.blank('/v1.0/zones') | ||
460 | 114 | req.method = 'POST' | ||
461 | 115 | req.body = json.dumps(body) | ||
462 | 116 | |||
463 | 117 | res = req.get_response(fakes.wsgi_app()) | ||
464 | 118 | res_dict = json.loads(res.body) | ||
465 | 119 | |||
466 | 120 | self.assertEqual(res.status_int, 200) | ||
467 | 121 | self.assertEqual(res_dict['zone']['id'], 1) | ||
468 | 122 | self.assertEqual(res_dict['zone']['api_url'], 'http://blah.zoo') | ||
469 | 123 | self.assertFalse('username' in res_dict['zone']) | ||
470 | 124 | |||
471 | 125 | def test_zone_update(self): | ||
472 | 126 | body = dict(zone=dict(username='zeb', password='sneaky')) | ||
473 | 127 | req = webob.Request.blank('/v1.0/zones/1') | ||
474 | 128 | req.method = 'PUT' | ||
475 | 129 | req.body = json.dumps(body) | ||
476 | 130 | |||
477 | 131 | res = req.get_response(fakes.wsgi_app()) | ||
478 | 132 | res_dict = json.loads(res.body) | ||
479 | 133 | |||
480 | 134 | self.assertEqual(res.status_int, 200) | ||
481 | 135 | self.assertEqual(res_dict['zone']['id'], 1) | ||
482 | 136 | self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com') | ||
483 | 137 | self.assertFalse('username' in res_dict['zone']) | ||
484 | 138 | |||
485 | 139 | |||
486 | 140 | if __name__ == '__main__': | ||
487 | 141 | unittest.main() |
was the bin/nova-combined change accidental?