Merge lp:~free.ekanayaka/landscape-client/drop-cloud-registration into lp:~landscape/landscape-client/trunk

Proposed by Free Ekanayaka
Status: Merged
Approved by: Free Ekanayaka
Approved revision: 783
Merged at revision: 778
Proposed branch: lp:~free.ekanayaka/landscape-client/drop-cloud-registration
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 1179 lines (+12/-941)
11 files modified
debian/cloud-default.conf (+0/-7)
debian/landscape-client.init (+2/-18)
debian/landscape-client.install (+0/-1)
landscape/broker/config.py (+0/-6)
landscape/broker/registration.py (+3/-210)
landscape/broker/tests/test_registration.py (+3/-669)
landscape/configuration.py (+1/-1)
landscape/message_schemas.py (+3/-0)
landscape/tests/test_configuration.py (+0/-16)
scripts/landscape-is-cloud-managed (+0/-12)
setup.py (+0/-1)
To merge this branch: bzr merge lp:~free.ekanayaka/landscape-client/drop-cloud-registration
Reviewer Review Type Date Requested Status
Benji York (community) Approve
Geoff Teale (community) Approve
Review via email: mp+226993@code.launchpad.net

Commit message

Drop the cloud registration code and associated tests.

Description of the change

Drop the cloud registration code and associated tests.

To post a comment you must log in.
Revision history for this message
Geoff Teale (tealeg) wrote :

+1 - I see a lot of lint output, might want to take a look.

review: Approve
Revision history for this message
Benji York (benji) wrote :

So much deleteed code! It's a thing of beauty.

=== modified file 'landscape/broker/tests/test_registration.py'
 from landscape.broker.registration import (
- InvalidCredentialsError, RegistrationHandler, is_cloud_managed, EC2_HOST,
- EC2_API, Identity)
+ InvalidCredentialsError, Identity)

I can't remember if we're uptight about alphebetical import order, but
if we are, look above.

=== modified file 'landscape/message_schemas.py'
--- landscape/message_schemas.py 2014-06-09 08:18:05 +0000
+++ landscape/message_schemas.py 2014-07-16 10:28:53 +0000
@@ -211,6 +211,8 @@
     {"otp": Bytes()})

+# XXX The register-cloud-vm message is obsolete, it's kept around just to not
+# break older LDS releases that import it. Eventually it shall be dropped.
 REGISTER_CLOUD_VM = Message(
     "register-cloud-vm",
     {"hostname": Unicode(),

I guess we can bzr-blame the above, but a date would make it obvious
when we can drop the noop.

review: Approve
Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Benji: we don't maintain imports in alphabetical order for now, but it'd be probably a good thing to do, together to having them one per line instead of smashed together. That takes increases a bit the lines count but it easier to read and maintain (e.g. conflicts). Might be a good topic for the sprint if you care to add it.

About the date, I've added the last LDS version that has this imports: 14.07.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== removed file 'debian/cloud-default.conf'
--- debian/cloud-default.conf 2009-03-28 19:04:34 +0000
+++ debian/cloud-default.conf 1970-01-01 00:00:00 +0000
@@ -1,7 +0,0 @@
1[client]
2cloud = True
3url = https://landscape.canonical.com/message-system
4data_path = /var/lib/landscape/client
5ping_url = http://landscape.canonical.com/ping
6include_manager_plugins = ScriptExecution
7script_users = ALL
80
=== modified file 'debian/landscape-client.init'
--- debian/landscape-client.init 2012-09-05 15:37:17 +0000
+++ debian/landscape-client.init 2014-07-17 06:52:14 +0000
@@ -31,24 +31,8 @@
31 # This $RUN check should match the semantics of31 # This $RUN check should match the semantics of
32 # l.sysvconfig.SysVConfig.is_configured_to_run.32 # l.sysvconfig.SysVConfig.is_configured_to_run.
33 if [ $RUN -eq 0 ]; then33 if [ $RUN -eq 0 ]; then
34 if [ $CLOUD -eq 1 ]; then34 echo "$NAME is not configured, please run landscape-config."
35 if landscape-is-cloud-managed; then35 exit 0
36 # Install the cloud default configuration file
37 cp /usr/share/landscape/cloud-default.conf /etc/landscape/client.conf
38 # Override default file for not going in this conditional again at
39 # next startup
40 sed -i "s/^RUN=.*/RUN=1/" $LANDSCAPE_DEFAULTS
41 if ! grep -q "^RUN=" $LANDSCAPE_DEFAULTS; then
42 echo "RUN=1" >> $LANDSCAPE_DEFAULTS
43 fi
44 else
45 echo "$NAME is not configured, please run landscape-config."
46 exit 0
47 fi
48 else
49 echo "$NAME is not configured, please run landscape-config."
50 exit 0
51 fi
52 fi36 fi
53}37}
5438
5539
=== modified file 'debian/landscape-client.install'
--- debian/landscape-client.install 2013-05-16 09:15:13 +0000
+++ debian/landscape-client.install 2014-07-17 06:52:14 +0000
@@ -7,7 +7,6 @@
7usr/bin/landscape-package-changer7usr/bin/landscape-package-changer
8usr/bin/landscape-package-reporter8usr/bin/landscape-package-reporter
9usr/bin/landscape-release-upgrader9usr/bin/landscape-release-upgrader
10usr/bin/landscape-is-cloud-managed
11usr/bin/landscape-dbus-proxy10usr/bin/landscape-dbus-proxy
12usr/share/landscape/cloud-default.conf11usr/share/landscape/cloud-default.conf
13usr/lib/landscape12usr/lib/landscape
1413
=== modified file 'landscape/broker/config.py'
--- landscape/broker/config.py 2014-03-25 15:21:52 +0000
+++ landscape/broker/config.py 2014-07-17 06:52:14 +0000
@@ -32,8 +32,6 @@
32 - C{urgent_exchange_interval} (C{1*60})32 - C{urgent_exchange_interval} (C{1*60})
33 - C{http_proxy}33 - C{http_proxy}
34 - C{https_proxy}34 - C{https_proxy}
35 - C{cloud}
36 - C{otp}
37 - C{provisioning_otp}35 - C{provisioning_otp}
38 """36 """
39 parser = super(BrokerConfiguration, self).make_parser()37 parser = super(BrokerConfiguration, self).make_parser()
@@ -60,10 +58,6 @@
60 help="The URL of the HTTP proxy, if one is needed.")58 help="The URL of the HTTP proxy, if one is needed.")
61 parser.add_option("--https-proxy", metavar="URL",59 parser.add_option("--https-proxy", metavar="URL",
62 help="The URL of the HTTPS proxy, if one is needed.")60 help="The URL of the HTTPS proxy, if one is needed.")
63 parser.add_option("--cloud", action="store_true",
64 help="Set this if your computer is in an EC2 cloud.")
65 parser.add_option("--otp", default="",
66 help="The OTP to use in cloud configuration.")
67 parser.add_option("--access-group", default="",61 parser.add_option("--access-group", default="",
68 help="Suggested access group for this computer.")62 help="Suggested access group for this computer.")
69 parser.add_option("--tags",63 parser.add_option("--tags",
7064
=== modified file 'landscape/broker/registration.py'
--- landscape/broker/registration.py 2014-06-19 10:51:57 +0000
+++ landscape/broker/registration.py 2014-07-17 06:52:14 +0000
@@ -8,25 +8,16 @@
8credentials yet and that the server accepts registration messages, so it8credentials yet and that the server accepts registration messages, so it
9will craft an appropriate one and send it out.9will craft an appropriate one and send it out.
10"""10"""
11import time
12import logging11import logging
13import socket
1412
15from twisted.internet.defer import Deferred13from twisted.internet.defer import Deferred
1614
17from landscape.lib.bpickle import loads
18from landscape.lib.log import log_failure
19from landscape.lib.juju import get_juju_info15from landscape.lib.juju import get_juju_info
20from landscape.lib.fetch import fetch, FetchError
21from landscape.lib.tag import is_valid_tag_list16from landscape.lib.tag import is_valid_tag_list
22from landscape.lib.network import get_fqdn17from landscape.lib.network import get_fqdn
23from landscape.lib.vm_info import get_vm_info, get_container_info18from landscape.lib.vm_info import get_vm_info, get_container_info
2419
2520
26EC2_HOST = "169.254.169.254"
27EC2_API = "http://%s/latest" % (EC2_HOST,)
28
29
30class InvalidCredentialsError(Exception):21class InvalidCredentialsError(Exception):
31 """22 """
32 Raised when an invalid account title and/or registration key23 Raised when an invalid account title and/or registration key
@@ -99,7 +90,6 @@
99 self._exchange = exchange90 self._exchange = exchange
100 self._pinger = pinger91 self._pinger = pinger
101 self._message_store = message_store92 self._message_store = message_store
102 self._reactor.call_on("run", self._fetch_ec2_data)
103 self._reactor.call_on("run", self._get_juju_data)93 self._reactor.call_on("run", self._get_juju_data)
104 self._reactor.call_on("pre-exchange", self._handle_pre_exchange)94 self._reactor.call_on("pre-exchange", self._handle_pre_exchange)
105 self._reactor.call_on("exchange-done", self._handle_exchange_done)95 self._reactor.call_on("exchange-done", self._handle_exchange_done)
@@ -109,21 +99,15 @@
109 self._handle_registration)99 self._handle_registration)
110 self._should_register = None100 self._should_register = None
111 self._fetch_async = fetch_async101 self._fetch_async = fetch_async
112 self._otp = None
113 self._ec2_data = None102 self._ec2_data = None
114 self._juju_data = None103 self._juju_data = None
115104
116 def should_register(self):105 def should_register(self):
117 id = self._identity106 id = self._identity
118 if id.secure_id:107 if id.secure_id:
119 # We already have a secure ID, no need to register
120 logging.info("Machine already has a secure-id. Skipping "
121 "registration.")
122 return False108 return False
123109
124 if self._config.cloud:110 if self._config.provisioning_otp:
125 return self._message_store.accepts("register-cloud-vm")
126 elif self._config.provisioning_otp:
127 return self._message_store.accepts("register-provisioned-machine")111 return self._message_store.accepts("register-provisioned-machine")
128112
129 return bool(id.computer_title and id.account_name113 return bool(id.computer_title and id.account_name
@@ -157,94 +141,6 @@
157 """141 """
158 return self._fetch_async(EC2_API + path).addCallback(accumulate.append)142 return self._fetch_async(EC2_API + path).addCallback(accumulate.append)
159143
160 def _fetch_ec2_data(self):
161 """Retrieve available EC2 information, if in a EC2 compatible cloud."""
162 id = self._identity
163 if self._config.cloud and not id.secure_id:
164 # Fetch data from the EC2 API, to be used later in the registration
165 # process
166 # We ignore errors from user-data because it's common for the
167 # URL to return a 404 when the data is unavailable.
168 ec2_data = []
169 deferred = self._fetch_async(EC2_API + "/user-data").addErrback(
170 log_failure).addCallback(ec2_data.append)
171 paths = [
172 "/meta-data/instance-id",
173 "/meta-data/reservation-id",
174 "/meta-data/local-hostname",
175 "/meta-data/public-hostname",
176 "/meta-data/ami-launch-index",
177 "/meta-data/ami-id",
178 "/meta-data/local-ipv4",
179 "/meta-data/public-ipv4"]
180 # We're not using a DeferredList here because we want to keep the
181 # number of connections to the backend minimal. See lp:567515.
182 for path in paths:
183 deferred.addCallback(
184 lambda ignore, path=path: self._get_data(path, ec2_data))
185 # Special case the ramdisk retrieval, because it may not be present
186 deferred.addCallback(
187 lambda ignore: self._fetch_async(
188 EC2_API + "/meta-data/ramdisk-id").addErrback(log_failure))
189 deferred.addCallback(ec2_data.append)
190 # And same for kernel
191 deferred.addCallback(
192 lambda ignore: self._fetch_async(
193 EC2_API + "/meta-data/kernel-id").addErrback(log_failure))
194 deferred.addCallback(ec2_data.append)
195
196 def record_data(ignore):
197 """Record the instance data returned by the EC2 API."""
198 (raw_user_data, instance_key, reservation_key,
199 local_hostname, public_hostname, launch_index,
200 ami_key, local_ip, public_ip, ramdisk_key,
201 kernel_key) = ec2_data
202 self._ec2_data = {
203 "instance_key": instance_key,
204 "reservation_key": reservation_key,
205 "local_hostname": local_hostname,
206 "public_hostname": public_hostname,
207 "launch_index": launch_index,
208 "kernel_key": kernel_key,
209 "ramdisk_key": ramdisk_key,
210 "image_key": ami_key,
211 "public_ipv4": public_ip,
212 "local_ipv4": local_ip}
213 for k, v in self._ec2_data.items():
214 if v is None and k in ("ramdisk_key", "kernel_key"):
215 continue
216 self._ec2_data[k] = v.decode("utf-8")
217 self._ec2_data["launch_index"] = int(
218 self._ec2_data["launch_index"])
219
220 if self._config.otp:
221 self._otp = self._config.otp
222 return
223 instance_data = _extract_ec2_instance_data(
224 raw_user_data, int(launch_index))
225 if instance_data is not None:
226 self._otp = instance_data["otp"]
227 exchange_url = instance_data["exchange-url"]
228 ping_url = instance_data["ping-url"]
229 self._exchange._transport.set_url(exchange_url)
230 self._config.url = exchange_url
231 self._config.ping_url = ping_url
232 if "ssl-ca-certificate" in instance_data:
233 from landscape.configuration import \
234 store_public_key_data
235 public_key_file = store_public_key_data(
236 self._config, instance_data["ssl-ca-certificate"])
237 self._config.ssl_public_key = public_key_file
238 self._exchange._transport._pubkey = public_key_file
239 self._config.write()
240
241 def log_error(error):
242 log_failure(error, msg="Got error while fetching meta-data: %r"
243 % (error.value,))
244
245 deferred.addCallback(record_data)
246 deferred.addErrback(log_error)
247
248 def _handle_exchange_done(self):144 def _handle_exchange_done(self):
249 """Registered handler for the C{"exchange-done"} event.145 """Registered handler for the C{"exchange-done"} event.
250146
@@ -264,12 +160,8 @@
264 message with the server.160 message with the server.
265161
266 A computer can fall into several categories:162 A computer can fall into several categories:
267 - a "cloud VM"
268 - a "normal" computer163 - a "normal" computer
269 - a "provisionned machine".164 - a "provisionned machine".
270
271 Furthermore, Cloud VMs can be registered with either a One Time
272 Password (OTP), or with a normal registration password.
273 """165 """
274 registration_failed = False166 registration_failed = False
275167
@@ -293,10 +185,9 @@
293185
294 if not is_valid_tag_list(tags):186 if not is_valid_tag_list(tags):
295 tags = None187 tags = None
296 logging.error("Invalid tags provided for cloud registration.")188 logging.error("Invalid tags provided for registration.")
297189
298 message = {"type": None, # either "register" or "register-cloud-vm"190 message = {"type": None, # either "register" or "register-cloud-vm"
299 "otp": None,
300 "hostname": get_fqdn(),191 "hostname": get_fqdn(),
301 "account_name": identity.account_name,192 "account_name": identity.account_name,
302 "registration_password": None,193 "registration_password": None,
@@ -306,28 +197,7 @@
306 if group:197 if group:
307 message["access_group"] = group198 message["access_group"] = group
308199
309 if self._config.cloud and self._ec2_data is not None:200 if account_name:
310 # This is the "cloud VM" case.
311 message["type"] = "register-cloud-vm"
312
313 message.update(self._ec2_data)
314 if self._otp:
315 logging.info("Queueing message to register with OTP")
316 message["otp"] = self._otp
317
318 elif account_name:
319 with_tags = "and tags %s " % tags if tags else ""
320 with_group = "in access group '%s' " % group if group else ""
321 logging.info(
322 u"Queueing message to register with account %r %s%s"
323 u"as an EC2 instance." % (
324 account_name, with_group, with_tags))
325 message["registration_password"] = registration_key
326
327 else:
328 registration_failed = True
329
330 elif account_name:
331 # The computer is a normal computer, possibly a container.201 # The computer is a normal computer, possibly a container.
332 with_word = "with" if bool(registration_key) else "without"202 with_word = "with" if bool(registration_key) else "without"
333 with_tags = "and tags %s " % tags if tags else ""203 with_tags = "and tags %s " % tags if tags else ""
@@ -433,80 +303,3 @@
433 def _failed(self):303 def _failed(self):
434 self.deferred.errback(InvalidCredentialsError())304 self.deferred.errback(InvalidCredentialsError())
435 self._cancel_calls()305 self._cancel_calls()
436
437
438def _extract_ec2_instance_data(raw_user_data, launch_index):
439 """
440 Given the raw string of EC2 User Data, parse it and return the dict of
441 instance data for this particular instance.
442
443 If the data can't be parsed, a debug message will be logged and None
444 will be returned.
445 """
446 try:
447 user_data = loads(raw_user_data)
448 except ValueError:
449 logging.debug("Got invalid user-data %r" % (raw_user_data,))
450 return
451
452 if not isinstance(user_data, dict):
453 logging.debug("user-data %r is not a dict" % (user_data,))
454 return
455 for key in "otps", "exchange-url", "ping-url":
456 if key not in user_data:
457 logging.debug("user-data %r doesn't have key %r."
458 % (user_data, key))
459 return
460 if len(user_data["otps"]) <= launch_index:
461 logging.debug("user-data %r doesn't have OTP for launch index %d"
462 % (user_data, launch_index))
463 return
464 instance_data = {"otp": user_data["otps"][launch_index],
465 "exchange-url": user_data["exchange-url"],
466 "ping-url": user_data["ping-url"]}
467 if "ssl-ca-certificate" in user_data:
468 instance_data["ssl-ca-certificate"] = user_data["ssl-ca-certificate"]
469 return instance_data
470
471
472def _wait_for_network():
473 """
474 Keep trying to connect to the EC2 metadata server until it becomes
475 accessible or until five minutes pass.
476
477 This is necessary because the networking init script on Ubuntu is
478 asynchronous; the network may not actually be up by the time the
479 landscape-client init script is invoked.
480 """
481 timeout = 5 * 60
482 port = 80
483
484 start = time.time()
485 while True:
486 s = socket.socket()
487 try:
488 s.connect((EC2_HOST, port))
489 s.close()
490 return
491 except socket.error:
492 time.sleep(1)
493 if time.time() - start > timeout:
494 break
495
496
497def is_cloud_managed(fetch=fetch):
498 """
499 Return C{True} if the machine has been started by Landscape, i.e. if we can
500 find the expected data inside the EC2 user-data field.
501 """
502 _wait_for_network()
503 try:
504 raw_user_data = fetch(EC2_API + "/user-data",
505 connect_timeout=5)
506 launch_index = fetch(EC2_API + "/meta-data/ami-launch-index",
507 connect_timeout=5)
508 except FetchError:
509 return False
510 instance_data = _extract_ec2_instance_data(
511 raw_user_data, int(launch_index))
512 return instance_data is not None
513306
=== modified file 'landscape/broker/tests/test_registration.py'
--- landscape/broker/tests/test_registration.py 2014-06-20 06:06:51 +0000
+++ landscape/broker/tests/test_registration.py 2014-07-17 06:52:14 +0000
@@ -1,24 +1,15 @@
1import json1import json
2import os
3import logging2import logging
4import pycurl
5import socket3import socket
64
7from twisted.internet.defer import succeed, fail
8
9from landscape.broker.registration import (5from landscape.broker.registration import (
10 InvalidCredentialsError, RegistrationHandler, is_cloud_managed, EC2_HOST,6 InvalidCredentialsError, Identity)
11 EC2_API, Identity)
127
13from landscape.broker.config import BrokerConfiguration
14from landscape.tests.helpers import LandscapeTest8from landscape.tests.helpers import LandscapeTest
15from landscape.broker.tests.helpers import (9from landscape.broker.tests.helpers import (
16 BrokerConfigurationHelper, RegistrationHelper)10 BrokerConfigurationHelper, RegistrationHelper)
17from landscape.lib.bpickle import dumps
18from landscape.lib.fetch import HTTPCodeError, FetchError
19from landscape.lib.persist import Persist11from landscape.lib.persist import Persist
20from landscape.lib.vm_info import get_vm_info12from landscape.lib.vm_info import get_vm_info
21from landscape.configuration import print_text
2213
2314
24class IdentityTest(LandscapeTest):15class IdentityTest(LandscapeTest):
@@ -250,8 +241,7 @@
250 If the admin has defined tags for this computer, but they are not241 If the admin has defined tags for this computer, but they are not
251 valid, we drop them, and report an error.242 valid, we drop them, and report an error.
252 """243 """
253 self.log_helper.ignore_errors("Invalid tags provided for cloud "244 self.log_helper.ignore_errors("Invalid tags provided for registration")
254 "registration")
255 self.mstore.set_accepted_types(["register"])245 self.mstore.set_accepted_types(["register"])
256 self.config.computer_title = "Computer Title"246 self.config.computer_title = "Computer Title"
257 self.config.account_name = "account_name"247 self.config.account_name = "account_name"
@@ -261,8 +251,7 @@
261 messages = self.mstore.get_pending_messages()251 messages = self.mstore.get_pending_messages()
262 self.assertIs(None, messages[0]["tags"])252 self.assertIs(None, messages[0]["tags"])
263 self.assertEqual(self.logfile.getvalue().strip(),253 self.assertEqual(self.logfile.getvalue().strip(),
264 "ERROR: Invalid tags provided for cloud "254 "ERROR: Invalid tags provided for registration.\n "
265 "registration.\n "
266 "INFO: Queueing message to register with account "255 "INFO: Queueing message to register with account "
267 "'account_name' with a password.\n "256 "'account_name' with a password.\n "
268 "INFO: Sending registration message to exchange.")257 "INFO: Sending registration message to exchange.")
@@ -588,661 +577,6 @@
588 self.assertIn(expected2, juju_info)577 self.assertIn(expected2, juju_info)
589578
590579
591class CloudRegistrationHandlerTest(RegistrationHandlerTestBase):
592
593 cloud = True
594
595 def setUp(self):
596 super(CloudRegistrationHandlerTest, self).setUp()
597 self.query_results = {}
598
599 def fetch_stub(url):
600 value = self.query_results[url]
601 if isinstance(value, Exception):
602 return fail(value)
603 else:
604 return succeed(value)
605
606 self.fetch_func = fetch_stub
607
608 def get_user_data(self, otps=None,
609 exchange_url="https://example.com/message-system",
610 ping_url="http://example.com/ping",
611 ssl_ca_certificate=None):
612 if otps is None:
613 otps = ["otp1"]
614 user_data = {"otps": otps, "exchange-url": exchange_url,
615 "ping-url": ping_url}
616 if ssl_ca_certificate is not None:
617 user_data["ssl-ca-certificate"] = ssl_ca_certificate
618 return user_data
619
620 def prepare_query_results(
621 self, user_data=None, instance_key="key1", launch_index=0,
622 local_hostname="ooga.local", public_hostname="ooga.amazon.com",
623 reservation_key=u"res1", ramdisk_key=u"ram1", kernel_key=u"kernel1",
624 image_key=u"image1", local_ip="10.0.0.1", public_ip="10.0.0.2",
625 ssl_ca_certificate=None):
626 if user_data is None:
627 user_data = self.get_user_data(
628 ssl_ca_certificate=ssl_ca_certificate)
629 if not isinstance(user_data, Exception):
630 user_data = dumps(user_data)
631 api_base = "http://169.254.169.254/latest"
632 self.query_results.clear()
633 for url_suffix, value in [
634 ("/user-data", user_data),
635 ("/meta-data/instance-id", instance_key),
636 ("/meta-data/reservation-id", reservation_key),
637 ("/meta-data/local-hostname", local_hostname),
638 ("/meta-data/public-hostname", public_hostname),
639 ("/meta-data/ami-launch-index", str(launch_index)),
640 ("/meta-data/kernel-id", kernel_key),
641 ("/meta-data/ramdisk-id", ramdisk_key),
642 ("/meta-data/ami-id", image_key),
643 ("/meta-data/local-ipv4", local_ip),
644 ("/meta-data/public-ipv4", public_ip),
645 ]:
646 self.query_results[api_base + url_suffix] = value
647
648 def prepare_cloud_registration(self, account_name=None,
649 registration_key=None, tags=None,
650 access_group=None):
651 # Set things up so that the client thinks it should register
652 self.mstore.set_accepted_types(list(self.mstore.get_accepted_types())
653 + ["register-cloud-vm"])
654 self.config.account_name = account_name
655 self.config.registration_key = registration_key
656 self.config.computer_title = None
657 self.config.tags = tags
658 self.config.access_group = access_group
659 self.identity.secure_id = None
660 self.assertTrue(self.handler.should_register())
661
662 def get_expected_cloud_message(self, **kwargs):
663 """
664 Return the message which is expected from a similar call to
665 L{get_registration_handler_for_cloud}.
666 """
667 message = dict(type="register-cloud-vm",
668 otp="otp1",
669 hostname="ooga.local",
670 local_hostname="ooga.local",
671 public_hostname="ooga.amazon.com",
672 instance_key=u"key1",
673 reservation_key=u"res1",
674 ramdisk_key=u"ram1",
675 kernel_key=u"kernel1",
676 launch_index=0,
677 image_key=u"image1",
678 account_name=None,
679 registration_password=None,
680 local_ipv4=u"10.0.0.1",
681 public_ipv4=u"10.0.0.2",
682 tags=None)
683 # The get_vm_info() needs to be deferred to the else. If vm-info is
684 # not specified in kwargs, get_vm_info() will typically be mocked.
685 if "vm_info" in kwargs:
686 message["vm-info"] = kwargs.pop("vm_info")
687 else:
688 message["vm-info"] = get_vm_info()
689 message.update(kwargs)
690 return message
691
692 def test_cloud_registration(self):
693 """
694 When the 'cloud' configuration variable is set, cloud registration is
695 done instead of normal password-based registration. This means:
696
697 - "Launch Data" is fetched from the EC2 Launch Data URL. This contains
698 a one-time password that is used during registration.
699 - A different "register-cloud-vm" message is sent to the server instead
700 of "register", containing the OTP. This message is handled by
701 immediately accepting the computer, instead of going through the
702 pending computer stage.
703 """
704 get_vm_info_mock = self.mocker.replace(get_vm_info)
705 get_vm_info_mock()
706 self.mocker.result("xen")
707 self.mocker.replay()
708 self.prepare_query_results()
709 self.prepare_cloud_registration(tags=u"server,london")
710
711 # metadata is fetched and stored at reactor startup:
712 self.reactor.fire("run")
713
714 # And the metadata returned determines the URLs that are used
715 self.assertEqual(self.transport.get_url(),
716 "https://example.com/message-system")
717 self.assertEqual(self.pinger.get_url(),
718 "http://example.com/ping")
719 # Lets make sure those values were written back to the config file
720 new_config = BrokerConfiguration()
721 new_config.load_configuration_file(self.config_filename)
722 self.assertEqual(new_config.url, "https://example.com/message-system")
723 self.assertEqual(new_config.ping_url, "http://example.com/ping")
724
725 # Okay! Exchange should cause the registration to happen.
726 self.exchanger.exchange()
727 # This *should* be asynchronous, but I think a billion tests are
728 # written like this
729 self.assertEqual(len(self.transport.payloads), 1)
730 self.assertMessages(
731 self.transport.payloads[0]["messages"],
732 [self.get_expected_cloud_message(tags=u"server,london",
733 vm_info="xen")])
734
735 def test_cloud_registration_with_access_group(self):
736 """
737 If the access_group field is presnet in the configuration, the
738 access_group field is present in the outgoing message for a VM
739 registration, and a notice appears in the logs.
740 """
741 self.prepare_query_results()
742 self.prepare_cloud_registration(access_group=u"dinosaurs",
743 tags=u"server,london")
744
745 self.reactor.fire("run")
746 self.exchanger.exchange()
747 self.assertEqual(len(self.transport.payloads), 1)
748 self.assertMessages(
749 self.transport.payloads[0]["messages"],
750 [self.get_expected_cloud_message(
751 access_group=u"dinosaurs", tags=u"server,london")])
752
753 def test_cloud_registration_with_otp(self):
754 """
755 If the OTP is present in the configuration, it's used to trigger the
756 registration instead of using the user data.
757 """
758 self.config.otp = "otp1"
759 self.prepare_query_results(user_data=None)
760
761 self.prepare_cloud_registration()
762
763 # metadata is fetched and stored at reactor startup:
764 self.reactor.fire("run")
765
766 # Okay! Exchange should cause the registration to happen.
767 self.exchanger.exchange()
768 # This *should* be asynchronous, but I think a billion tests are
769 # written like this
770 self.assertEqual(len(self.transport.payloads), 1)
771 self.assertMessages(
772 self.transport.payloads[0]["messages"],
773 [self.get_expected_cloud_message()])
774
775 def test_cloud_registration_with_invalid_tags(self):
776 """
777 Invalid tags in the configuration should result in the tags not being
778 sent to the server, and this fact logged.
779 """
780 self.log_helper.ignore_errors("Invalid tags provided for cloud "
781 "registration")
782 self.prepare_query_results()
783 self.prepare_cloud_registration(tags=u"<script>alert()</script>,hardy")
784
785 # metadata is fetched and stored at reactor startup:
786 self.reactor.fire("run")
787 self.exchanger.exchange()
788 self.assertEqual(len(self.transport.payloads), 1)
789 self.assertMessages(self.transport.payloads[0]["messages"],
790 [self.get_expected_cloud_message(tags=None)])
791 self.assertEqual(self.logfile.getvalue().strip()[:-7],
792 "ERROR: Invalid tags provided for cloud "
793 "registration.\n "
794 "INFO: Queueing message to register with OTP\n "
795 "INFO: Sending registration message to exchange.\n "
796 " INFO: Starting message exchange with "
797 "https://example.com/message-system.\n "
798 "INFO: Message exchange completed in")
799
800 def test_cloud_registration_with_ssl_ca_certificate(self):
801 """
802 If we have an SSL certificate CA included in the user-data, this should
803 be written out, and the configuration updated to reflect this.
804 """
805 key_filename = os.path.join(self.config.data_path,
806 "%s.ssl_public_key" % os.path.basename(self.config_filename))
807
808 print_text_mock = self.mocker.replace(print_text)
809 print_text_mock("Writing SSL CA certificate to %s..." %
810 key_filename)
811 self.mocker.replay()
812 self.prepare_query_results(ssl_ca_certificate=u"1234567890")
813 self.prepare_cloud_registration(tags=u"server,london")
814 # metadata is fetched and stored at reactor startup:
815 self.reactor.fire("run")
816 # And the metadata returned determines the URLs that are used
817 self.assertEqual("https://example.com/message-system",
818 self.transport.get_url())
819 self.assertEqual(key_filename, self.transport._pubkey)
820 self.assertEqual("http://example.com/ping",
821 self.pinger.get_url())
822 # Let's make sure those values were written back to the config file
823 new_config = BrokerConfiguration()
824 new_config.load_configuration_file(self.config_filename)
825 self.assertEqual("https://example.com/message-system", new_config.url)
826 self.assertEqual("http://example.com/ping", new_config.ping_url)
827 self.assertEqual(key_filename, new_config.ssl_public_key)
828 self.assertEqual("1234567890", open(key_filename, "r").read())
829
830 def test_wrong_user_data(self):
831 self.prepare_query_results(user_data="other stuff, not a bpickle")
832 self.prepare_cloud_registration()
833
834 # Mock registration-failed call
835 reactor_mock = self.mocker.patch(self.reactor)
836 reactor_mock.fire("registration-failed")
837 self.mocker.replay()
838
839 self.reactor.fire("run")
840 self.exchanger.exchange()
841
842 def test_wrong_object_type_in_user_data(self):
843 self.prepare_query_results(user_data=True)
844 self.prepare_cloud_registration()
845
846 # Mock registration-failed call
847 reactor_mock = self.mocker.patch(self.reactor)
848 reactor_mock.fire("registration-failed")
849 self.mocker.replay()
850
851 self.reactor.fire("run")
852 self.exchanger.exchange()
853
854 def test_user_data_with_not_enough_elements(self):
855 """
856 If the AMI launch index isn't represented in the list of OTPs in the
857 user data then BOOM.
858 """
859 self.prepare_query_results(launch_index=1)
860 self.prepare_cloud_registration()
861
862 # Mock registration-failed call
863 reactor_mock = self.mocker.patch(self.reactor)
864 reactor_mock.fire("registration-failed")
865 self.mocker.replay()
866
867 self.reactor.fire("run")
868 self.exchanger.exchange()
869
870 def test_user_data_bpickle_without_otp(self):
871 self.prepare_query_results(user_data={"foo": "bar"})
872 self.prepare_cloud_registration()
873
874 # Mock registration-failed call
875 reactor_mock = self.mocker.patch(self.reactor)
876 reactor_mock.fire("registration-failed")
877 self.mocker.replay()
878
879 self.reactor.fire("run")
880 self.exchanger.exchange()
881
882 def test_no_otp_fallback_to_account(self):
883 self.prepare_query_results(user_data="other stuff, not a bpickle",
884 instance_key=u"key1")
885 self.prepare_cloud_registration(account_name=u"onward",
886 registration_key=u"password",
887 tags=u"london,server")
888
889 self.reactor.fire("run")
890 self.exchanger.exchange()
891
892 self.assertEqual(len(self.transport.payloads), 1)
893 self.assertMessages(self.transport.payloads[0]["messages"],
894 [self.get_expected_cloud_message(
895 otp=None,
896 account_name=u"onward",
897 registration_password=u"password",
898 tags=u"london,server")])
899 self.assertEqual(self.logfile.getvalue().strip()[:-7],
900 "INFO: Queueing message to register with account u'onward' and "
901 "tags london,server as an EC2 instance.\n "
902 "INFO: Sending registration message to exchange.\n "
903 "INFO: Starting message exchange with http://localhost:91919.\n "
904 " INFO: Message exchange completed in")
905
906 def test_queueing_cloud_registration_message_resets_message_store(self):
907 """
908 When a registration from a cloud is about to happen, the message store
909 is reset, because all previous messages are now meaningless.
910 """
911 self.mstore.set_accepted_types(list(self.mstore.get_accepted_types())
912 + ["test"])
913
914 self.mstore.add({"type": "test"})
915
916 self.prepare_query_results()
917
918 self.prepare_cloud_registration()
919
920 self.reactor.fire("run")
921 self.reactor.fire("pre-exchange")
922
923 messages = self.mstore.get_pending_messages()
924 self.assertEqual(len(messages), 1)
925 self.assertEqual(messages[0]["type"], "register-cloud-vm")
926
927 def test_cloud_registration_fetch_errors(self):
928 """
929 If fetching metadata fails, and we have no account details to fall
930 back to, we fire 'registration-failed'.
931 """
932 self.log_helper.ignore_errors(pycurl.error)
933
934 def fetch_stub(url):
935 return fail(pycurl.error(7, "couldn't connect to host"))
936
937 self.handler = RegistrationHandler(
938 self.config, self.identity, self.reactor, self.exchanger,
939 self.pinger, self.mstore, fetch_async=fetch_stub)
940
941 self.fetch_stub = fetch_stub
942 self.prepare_query_results()
943 self.fetch_stub = fetch_stub
944
945 self.prepare_cloud_registration()
946
947 failed = []
948 self.reactor.call_on(
949 "registration-failed", lambda: failed.append(True))
950
951 self.log_helper.ignore_errors("Got error while fetching meta-data")
952 self.reactor.fire("run")
953 self.exchanger.exchange()
954 self.assertEqual(failed, [True])
955 self.assertIn('error: (7, "couldn\'t connect to host")',
956 self.logfile.getvalue())
957
958 def test_cloud_registration_continues_without_user_data(self):
959 """
960 If no user-data exists (i.e., the user-data URL returns a 404), then
961 register-cloud-vm still occurs.
962 """
963 self.log_helper.ignore_errors(HTTPCodeError)
964 self.prepare_query_results(user_data=HTTPCodeError(404, "ohno"))
965 self.prepare_cloud_registration(account_name="onward",
966 registration_key="password")
967
968 self.reactor.fire("run")
969 self.exchanger.exchange()
970 self.assertIn("HTTPCodeError: Server returned HTTP code 404",
971 self.logfile.getvalue())
972 self.assertEqual(len(self.transport.payloads), 1)
973 self.assertMessages(self.transport.payloads[0]["messages"],
974 [self.get_expected_cloud_message(
975 otp=None,
976 account_name=u"onward",
977 registration_password=u"password")])
978
979 def test_cloud_registration_continues_without_ramdisk(self):
980 """
981 If the instance doesn't have a ramdisk (ie, the query for ramdisk
982 returns a 404), then register-cloud-vm still occurs.
983 """
984 self.log_helper.ignore_errors(HTTPCodeError)
985 self.prepare_query_results(ramdisk_key=HTTPCodeError(404, "ohno"))
986 self.prepare_cloud_registration()
987
988 self.reactor.fire("run")
989 self.exchanger.exchange()
990 self.assertIn("HTTPCodeError: Server returned HTTP code 404",
991 self.logfile.getvalue())
992 self.assertEqual(len(self.transport.payloads), 1)
993 self.assertMessages(self.transport.payloads[0]["messages"],
994 [self.get_expected_cloud_message(
995 ramdisk_key=None)])
996
997 def test_cloud_registration_continues_without_kernel(self):
998 """
999 If the instance doesn't have a kernel (ie, the query for kernel
1000 returns a 404), then register-cloud-vm still occurs.
1001 """
1002 self.log_helper.ignore_errors(HTTPCodeError)
1003 self.prepare_query_results(kernel_key=HTTPCodeError(404, "ohno"))
1004 self.prepare_cloud_registration()
1005
1006 self.reactor.fire("run")
1007 self.exchanger.exchange()
1008 self.assertIn("HTTPCodeError: Server returned HTTP code 404",
1009 self.logfile.getvalue())
1010 self.assertEqual(len(self.transport.payloads), 1)
1011 self.assertMessages(self.transport.payloads[0]["messages"],
1012 [self.get_expected_cloud_message(
1013 kernel_key=None)])
1014
1015 def test_fall_back_to_normal_registration_when_metadata_fetch_fails(self):
1016 """
1017 If fetching metadata fails, but we do have an account name, then we
1018 fall back to normal 'register' registration.
1019 """
1020 self.mstore.set_accepted_types(["register"])
1021 self.log_helper.ignore_errors(HTTPCodeError)
1022 self.prepare_query_results(
1023 public_hostname=HTTPCodeError(404, "ohnoes"))
1024 self.prepare_cloud_registration(account_name="onward",
1025 registration_key="password")
1026 self.config.computer_title = "whatever"
1027 self.reactor.fire("run")
1028 self.exchanger.exchange()
1029 self.assertIn("HTTPCodeError: Server returned HTTP code 404",
1030 self.logfile.getvalue())
1031 self.assertEqual(len(self.transport.payloads), 1)
1032 messages = self.transport.payloads[0]["messages"]
1033 self.assertEqual("register", messages[0]["type"])
1034
1035 def test_should_register_in_cloud(self):
1036 """
1037 The client should register when it's in the cloud even though
1038 it doesn't have the normal account details.
1039 """
1040 self.mstore.set_accepted_types(self.mstore.get_accepted_types()
1041 + ("register-cloud-vm",))
1042 self.config.account_name = None
1043 self.config.registration_key = None
1044 self.config.computer_title = None
1045 self.identity.secure_id = None
1046 self.assertTrue(self.handler.should_register())
1047
1048 def test_launch_index(self):
1049 """
1050 The client used the value in C{ami-launch-index} to choose the
1051 appropriate OTP in the user data.
1052 """
1053 otp = "correct otp for launch index"
1054 self.prepare_query_results(
1055 user_data=self.get_user_data(otps=["wrong index", otp,
1056 "wrong again"],),
1057 instance_key="key1",
1058 launch_index=1)
1059
1060 self.prepare_cloud_registration()
1061
1062 self.reactor.fire("run")
1063 self.exchanger.exchange()
1064 self.assertEqual(len(self.transport.payloads), 1)
1065 self.assertMessages(self.transport.payloads[0]["messages"],
1066 [self.get_expected_cloud_message(otp=otp,
1067 launch_index=1)])
1068
1069 def test_should_not_register_in_cloud(self):
1070 """
1071 Having a secure ID means we shouldn't register, even in the cloud.
1072 """
1073 self.mstore.set_accepted_types(self.mstore.get_accepted_types()
1074 + ("register-cloud-vm",))
1075 self.config.account_name = None
1076 self.config.registration_key = None
1077 self.config.computer_title = None
1078 self.identity.secure_id = "hello"
1079 self.assertFalse(self.handler.should_register())
1080
1081 def test_should_not_register_without_register_cloud_vm(self):
1082 """
1083 If the server isn't accepting a 'register-cloud-vm' message,
1084 we shouldn't register.
1085 """
1086 self.config.account_name = None
1087 self.config.registration_key = None
1088 self.config.computer_title = None
1089 self.identity.secure_id = None
1090 self.assertFalse(self.handler.should_register())
1091
1092
1093class IsCloudManagedTests(LandscapeTest):
1094
1095 def setUp(self):
1096 super(IsCloudManagedTests, self).setUp()
1097 self.urls = []
1098 self.responses = []
1099
1100 def fake_fetch(self, url, connect_timeout=None):
1101 self.urls.append((url, connect_timeout))
1102 return self.responses.pop(0)
1103
1104 def mock_socket(self):
1105 """
1106 Mock out socket usage by is_cloud_managed to wait for the network.
1107 """
1108 # Mock the socket.connect call that it also does
1109 socket_class = self.mocker.replace("socket.socket", passthrough=False)
1110 socket = socket_class()
1111 socket.connect((EC2_HOST, 80))
1112 socket.close()
1113
1114 def test_is_managed(self):
1115 """
1116 L{is_cloud_managed} returns True if the EC2 user-data contains
1117 Landscape instance information. It fetches the EC2 data with low
1118 timeouts.
1119 """
1120 user_data = {"otps": ["otp1"], "exchange-url": "http://exchange",
1121 "ping-url": "http://ping"}
1122 self.responses = [dumps(user_data), "0"]
1123
1124 self.mock_socket()
1125 self.mocker.replay()
1126
1127 self.assertTrue(is_cloud_managed(self.fake_fetch))
1128 self.assertEqual(
1129 self.urls,
1130 [(EC2_API + "/user-data", 5),
1131 (EC2_API + "/meta-data/ami-launch-index", 5)])
1132
1133 def test_is_managed_index(self):
1134 user_data = {"otps": ["otp1", "otp2"],
1135 "exchange-url": "http://exchange",
1136 "ping-url": "http://ping"}
1137 self.responses = [dumps(user_data), "1"]
1138 self.mock_socket()
1139 self.mocker.replay()
1140 self.assertTrue(is_cloud_managed(self.fake_fetch))
1141
1142 def test_is_managed_wrong_index(self):
1143 user_data = {"otps": ["otp1"], "exchange-url": "http://exchange",
1144 "ping-url": "http://ping"}
1145 self.responses = [dumps(user_data), "1"]
1146 self.mock_socket()
1147 self.mocker.replay()
1148 self.assertFalse(is_cloud_managed(self.fake_fetch))
1149
1150 def test_is_managed_exchange_url(self):
1151 user_data = {"otps": ["otp1"], "ping-url": "http://ping"}
1152 self.responses = [dumps(user_data), "0"]
1153 self.mock_socket()
1154 self.mocker.replay()
1155 self.assertFalse(is_cloud_managed(self.fake_fetch))
1156
1157 def test_is_managed_ping_url(self):
1158 user_data = {"otps": ["otp1"], "exchange-url": "http://exchange"}
1159 self.responses = [dumps(user_data), "0"]
1160 self.mock_socket()
1161 self.mocker.replay()
1162 self.assertFalse(is_cloud_managed(self.fake_fetch))
1163
1164 def test_is_managed_bpickle(self):
1165 self.responses = ["some other user data", "0"]
1166 self.mock_socket()
1167 self.mocker.replay()
1168 self.assertFalse(is_cloud_managed(self.fake_fetch))
1169
1170 def test_is_managed_no_data(self):
1171 self.responses = ["", "0"]
1172 self.mock_socket()
1173 self.mocker.replay()
1174 self.assertFalse(is_cloud_managed(self.fake_fetch))
1175
1176 def test_is_managed_fetch_not_found(self):
1177
1178 def fake_fetch(url, connect_timeout=None):
1179 raise HTTPCodeError(404, "ohnoes")
1180
1181 self.mock_socket()
1182 self.mocker.replay()
1183 self.assertFalse(is_cloud_managed(fake_fetch))
1184
1185 def test_is_managed_fetch_error(self):
1186
1187 def fake_fetch(url, connect_timeout=None):
1188 raise FetchError(7, "couldn't connect to host")
1189
1190 self.mock_socket()
1191 self.mocker.replay()
1192 self.assertFalse(is_cloud_managed(fake_fetch))
1193
1194 def test_waits_for_network(self):
1195 """
1196 is_cloud_managed will wait until the network before trying to fetch
1197 the EC2 user data.
1198 """
1199 user_data = {"otps": ["otp1"], "exchange-url": "http://exchange",
1200 "ping-url": "http://ping"}
1201 self.responses = [dumps(user_data), "0"]
1202
1203 self.mocker.order()
1204 time_sleep = self.mocker.replace("time.sleep", passthrough=False)
1205 socket_class = self.mocker.replace("socket.socket", passthrough=False)
1206 socket_obj = socket_class()
1207 socket_obj.connect((EC2_HOST, 80))
1208 self.mocker.throw(socket.error("woops"))
1209 time_sleep(1)
1210 socket_obj = socket_class()
1211 socket_obj.connect((EC2_HOST, 80))
1212 self.mocker.result(None)
1213 socket_obj.close()
1214 self.mocker.replay()
1215 self.assertTrue(is_cloud_managed(self.fake_fetch))
1216
1217 def test_waiting_times_out(self):
1218 """
1219 We'll only wait five minutes for the network to come up.
1220 """
1221
1222 def fake_fetch(url, connect_timeout=None):
1223 raise FetchError(7, "couldn't connect to host")
1224
1225 self.mocker.order()
1226 time_sleep = self.mocker.replace("time.sleep", passthrough=False)
1227 time_time = self.mocker.replace("time.time", passthrough=False)
1228 time_time()
1229 self.mocker.result(100)
1230 socket_class = self.mocker.replace("socket.socket", passthrough=False)
1231 socket_obj = socket_class()
1232 socket_obj.connect((EC2_HOST, 80))
1233 self.mocker.throw(socket.error("woops"))
1234 time_sleep(1)
1235 time_time()
1236 self.mocker.result(401)
1237 self.mocker.replay()
1238 # Mocking time.time is dangerous, because the test harness calls it. So
1239 # we explicitly reset mocker before returning from the test.
1240 try:
1241 self.assertFalse(is_cloud_managed(fake_fetch))
1242 finally:
1243 self.mocker.reset()
1244
1245
1246class ProvisioningRegistrationTest(RegistrationHandlerTestBase):580class ProvisioningRegistrationTest(RegistrationHandlerTestBase):
1247581
1248 def test_provisioned_machine_registration_with_otp(self):582 def test_provisioned_machine_registration_with_otp(self):
1249583
=== modified file 'landscape/configuration.py'
--- landscape/configuration.py 2014-07-01 14:52:02 +0000
+++ landscape/configuration.py 2014-07-17 06:52:14 +0000
@@ -581,7 +581,7 @@
581 decode_base64_ssl_public_certificate(config)581 decode_base64_ssl_public_certificate(config)
582 config.write()582 config.write()
583 # Restart the client to ensure that it's using the new configuration.583 # Restart the client to ensure that it's using the new configuration.
584 if not config.no_start and not config.otp:584 if not config.no_start:
585 try:585 try:
586 sysvconfig.restart_landscape()586 sysvconfig.restart_landscape()
587 except ProcessError:587 except ProcessError:
588588
=== modified file 'landscape/message_schemas.py'
--- landscape/message_schemas.py 2014-06-09 08:18:05 +0000
+++ landscape/message_schemas.py 2014-07-17 06:52:14 +0000
@@ -211,6 +211,9 @@
211 {"otp": Bytes()})211 {"otp": Bytes()})
212212
213213
214# XXX The register-cloud-vm message is obsolete, it's kept around just to not
215# break older LDS releases that import it (the last LDS release to have it
216# is 14.07). Eventually it shall be dropped.
214REGISTER_CLOUD_VM = Message(217REGISTER_CLOUD_VM = Message(
215 "register-cloud-vm",218 "register-cloud-vm",
216 {"hostname": Unicode(),219 {"hostname": Unicode(),
217220
=== modified file 'landscape/tests/test_configuration.py'
--- landscape/tests/test_configuration.py 2014-02-25 18:06:48 +0000
+++ landscape/tests/test_configuration.py 2014-07-17 06:52:14 +0000
@@ -820,7 +820,6 @@
820 "--ping-interval", "30",820 "--ping-interval", "30",
821 "--http-proxy", "",821 "--http-proxy", "",
822 "--https-proxy", "",822 "--https-proxy", "",
823 "--otp", "",
824 "--tags", "",823 "--tags", "",
825 "--provisioning-otp", ""]824 "--provisioning-otp", ""]
826 config = self.get_config(args)825 config = self.get_config(args)
@@ -837,7 +836,6 @@
837 "https_proxy = \n"836 "https_proxy = \n"
838 "url = https://landscape.canonical.com/message-system\n"837 "url = https://landscape.canonical.com/message-system\n"
839 "exchange_interval = 900\n"838 "exchange_interval = 900\n"
840 "otp = \n"
841 "ping_interval = 30\n"839 "ping_interval = 30\n"
842 "ping_url = http://landscape.canonical.com/ping\n"840 "ping_url = http://landscape.canonical.com/ping\n"
843 "provisioning_otp = \n"841 "provisioning_otp = \n"
@@ -862,20 +860,6 @@
862 config = self.get_config(["--silent", "-t", "rex"])860 config = self.get_config(["--silent", "-t", "rex"])
863 self.assertRaises(ConfigurationError, setup, config)861 self.assertRaises(ConfigurationError, setup, config)
864862
865 def test_silent_setup_with_otp(self):
866 """
867 If the OTP is specified, there is no need to pass the account name and
868 the computer title.
869 """
870 sysvconfig_mock = self.mocker.patch(SysVConfig)
871 sysvconfig_mock.set_start_on_boot(True)
872 self.mocker.replay()
873
874 config = self.get_config(["--silent", "--otp", "otp1"])
875 setup(config)
876
877 self.assertEqual("otp1", config.otp)
878
879 def test_silent_setup_with_provisioning_otp(self):863 def test_silent_setup_with_provisioning_otp(self):
880 """864 """
881 If the provisioning OTP is specified, there is no need to pass the865 If the provisioning OTP is specified, there is no need to pass the
882866
=== removed file 'scripts/landscape-is-cloud-managed'
--- scripts/landscape-is-cloud-managed 2009-04-09 14:32:45 +0000
+++ scripts/landscape-is-cloud-managed 1970-01-01 00:00:00 +0000
@@ -1,12 +0,0 @@
1#!/usr/bin/python
2import sys, os
3if os.path.dirname(os.path.abspath(sys.argv[0])) == os.path.abspath("scripts"):
4 sys.path.insert(0, "./")
5else:
6 from landscape.lib.warning import hide_warnings
7 hide_warnings()
8
9from landscape.broker.registration import is_cloud_managed
10
11# We return 0 if it succeeds
12sys.exit(not is_cloud_managed())
130
=== modified file 'setup.py'
--- setup.py 2013-06-03 12:26:30 +0000
+++ setup.py 2014-07-17 06:52:14 +0000
@@ -56,7 +56,6 @@
56 "scripts/landscape-package-reporter",56 "scripts/landscape-package-reporter",
57 "scripts/landscape-release-upgrader",57 "scripts/landscape-release-upgrader",
58 "scripts/landscape-sysinfo",58 "scripts/landscape-sysinfo",
59 "scripts/landscape-is-cloud-managed",
60 "scripts/landscape-dbus-proxy",59 "scripts/landscape-dbus-proxy",
61 "scripts/landscape-client-settings-mechanism",60 "scripts/landscape-client-settings-mechanism",
62 "scripts/landscape-client-registration-mechanism",61 "scripts/landscape-client-registration-mechanism",

Subscribers

People subscribed via source and target branches

to all changes: