Merge lp:~therve/landscape-client/ec2-no-ramdisk into lp:~landscape/landscape-client/trunk

Proposed by Thomas Herve
Status: Merged
Approved by: Sidnei da Silva
Approved revision: 242
Merged at revision: 248
Proposed branch: lp:~therve/landscape-client/ec2-no-ramdisk
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 274 lines (+64/-30)
3 files modified
landscape/broker/registration.py (+8/-2)
landscape/broker/tests/test_registration.py (+55/-27)
landscape/message_schemas.py (+1/-1)
To merge this branch: bzr merge lp:~therve/landscape-client/ec2-no-ramdisk
Reviewer Review Type Date Requested Status
Sidnei da Silva (community) Approve
Free Ekanayaka (community) Approve
Review via email: mp+24727@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Looks good! +1

[1]

+ deferred.addCallback(
+ lambda ignore: self._fetch_async(
+ EC2_API + "/meta-data/ramdisk-id").addErrback(log_failure))
+ deferred.addCallback(ec2_data.append)

Maybe it'd be worth adding a comment about why we need to special case ramdisk-id.

review: Approve
Revision history for this message
Andreas Hasenack (ahasenack) wrote :

I don't think it's working, or I did something wrong. The registration doesn't "stick". Attached is the full broker log of this client running on a lucid instance in the APAC region. Below are the last bits of my broker.log, but the pattern is the same all over the log:

2010-05-05 14:43:40,370 INFO [MainThread] Broker stopped with config /etc/landscape/client.conf
2010-05-05 14:43:41,865 INFO [MainThread] Broker started with config /etc/landscape/client.conf
2010-05-05 14:43:41,881 ERROR [MainThread] None
Traceback (most recent call last):
  File "/usr/lib/python2.6/dist-packages/twisted/python/threadpool.py", line 210, in _worker
    result = context.call(ctx, function, *args, **kwargs)
  File "/usr/lib/python2.6/dist-packages/twisted/python/context.py", line 59, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "/usr/lib/python2.6/dist-packages/twisted/python/context.py", line 37, in callWithContext
    return func(*args,**kw)
  File "/usr/lib/python2.6/dist-packages/landscape/lib/fetch.py", line 98, in fetch
    raise HTTPCodeError(http_code, body)
HTTPCodeError: Server returned HTTP code 404
2010-05-05 14:44:41,870 INFO [MainThread] Queueing message to register with OTP
2010-05-05 14:44:41,872 INFO [MainThread] Starting urgent message exchange with https://staging.landscape.canonical.com/message-system.
2010-05-05 14:44:44,462 INFO [Dummy-1 ] Sent 1146 bytes and received 70 bytes in 2.59s.
2010-05-05 14:44:44,466 INFO [MainThread] Switching to normal exchange mode.
2010-05-05 14:44:44,466 INFO [MainThread] Server UUID changed (old=af6f2dcf-1967-11de-8dd0-001a4b4d8d10, new=7c6efe47-12fb-11de-91bb-0016353c8658).
2010-05-05 14:44:44,467 INFO [MainThread] Message exchange completed in 2.60s.
2010-05-05 14:59:44,474 INFO [MainThread] Queueing message to register with OTP
2010-05-05 14:59:44,477 INFO [MainThread] Starting message exchange with https://staging.landscape.canonical.com/message-system.
2010-05-05 14:59:47,343 INFO [Dummy-1 ] Sent 1146 bytes and received 70 bytes in 2.87s.
2010-05-05 14:59:47,348 INFO [MainThread] Message exchange completed in 2.87s.

Revision history for this message
Thomas Herve (therve) wrote :

It can't work against staging or production because I updated the message schema to allow the ramdisk to be None.

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

Cool. Works fine after I start a trunk server using this client branch.

Revision history for this message
Sidnei da Silva (sidnei) wrote :

Looks good. Please add a comment as per Free's request. +1.

review: Approve
243. By Thomas Herve

Add comment

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'landscape/broker/registration.py'
2--- landscape/broker/registration.py 2010-04-28 13:07:53 +0000
3+++ landscape/broker/registration.py 2010-05-11 09:40:57 +0000
4@@ -146,19 +146,23 @@
5 "/meta-data/public-hostname",
6 "/meta-data/ami-launch-index",
7 "/meta-data/kernel-id",
8- "/meta-data/ramdisk-id",
9 "/meta-data/ami-id"]
10 # We're not using a DeferredList here because we want to keep the
11 # number of connections to the backend minimal. See lp:567515.
12 for path in paths:
13 deferred.addCallback(
14 lambda ignore, path=path: self._get_data(path, ec2_data))
15+ # Special case the ramdisk retrieval, because it may not be present
16+ deferred.addCallback(
17+ lambda ignore: self._fetch_async(
18+ EC2_API + "/meta-data/ramdisk-id").addErrback(log_failure))
19+ deferred.addCallback(ec2_data.append)
20
21 def record_data(ignore):
22 """Record the instance data returned by the EC2 API."""
23 (raw_user_data, instance_key, reservation_key,
24 local_hostname, public_hostname, launch_index,
25- kernel_key, ramdisk_key, ami_key) = ec2_data
26+ kernel_key, ami_key, ramdisk_key) = ec2_data
27 self._ec2_data = {
28 "instance_key": instance_key,
29 "reservation_key": reservation_key,
30@@ -169,6 +173,8 @@
31 "ramdisk_key": ramdisk_key,
32 "image_key": ami_key}
33 for k, v in self._ec2_data.items():
34+ if v is None and k == "ramdisk_key":
35+ continue
36 self._ec2_data[k] = v.decode("utf-8")
37 self._ec2_data["launch_index"] = int(
38 self._ec2_data["launch_index"])
39
40=== modified file 'landscape/broker/tests/test_registration.py'
41--- landscape/broker/tests/test_registration.py 2010-01-06 15:11:24 +0000
42+++ landscape/broker/tests/test_registration.py 2010-05-11 09:40:57 +0000
43@@ -35,8 +35,9 @@
44 self.assertEquals(getattr(self.identity, attr), value,
45 "%r attribute should be %r, not %r" %
46 (attr, value, getattr(self.identity, attr)))
47- self.assertEquals(self.persist.get(persist_name), value,
48- "%r not set to %r in persist" % (persist_name, value))
49+ self.assertEquals(
50+ self.persist.get(persist_name), value,
51+ "%r not set to %r in persist" % (persist_name, value))
52
53 def check_config_property(self, attr):
54 value = "VALUE"
55@@ -151,8 +152,7 @@
56 "account_name": "account_name",
57 "registration_password": None,
58 "hostname": "ooga.local",
59- "tags": None,}
60- ])
61+ "tags": None}])
62 self.assertEquals(self.logfile.getvalue().strip(),
63 "INFO: Queueing message to register with account "
64 "'account_name' without a password.")
65@@ -170,8 +170,7 @@
66 "account_name": "account_name",
67 "registration_password": "SEKRET",
68 "hostname": "ooga.local",
69- "tags": None,}
70- ])
71+ "tags": None}])
72 self.assertEquals(self.logfile.getvalue().strip(),
73 "INFO: Queueing message to register with account "
74 "'account_name' with a password.")
75@@ -193,8 +192,7 @@
76 "account_name": "account_name",
77 "registration_password": "SEKRET",
78 "hostname": "ooga.local",
79- "tags": u"computer,tag"}
80- ])
81+ "tags": u"computer,tag"}])
82 self.assertEquals(self.logfile.getvalue().strip(),
83 "INFO: Queueing message to register with account "
84 "'account_name' and tags computer,tag "
85@@ -219,8 +217,7 @@
86 "account_name": "account_name",
87 "registration_password": "SEKRET",
88 "hostname": "ooga.local",
89- "tags": None}
90- ])
91+ "tags": None}])
92 self.assertEquals(self.logfile.getvalue().strip(),
93 "ERROR: Invalid tags provided for cloud "
94 "registration.\n "
95@@ -238,14 +235,14 @@
96 self.config.registration_password = "SEKRET"
97 self.config.tags = u"prova\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}o"
98 self.reactor.fire("pre-exchange")
99- self.assertMessages(self.mstore.get_pending_messages(),
100- [{"type": "register",
101- "computer_title": "Computer Title",
102- "account_name": "account_name",
103- "registration_password": "SEKRET",
104- "hostname": "ooga.local",
105- "tags": u"prova\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}o"}
106- ])
107+ self.assertMessages(
108+ self.mstore.get_pending_messages(),
109+ [{"type": "register",
110+ "computer_title": "Computer Title",
111+ "account_name": "account_name",
112+ "registration_password": "SEKRET",
113+ "hostname": "ooga.local",
114+ "tags": u"prova\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}o"}])
115 self.assertEquals(self.logfile.getvalue().strip(),
116 "INFO: Queueing message to register with account "
117 "'account_name' and tags prova\xc4\xb5o "
118@@ -333,9 +330,11 @@
119
120 calls = [0]
121 d = self.handler.register()
122+
123 def add_call(result):
124 self.assertEquals(result, None)
125 calls[0] += 1
126+
127 d.addCallback(add_call)
128
129 # This should somehow callback the deferred.
130@@ -355,8 +354,10 @@
131 def test_resynchronize_fired_when_registration_done(self):
132
133 results = []
134+
135 def append():
136 results.append(True)
137+
138 self.reactor.call_on("resynchronize-clients", append)
139
140 self.handler.register()
141@@ -373,10 +374,12 @@
142
143 calls = [0]
144 d = self.handler.register()
145+
146 def add_call(failure):
147 exception = failure.value
148 self.assertTrue(isinstance(exception, InvalidCredentialsError))
149 calls[0] += 1
150+
151 d.addErrback(add_call)
152
153 # This should somehow callback the deferred.
154@@ -428,8 +431,7 @@
155 "account_name": "account_name",
156 "registration_password": "SEKRET",
157 "hostname": socket.getfqdn(),
158- "tags": None,}
159- ])
160+ "tags": None}])
161
162
163 class CloudRegistrationHandlerTest(RegistrationHandlerTestBase):
164@@ -550,9 +552,10 @@
165 # This *should* be asynchronous, but I think a billion tests are
166 # written like this
167 self.assertEquals(len(self.transport.payloads), 1)
168- self.assertMessages(self.transport.payloads[0]["messages"],
169- [self.get_expected_cloud_message(tags=u"server,london")])
170-
171+ self.assertMessages(
172+ self.transport.payloads[0]["messages"],
173+ [self.get_expected_cloud_message(tags=u"server,london")])
174+
175 def test_cloud_registration_with_invalid_tags(self):
176 """
177 Invalid tags in the configuration should result in the tags not being
178@@ -694,7 +697,8 @@
179 self.prepare_cloud_registration()
180
181 failed = []
182- self.reactor.call_on("registration-failed", lambda: failed.append(True))
183+ self.reactor.call_on(
184+ "registration-failed", lambda: failed.append(True))
185
186 self.log_helper.ignore_errors("Got error while fetching meta-data")
187 self.reactor.fire("run")
188@@ -724,6 +728,24 @@
189 account_name=u"onward",
190 registration_password=u"password")])
191
192+ def test_cloud_registration_continues_without_ramdisk(self):
193+ """
194+ If the instance doesn't have a ramdisk (ie, the query for ramdisk
195+ returns a 404), then register-cloud-vm still occurs.
196+ """
197+ self.log_helper.ignore_errors(HTTPCodeError)
198+ self.prepare_query_results(ramdisk_key=HTTPCodeError(404, "ohno"))
199+ self.prepare_cloud_registration()
200+
201+ self.reactor.fire("run")
202+ self.exchanger.exchange()
203+ self.assertIn("HTTPCodeError: Server returned HTTP code 404",
204+ self.logfile.getvalue())
205+ self.assertEquals(len(self.transport.payloads), 1)
206+ self.assertMessages(self.transport.payloads[0]["messages"],
207+ [self.get_expected_cloud_message(
208+ ramdisk_key=None)])
209+
210 def test_fall_back_to_normal_registration_when_metadata_fetch_fails(self):
211 """
212 If fetching metadata fails, but we do have an account name, then we
213@@ -747,7 +769,7 @@
214 "account_name": u"onward",
215 "registration_password": u"password",
216 "hostname": socket.getfqdn(),
217- "tags": None,}])
218+ "tags": None}])
219
220 def test_should_register_in_cloud(self):
221 """
222@@ -830,8 +852,9 @@
223
224 def test_is_managed(self):
225 """
226- L{is_cloud_managed} returns True if the EC2 user-data contains Landscape
227- instance information. It fetches the EC2 data with low timeouts.
228+ L{is_cloud_managed} returns True if the EC2 user-data contains
229+ Landscape instance information. It fetches the EC2 data with low
230+ timeouts.
231 """
232 user_data = {"otps": ["otp1"], "exchange-url": "http://exchange",
233 "ping-url": "http://ping"}
234@@ -890,15 +913,19 @@
235 self.assertFalse(is_cloud_managed(self.fake_fetch))
236
237 def test_is_managed_fetch_not_found(self):
238+
239 def fake_fetch(url, connect_timeout=None):
240 raise HTTPCodeError(404, "ohnoes")
241+
242 self.mock_socket()
243 self.mocker.replay()
244 self.assertFalse(is_cloud_managed(fake_fetch))
245
246 def test_is_managed_fetch_error(self):
247+
248 def fake_fetch(url, connect_timeout=None):
249 raise FetchError(7, "couldn't connect to host")
250+
251 self.mock_socket()
252 self.mocker.replay()
253 self.assertFalse(is_cloud_managed(fake_fetch))
254@@ -930,6 +957,7 @@
255 """
256 We'll only wait five minutes for the network to come up.
257 """
258+
259 def fake_fetch(url, connect_timeout=None):
260 raise FetchError(7, "couldn't connect to host")
261
262
263=== modified file 'landscape/message_schemas.py'
264--- landscape/message_schemas.py 2010-04-26 21:55:00 +0000
265+++ landscape/message_schemas.py 2010-05-11 09:40:57 +0000
266@@ -153,7 +153,7 @@
267 "public_hostname": Unicode(),
268 "local_hostname": Unicode(),
269 "kernel_key": Unicode(),
270- "ramdisk_key": Unicode(),
271+ "ramdisk_key": Any(Unicode(), Constant(None)),
272 "launch_index": Int(),
273 "image_key": Unicode(),
274 "tags": Any(utf8, Constant(None))},

Subscribers

People subscribed via source and target branches

to all changes: