Merge lp:~fcorrea/landscape-client/add-otp-field into lp:~landscape/landscape-client/trunk

Proposed by Fernando Correa Neto
Status: Merged
Approved by: Free Ekanayaka
Approved revision: 400
Merged at revision: 400
Proposed branch: lp:~fcorrea/landscape-client/add-otp-field
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 176 lines (+78/-8)
6 files modified
landscape/broker/config.py (+3/-1)
landscape/broker/registration.py (+11/-2)
landscape/broker/tests/test_registration.py (+38/-0)
landscape/configuration.py (+2/-2)
landscape/message_schemas.py (+8/-2)
landscape/tests/test_configuration.py (+16/-1)
To merge this branch: bzr merge lp:~fcorrea/landscape-client/add-otp-field
Reviewer Review Type Date Requested Status
Free Ekanayaka (community) Approve
Alberto Donato (community) Approve
Review via email: mp+81783@code.launchpad.net

Description of the change

This branch adds a optinal OTP field to the REGISTER message so computers can be registered automatically.

To post a comment you must log in.
Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Nice working, marking needs fixing mainly because of [1].

Thanks!

[1]

+ elif self._config.provisioning_otp:
+ return True

I think this will make the client try to register again every time it's started. It should be probably

return (not id.secure_id) and self._message_store.accepts("register-provisioned-machine")

the (not id.secure_id) means that the computer has not a secure ID yet (which afair is something that the client get and stores after a successful registration) and self._message_store.accepts("register-provisioned-machine") ensures that the server actually supports that kind of message (in principle it might be an older server that does not support it yet).

[2]

+ logging.info(u"Queueing message to register with OTP as a"
+ u" provisiong machine.")

I'd suggest s/provisioning/newly provisioned/

[3]

+ {"otp": Any(String(), Constant(None))})

This should be just

    {"otp": String()})

as the client must never send an otp with a None value.

[4]
+ """
+ If the provisioning OTP is specified, there is no need to pass the
+ account name and the computer title.
+ """

This is fine, but I just wanted to remind that in practice what we will do is probably to include the account name and computer title in the configuration file (via debconf), for sake of reference. However I think this code doesn't need to be changed in that case.

review: Needs Fixing
Revision history for this message
Alberto Donato (ack) wrote :

Looks good! +1

#1:
+ self.assertMessages([{'otp': 'ohteepee', 'timestamp': 0, 'api': '3.2',
+ 'type': 'register-provisioned-machine'}],

Please use double quotes (in the tests that follow as well).

#2:
         if not config.get("otp") and (not config.get("account_name") or not
- config.get("computer_title")):
+ config.get("computer_title"))\
+ and not config.provisioning_otp:

This is a bit hard to read, I think it could be rewritten as

if not (config.get("otp") or config.provisioning_otp or
       (config.get("account_name") and config.get("computer_title"))):

review: Approve
399. By Fernando Correa Neto <email address hidden>

- addressing review comments

400. By Fernando Correa Neto <email address hidden>

- addressing review comments

Revision history for this message
Fernando Correa Neto (fcorrea) wrote :

> Nice working, marking needs fixing mainly because of [1].
>
> Thanks!
>
> [1]
>
> + elif self._config.provisioning_otp:
> + return True
>
> I think this will make the client try to register again every time it's
> started. It should be probably
>
> return (not id.secure_id) and self._message_store.accepts("register-
> provisioned-machine")
>
> the (not id.secure_id) means that the computer has not a secure ID yet (which
> afair is something that the client get and stores after a successful
> registration) and self._message_store.accepts("register-provisioned-machine")
> ensures that the server actually supports that kind of message (in principle
> it might be an older server that does not support it yet).
>

Nice. Fixed and thanks for the explanation. It really helps understanding how this works.

> [2]
>
> + logging.info(u"Queueing message to register with OTP as a"
> + u" provisiong machine.")
>
> I'd suggest s/provisioning/newly provisioned/

Done.

>
> [3]
>
> + {"otp": Any(String(), Constant(None))})
>
> This should be just
>
> {"otp": String()})
>
> as the client must never send an otp with a None value.

Right. I was pretty much doing the same thing was being done by the cloud instance opt. Fixed.

>
> [4]
> + """
> + If the provisioning OTP is specified, there is no need to pass the
> + account name and the computer title.
> + """
>
> This is fine, but I just wanted to remind that in practice what we will do is
> probably to include the account name and computer title in the configuration
> file (via debconf), for sake of reference. However I think this code doesn't
> need to be changed in that case.

Sure. I believe we'll have some more interactions until we get everything done so that we can change it appropriately.

Thanks!

Revision history for this message
Fernando Correa Neto (fcorrea) wrote :

> Looks good! +1
>
> #1:
> + self.assertMessages([{'otp': 'ohteepee', 'timestamp': 0, 'api':
> '3.2',
> + 'type': 'register-provisioned-machine'}],
>
> Please use double quotes (in the tests that follow as well).

Fixed.

>
> #2:
> if not config.get("otp") and (not config.get("account_name") or not
> - config.get("computer_title")):
> + config.get("computer_title"))\
> + and not config.provisioning_otp:
>
> This is a bit hard to read, I think it could be rewritten as
>
> if not (config.get("otp") or config.provisioning_otp or
> (config.get("account_name") and config.get("computer_title"))):

Agreed. Before pushing that one, I tried two other possible ways that didn't end up as nice as yours :-)

Thanks!

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

+1!

[5]

+ parser.add_option("--provisioning-otp", default="",

I think you can just omit the default="" parameter and you will get None if nothing is passed. I see it's there for the cloud otp option, but I'm not sure if there's a good reason for that.

review: Approve
401. By Fernando Correa Neto <email address hidden>

- removing defaults for provisioning otp cli option

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'landscape/broker/config.py'
2--- landscape/broker/config.py 2011-10-20 17:59:29 +0000
3+++ landscape/broker/config.py 2011-11-11 16:21:24 +0000
4@@ -85,7 +85,9 @@
5 help="Comma separated list of tag names to be sent "
6 "to the server.")
7 parser.add_option("--record", action="store_true",
8- help="Record data sent to the server on filesystem ")
9+ help="Record data sent to the server on filesystem.")
10+ parser.add_option("--provisioning-otp",
11+ help="The OTP to use for a provisioned machine.")
12 return parser
13
14 @property
15
16=== modified file 'landscape/broker/registration.py'
17--- landscape/broker/registration.py 2011-07-21 23:55:47 +0000
18+++ landscape/broker/registration.py 2011-11-11 16:21:24 +0000
19@@ -104,6 +104,9 @@
20 if self._config.cloud:
21 return bool(not id.secure_id
22 and self._message_store.accepts("register-cloud-vm"))
23+ elif self._config.provisioning_otp:
24+ return (not id.secure_id) and\
25+ self._message_store.accepts("register-provisioned-machine")
26 return bool(not id.secure_id and id.computer_title and id.account_name
27 and self._message_store.accepts("register"))
28
29@@ -274,8 +277,8 @@
30 with_word = ["without", "with"][bool(id.registration_password)]
31 with_tags = ["", u"and tags %s " % tags][bool(tags)]
32 logging.info(u"Queueing message to register with account %r %s"
33- "%s a password." % (id.account_name, with_tags,
34- with_word))
35+ "%s a password." % (id.account_name, with_tags,
36+ with_word))
37 message = {"type": "register",
38 "computer_title": id.computer_title,
39 "account_name": id.account_name,
40@@ -284,6 +287,12 @@
41 "tags": tags,
42 "vm-info": get_vm_info()}
43 self._exchange.send(message)
44+ elif self._config.provisioning_otp:
45+ logging.info(u"Queueing message to register with OTP as a"
46+ u" newly provisioned machine.")
47+ message = {"type": "register-provisioned-machine",
48+ "otp": self._config.provisioning_otp}
49+ self._exchange.send(message)
50 else:
51 self._reactor.fire("registration-failed")
52
53
54=== modified file 'landscape/broker/tests/test_registration.py'
55--- landscape/broker/tests/test_registration.py 2011-06-15 10:08:15 +0000
56+++ landscape/broker/tests/test_registration.py 2011-11-11 16:21:24 +0000
57@@ -1078,3 +1078,41 @@
58 self.assertFalse(is_cloud_managed(fake_fetch))
59 finally:
60 self.mocker.reset()
61+
62+
63+class ProvisioningRegistrationTest(RegistrationHandlerTestBase):
64+
65+ def test_provisioned_machine_registration_with_otp(self):
66+ """
67+ Register provisioned machines using an OTP.
68+ """
69+ self.mstore.set_accepted_types(["register-provisioned-machine"])
70+ self.config.account_name = ""
71+ self.config.provisioning_otp = "ohteepee"
72+ self.reactor.fire("pre-exchange")
73+
74+ self.assertMessages([{"otp": "ohteepee", "timestamp": 0, "api": "3.2",
75+ "type": "register-provisioned-machine"}],
76+ self.mstore.get_pending_messages())
77+ self.assertEqual(u"INFO: Queueing message to register with OTP as a"
78+ u" newly provisioned machine.",
79+ self.logfile.getvalue().strip())
80+
81+ self.exchanger.exchange()
82+ self.assertMessages([{"otp": "ohteepee", "timestamp": 0, "api": "3.2",
83+ "type": "register-provisioned-machine"}],
84+ self.transport.payloads[0]["messages"])
85+
86+ def test_provisioned_machine_registration_with_empty_otp(self):
87+ """
88+ No message should be sent when an empty OTP is passed.
89+ """
90+ self.mstore.set_accepted_types(["register-provisioned-machine"])
91+ self.config.account_name = ""
92+ self.config.provisioning_otp = ""
93+ self.reactor.fire("pre-exchange")
94+
95+ self.assertMessages([],
96+ self.mstore.get_pending_messages())
97+ self.assertEqual(u"",
98+ self.logfile.getvalue().strip())
99
100=== modified file 'landscape/configuration.py'
101--- landscape/configuration.py 2011-10-20 18:17:59 +0000
102+++ landscape/configuration.py 2011-11-11 16:21:24 +0000
103@@ -440,8 +440,8 @@
104 config.https_proxy = os.environ["https_proxy"]
105
106 if config.silent and not config.no_start:
107- if not config.get("otp") and (not config.get("account_name") or not
108- config.get("computer_title")):
109+ if not (config.get("otp") or config.provisioning_otp or
110+ (config.get("account_name") and config.get("computer_title"))):
111 raise ConfigurationError("An account name and computer title are "
112 "required.")
113 if config.silent:
114
115=== modified file 'landscape/message_schemas.py'
116--- landscape/message_schemas.py 2011-08-10 14:52:05 +0000
117+++ landscape/message_schemas.py 2011-11-11 16:21:24 +0000
118@@ -143,6 +143,12 @@
119 # hostname wasn't around in old versions
120 optional=["registration_password", "hostname", "tags", "vm-info"])
121
122+
123+REGISTER_PROVISIONED_MACHINE = Message(
124+ "register-provisioned-machine",
125+ {"otp": String()})
126+
127+
128 REGISTER_CLOUD_VM = Message(
129 "register-cloud-vm",
130 {"hostname": utf8,
131@@ -388,8 +394,8 @@
132 OPERATION_RESULT, COMPUTER_INFO, DISTRIBUTION_INFO,
133 HARDWARE_INVENTORY, LOAD_AVERAGE, MEMORY_INFO,
134 RESYNCHRONIZE, MOUNT_ACTIVITY, MOUNT_INFO, FREE_SPACE,
135- REGISTER, REGISTER_CLOUD_VM, TEMPERATURE, PROCESSOR_INFO,
136- USERS, PACKAGES, PACKAGE_LOCKS,
137+ REGISTER, REGISTER_CLOUD_VM, REGISTER_PROVISIONED_MACHINE,
138+ TEMPERATURE, PROCESSOR_INFO, USERS, PACKAGES, PACKAGE_LOCKS,
139 CHANGE_PACKAGES_RESULT, UNKNOWN_PACKAGE_HASHES,
140 ADD_PACKAGES, PACKAGE_REPORTER_RESULT, TEXT_MESSAGE, TEST,
141 CUSTOM_GRAPH, REBOOT_REQUIRED, APT_PREFERENCES, EUCALYPTUS_INFO,
142
143=== modified file 'landscape/tests/test_configuration.py'
144--- landscape/tests/test_configuration.py 2011-11-02 13:24:35 +0000
145+++ landscape/tests/test_configuration.py 2011-11-11 16:21:24 +0000
146@@ -778,7 +778,8 @@
147 "--http-proxy", "",
148 "--https-proxy", "",
149 "--otp", "",
150- "--tags", ""]
151+ "--tags", "",
152+ "--provisioning-otp", ""]
153 config = self.get_config(args)
154 setup(config)
155 self.assertConfigEqual(self.get_content(config),
156@@ -826,6 +827,20 @@
157
158 self.assertEqual("otp1", config.otp)
159
160+ def test_silent_setup_with_provisioning_otp(self):
161+ """
162+ If the provisioning OTP is specified, there is no need to pass the
163+ account name and the computer title.
164+ """
165+ sysvconfig_mock = self.mocker.patch(SysVConfig)
166+ sysvconfig_mock.set_start_on_boot(True)
167+ self.mocker.replay()
168+
169+ config = self.get_config(["--silent", "--provisioning-otp", "otp1"])
170+ setup(config)
171+
172+ self.assertEqual("otp1", config.provisioning_otp)
173+
174 def test_silent_script_users_imply_script_execution_plugin(self):
175 """
176 If C{--script-users} is specified, without C{ScriptExecution} in the

Subscribers

People subscribed via source and target branches

to all changes: