Merge lp:~tilmanbaumann/charms/trusty/contrail-control/trunk into lp:~sdn-charmers/charms/trusty/contrail-control/trunk
- Trusty Tahr (14.04)
- trunk
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~tilmanbaumann/charms/trusty/contrail-control/trunk |
Merge into: | lp:~sdn-charmers/charms/trusty/contrail-control/trunk |
Diff against target: |
688 lines (+264/-75) 5 files modified
config.yaml (+6/-0) hooks/contrail_control_hooks.py (+85/-13) hooks/contrail_control_utils.py (+161/-61) metadata.yaml (+2/-0) templates/control-node.conf (+10/-1) |
To merge this branch: | bzr merge lp:~tilmanbaumann/charms/trusty/contrail-control/trunk |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tilman Baumann (community) | Approve | ||
Robert Ayres | Pending | ||
Review via email: mp+340110@code.launchpad.net |
Commit message
Description of the change
Previously xmpp-auth was only set to true when tls certificates are set.
There is, however, no strong link between the two settings.
This change allows setting this independently as required.
It would undoubtedly be nicer to have this option only in contrail-contrail and read it via context relation from there. This is rather quick and dirty and a bit redundant.
But it is what I built so far...
Unmerged revisions
- 33. By Tilman Baumann
-
Adding xmpp_auth option
Sparating xmpp_auth_enable from tls settings
Making it switchable via xmpp_auth config option - 32. By Dmitrii Shcherbakov
-
enable TLS for XMPP communication as of contrail 3
TLS is enabled unconditionally for contail 3.0 and above deployments to
make sure communication is secure by default.XMPP clients are vrouter agents on compute nodes. XMPP servers are
contrail-control nodes.Certificates are generated automatically from a PKI charm (e.g. easyrsa
with a Subject Alternative Name field containing an IP address on a
control network which is used by both contrail-control and
neutron-contrail to communicate with each other.Using a Subject Alternative Name (SAN) with an IP address avoids a
dependency on a DNS infrastructure while keeping the communication
secure between endpoints that are related.Client authentication by XMPP servers was not supported at the time of
writing hence there is no mention of that in the code.As of Juju 2.x network spaces can be used if an underlying cloud
supports them. In order to facilitate that support one should bind
control-node endpoint to a specific network space. Otherwise, old
mechanisms such as unit private address are going to be used to retrieve
an ip address to be included into a certificate.Control node address fetching mechanism has changed as well: instead of
just doing a relation-get for a private IP address of a control-node
unit a different value is taken from the relation data called
control_node_ip (available due to modifications on the contrail-control
side) - it is either an address in the network space which control-node
endpoint is bound to or a fall-back address (unit private address). - 31. By Dmitrii Shcherbakov
-
hooks: pep8 refactoring
Preview Diff
1 | === modified file 'config.yaml' | |||
2 | --- config.yaml 2017-03-10 12:54:47 +0000 | |||
3 | +++ config.yaml 2018-02-28 14:49:00 +0000 | |||
4 | @@ -14,6 +14,12 @@ | |||
5 | 14 | The IP address and netmask of the control network (e.g. 192.168.0.0/24). | 14 | The IP address and netmask of the control network (e.g. 192.168.0.0/24). |
6 | 15 | This network will be used for Contrail endpoints. | 15 | This network will be used for Contrail endpoints. |
7 | 16 | If not specified, default network will be used. | 16 | If not specified, default network will be used. |
8 | 17 | As of Juju 2.x, use network spaces instead and bind the control-node endpoint | ||
9 | 18 | to your desired network space instead. | ||
10 | 17 | ssl-ca: | 19 | ssl-ca: |
11 | 18 | type: string | 20 | type: string |
12 | 19 | description: PEM encoded X.509 CA certificate for use in SSL. | 21 | description: PEM encoded X.509 CA certificate for use in SSL. |
13 | 22 | xmpp_auth: | ||
14 | 23 | type: boolean | ||
15 | 24 | default: False | ||
16 | 25 | description: Use authentication for XMPP vrouter communication | ||
17 | 20 | 26 | ||
18 | === modified file 'hooks/contrail_control_hooks.py' | |||
19 | --- hooks/contrail_control_hooks.py 2017-03-10 12:54:47 +0000 | |||
20 | +++ hooks/contrail_control_hooks.py 2018-02-28 14:49:00 +0000 | |||
21 | @@ -12,7 +12,7 @@ | |||
22 | 12 | log, | 12 | log, |
23 | 13 | relation_get, | 13 | relation_get, |
24 | 14 | relation_ids, | 14 | relation_ids, |
26 | 15 | relation_set | 15 | relation_set, |
27 | 16 | ) | 16 | ) |
28 | 17 | 17 | ||
29 | 18 | from charmhelpers.core.host import ( | 18 | from charmhelpers.core.host import ( |
30 | @@ -39,46 +39,52 @@ | |||
31 | 39 | write_control_config, | 39 | write_control_config, |
32 | 40 | write_nodemgr_config, | 40 | write_nodemgr_config, |
33 | 41 | write_ssl_ca_certificate, | 41 | write_ssl_ca_certificate, |
35 | 42 | write_vnc_api_config | 42 | write_vnc_api_config, |
36 | 43 | write_xmpp_tls_files, | ||
37 | 43 | ) | 44 | ) |
38 | 44 | 45 | ||
40 | 45 | PACKAGES = [ "contrail-control", "contrail-utils", "contrail-nodemgr" ] | 46 | PACKAGES = ["contrail-control", "contrail-utils", "contrail-nodemgr"] |
41 | 46 | 47 | ||
42 | 47 | hooks = Hooks() | 48 | hooks = Hooks() |
43 | 48 | config = config() | 49 | config = config() |
44 | 49 | 50 | ||
45 | 51 | |||
46 | 50 | def add_control(): | 52 | def add_control(): |
47 | 51 | # check relation dependencies | 53 | # check relation dependencies |
48 | 52 | if not config_get("control-provisioned") \ | 54 | if not config_get("control-provisioned") \ |
49 | 53 | and config_get("contrail-api-ready") \ | 55 | and config_get("contrail-api-ready") \ |
50 | 54 | and config_get("contrail-discovery-ready") \ | 56 | and config_get("contrail-discovery-ready") \ |
51 | 55 | and config_get("contrail-ifmap-ready") \ | 57 | and config_get("contrail-ifmap-ready") \ |
53 | 56 | and config_get("identity-admin-ready"): | 58 | and config_get("identity-admin-ready") \ |
54 | 59 | and config_get("tls-certificates-ready"): | ||
55 | 57 | provision_control() | 60 | provision_control() |
56 | 58 | config["control-provisioned"] = True | 61 | config["control-provisioned"] = True |
57 | 59 | 62 | ||
58 | 63 | |||
59 | 60 | @hooks.hook("config-changed") | 64 | @hooks.hook("config-changed") |
60 | 61 | def config_changed(): | 65 | def config_changed(): |
61 | 62 | write_config() | 66 | write_config() |
62 | 63 | configure_ssl() | 67 | configure_ssl() |
63 | 64 | if config.get("control-provisioned"): | 68 | if config.get("control-provisioned"): |
64 | 65 | configure_control_network() | 69 | configure_control_network() |
67 | 66 | 70 | settings = {"private-address": control_network_ip()} | |
66 | 67 | settings = { "private-address": control_network_ip() } | ||
68 | 68 | for rid in relation_ids("control-node"): | 71 | for rid in relation_ids("control-node"): |
69 | 69 | relation_set(relation_id=rid, relation_settings=settings) | 72 | relation_set(relation_id=rid, relation_settings=settings) |
70 | 70 | 73 | ||
71 | 74 | |||
72 | 71 | def config_get(key): | 75 | def config_get(key): |
73 | 72 | try: | 76 | try: |
74 | 73 | return config[key] | 77 | return config[key] |
75 | 74 | except KeyError: | 78 | except KeyError: |
76 | 75 | return None | 79 | return None |
77 | 76 | 80 | ||
78 | 81 | |||
79 | 77 | def configure_control_network(): | 82 | def configure_control_network(): |
80 | 78 | if control_network_ip() != config["provisioned-host-ip"]: | 83 | if control_network_ip() != config["provisioned-host-ip"]: |
81 | 79 | unprovision_control() | 84 | unprovision_control() |
82 | 80 | provision_control() | 85 | provision_control() |
83 | 81 | 86 | ||
84 | 87 | |||
85 | 82 | def configure_ssl(): | 88 | def configure_ssl(): |
86 | 83 | cert = config.get("ssl-ca") | 89 | cert = config.get("ssl-ca") |
87 | 84 | if cert: | 90 | if cert: |
88 | @@ -86,6 +92,7 @@ | |||
89 | 86 | else: | 92 | else: |
90 | 87 | remove_ssl_ca_certificate() | 93 | remove_ssl_ca_certificate() |
91 | 88 | 94 | ||
92 | 95 | |||
93 | 89 | @hooks.hook("contrail-api-relation-changed") | 96 | @hooks.hook("contrail-api-relation-changed") |
94 | 90 | def contrail_api_changed(): | 97 | def contrail_api_changed(): |
95 | 91 | if not relation_get("port"): | 98 | if not relation_get("port"): |
96 | @@ -95,6 +102,7 @@ | |||
97 | 95 | config["contrail-api-ready"] = True | 102 | config["contrail-api-ready"] = True |
98 | 96 | add_control() | 103 | add_control() |
99 | 97 | 104 | ||
100 | 105 | |||
101 | 98 | @hooks.hook("contrail-api-relation-departed") | 106 | @hooks.hook("contrail-api-relation-departed") |
102 | 99 | @hooks.hook("contrail-api-relation-broken") | 107 | @hooks.hook("contrail-api-relation-broken") |
103 | 100 | def contrail_api_departed(): | 108 | def contrail_api_departed(): |
104 | @@ -103,6 +111,7 @@ | |||
105 | 103 | config["contrail-api-ready"] = False | 111 | config["contrail-api-ready"] = False |
106 | 104 | write_vnc_api_config() | 112 | write_vnc_api_config() |
107 | 105 | 113 | ||
108 | 114 | |||
109 | 106 | @hooks.hook("contrail-discovery-relation-changed") | 115 | @hooks.hook("contrail-discovery-relation-changed") |
110 | 107 | def contrail_discovery_changed(): | 116 | def contrail_discovery_changed(): |
111 | 108 | if not relation_get("port"): | 117 | if not relation_get("port"): |
112 | @@ -112,6 +121,7 @@ | |||
113 | 112 | config["contrail-discovery-ready"] = True | 121 | config["contrail-discovery-ready"] = True |
114 | 113 | add_control() | 122 | add_control() |
115 | 114 | 123 | ||
116 | 124 | |||
117 | 115 | @hooks.hook("contrail-discovery-relation-departed") | 125 | @hooks.hook("contrail-discovery-relation-departed") |
118 | 116 | @hooks.hook("contrail-discovery-relation-broken") | 126 | @hooks.hook("contrail-discovery-relation-broken") |
119 | 117 | def contrail_discovery_departed(): | 127 | def contrail_discovery_departed(): |
120 | @@ -120,13 +130,18 @@ | |||
121 | 120 | config["contrail-discovery-ready"] = False | 130 | config["contrail-discovery-ready"] = False |
122 | 121 | contrail_discovery_relation() | 131 | contrail_discovery_relation() |
123 | 122 | 132 | ||
127 | 123 | @restart_on_change({"/etc/contrail/contrail-control.conf": ["contrail-control"], | 133 | |
128 | 124 | "/etc/contrail/control-node.conf": ["contrail-control"], | 134 | @restart_on_change( |
129 | 125 | "/etc/contrail/contrail-control-nodemgr.conf": ["contrail-control-nodemgr"]}) | 135 | { |
130 | 136 | "/etc/contrail/contrail-control.conf": ["contrail-control"], | ||
131 | 137 | "/etc/contrail/control-node.conf": ["contrail-control"], | ||
132 | 138 | "/etc/contrail/contrail-control-nodemgr.conf": | ||
133 | 139 | ["contrail-control-nodemgr"]}) | ||
134 | 126 | def contrail_discovery_relation(): | 140 | def contrail_discovery_relation(): |
135 | 127 | write_control_config() | 141 | write_control_config() |
136 | 128 | write_nodemgr_config() | 142 | write_nodemgr_config() |
137 | 129 | 143 | ||
138 | 144 | |||
139 | 130 | @hooks.hook("contrail-ifmap-relation-changed") | 145 | @hooks.hook("contrail-ifmap-relation-changed") |
140 | 131 | def contrail_ifmap_changed(): | 146 | def contrail_ifmap_changed(): |
141 | 132 | creds = relation_get("creds") | 147 | creds = relation_get("creds") |
142 | @@ -138,6 +153,7 @@ | |||
143 | 138 | config["contrail-ifmap-ready"] = True | 153 | config["contrail-ifmap-ready"] = True |
144 | 139 | add_control() | 154 | add_control() |
145 | 140 | 155 | ||
146 | 156 | |||
147 | 141 | @hooks.hook("contrail-ifmap-relation-departed") | 157 | @hooks.hook("contrail-ifmap-relation-departed") |
148 | 142 | @hooks.hook("contrail-ifmap-relation-broken") | 158 | @hooks.hook("contrail-ifmap-relation-broken") |
149 | 143 | def contrail_ifmap_departed(): | 159 | def contrail_ifmap_departed(): |
150 | @@ -146,16 +162,21 @@ | |||
151 | 146 | config["contrail-ifmap-ready"] = False | 162 | config["contrail-ifmap-ready"] = False |
152 | 147 | contrail_ifmap_relation() | 163 | contrail_ifmap_relation() |
153 | 148 | 164 | ||
156 | 149 | @restart_on_change({"/etc/contrail/contrail-control.conf": ["contrail-control"], | 165 | |
157 | 150 | "/etc/contrail/control-node.conf": ["contrail-control"]}) | 166 | @restart_on_change( |
158 | 167 | { | ||
159 | 168 | "/etc/contrail/contrail-control.conf": ["contrail-control"], | ||
160 | 169 | "/etc/contrail/control-node.conf": ["contrail-control"]}) | ||
161 | 151 | def contrail_ifmap_relation(): | 170 | def contrail_ifmap_relation(): |
162 | 152 | write_control_config() | 171 | write_control_config() |
163 | 153 | 172 | ||
164 | 173 | |||
165 | 154 | @hooks.hook("control-node-relation-joined") | 174 | @hooks.hook("control-node-relation-joined") |
166 | 155 | def control_node_joined(): | 175 | def control_node_joined(): |
168 | 156 | settings = { "private-address": control_network_ip() } | 176 | settings = {"private-address": control_network_ip()} |
169 | 157 | relation_set(relation_settings=settings) | 177 | relation_set(relation_settings=settings) |
170 | 158 | 178 | ||
171 | 179 | |||
172 | 159 | @hooks.hook("identity-admin-relation-changed") | 180 | @hooks.hook("identity-admin-relation-changed") |
173 | 160 | def identity_admin_changed(): | 181 | def identity_admin_changed(): |
174 | 161 | if not relation_get("service_hostname"): | 182 | if not relation_get("service_hostname"): |
175 | @@ -165,6 +186,7 @@ | |||
176 | 165 | config["identity-admin-ready"] = True | 186 | config["identity-admin-ready"] = True |
177 | 166 | add_control() | 187 | add_control() |
178 | 167 | 188 | ||
179 | 189 | |||
180 | 168 | @hooks.hook("identity-admin-relation-departed") | 190 | @hooks.hook("identity-admin-relation-departed") |
181 | 169 | @hooks.hook("identity-admin-relation-broken") | 191 | @hooks.hook("identity-admin-relation-broken") |
182 | 170 | def identity_admin_departed(): | 192 | def identity_admin_departed(): |
183 | @@ -173,6 +195,50 @@ | |||
184 | 173 | config["identity-admin-ready"] = False | 195 | config["identity-admin-ready"] = False |
185 | 174 | write_vnc_api_config() | 196 | write_vnc_api_config() |
186 | 175 | 197 | ||
187 | 198 | |||
188 | 199 | @hooks.hook('tls-certificates-relation-joined') | ||
189 | 200 | def tls_certificates_relation_joined(): | ||
190 | 201 | # a hostname could also be provided as a SAN | ||
191 | 202 | # (Subject Alternative Name) but having this one | ||
192 | 203 | # has certain implications | ||
193 | 204 | # https://tools.ietf.org/html/rfc2818#section-3.1 | ||
194 | 205 | # "If a subjectAltName extension of type dNSName | ||
195 | 206 | # is present, that MUST be used as the identity" | ||
196 | 207 | # Therefore it is not used here as we don't need | ||
197 | 208 | # a DNS infrastructure dependency | ||
198 | 209 | ip_san = control_network_ip() | ||
199 | 210 | settings = { | ||
200 | 211 | 'sans': json.dumps([ip_san, '127.0.0.1']), | ||
201 | 212 | 'common_name': ip_san, | ||
202 | 213 | 'certificate_name': local_unit().replace('/', '_') | ||
203 | 214 | } | ||
204 | 215 | relation_set(relation_settings=settings) | ||
205 | 216 | |||
206 | 217 | |||
207 | 218 | @hooks.hook('tls-certificates-relation-changed') | ||
208 | 219 | def tls_certificates_relation_changed(): | ||
209 | 220 | # check that the -provides side have set the data we need | ||
210 | 221 | # and render the affected files | ||
211 | 222 | unitname = local_unit().replace('/', '_') | ||
212 | 223 | cert = '{0}.server.cert'.format(unitname) | ||
213 | 224 | key = '{0}.server.key'.format(unitname) | ||
214 | 225 | certv = relation_get(cert) | ||
215 | 226 | keyv = relation_get(key) | ||
216 | 227 | ca = relation_get('ca') | ||
217 | 228 | |||
218 | 229 | if certv and keyv and ca: | ||
219 | 230 | write_xmpp_tls_files(certv, keyv, ca) | ||
220 | 231 | config["tls-certificates-ready"] = True | ||
221 | 232 | else: | ||
222 | 233 | log('tls-certificates relation data is not fully available') | ||
223 | 234 | config["tls-certificates-ready"] = False | ||
224 | 235 | |||
225 | 236 | |||
226 | 237 | @hooks.hook('tls-certificates-relation-departed') | ||
227 | 238 | def tls_certificates_relation_departed(): | ||
228 | 239 | config["tls-certificates-ready"] = False | ||
229 | 240 | |||
230 | 241 | |||
231 | 176 | @hooks.hook() | 242 | @hooks.hook() |
232 | 177 | def install(): | 243 | def install(): |
233 | 178 | configure_sources(True, "install-sources", "install-keys") | 244 | configure_sources(True, "install-sources", "install-keys") |
234 | @@ -182,26 +248,32 @@ | |||
235 | 182 | fix_permissions() | 248 | fix_permissions() |
236 | 183 | fix_nodemgr() | 249 | fix_nodemgr() |
237 | 184 | 250 | ||
238 | 251 | |||
239 | 185 | def main(): | 252 | def main(): |
240 | 186 | try: | 253 | try: |
241 | 187 | hooks.execute(sys.argv) | 254 | hooks.execute(sys.argv) |
242 | 188 | except UnregisteredHookError as e: | 255 | except UnregisteredHookError as e: |
243 | 189 | log("Unknown hook {} - skipping.".format(e)) | 256 | log("Unknown hook {} - skipping.".format(e)) |
244 | 190 | 257 | ||
245 | 258 | |||
246 | 191 | def remove_control(): | 259 | def remove_control(): |
247 | 192 | if config_get("control-provisioned"): | 260 | if config_get("control-provisioned"): |
248 | 193 | unprovision_control() | 261 | unprovision_control() |
249 | 194 | config["control-provisioned"] = False | 262 | config["control-provisioned"] = False |
250 | 195 | 263 | ||
251 | 264 | |||
252 | 196 | @hooks.hook("upgrade-charm") | 265 | @hooks.hook("upgrade-charm") |
253 | 197 | def upgrade_charm(): | 266 | def upgrade_charm(): |
254 | 198 | write_control_config() | 267 | write_control_config() |
255 | 199 | write_nodemgr_config() | 268 | write_nodemgr_config() |
256 | 200 | service_restart("supervisor-control") | 269 | service_restart("supervisor-control") |
257 | 201 | 270 | ||
259 | 202 | @restart_on_change({"/etc/contrail/contrail-control.conf": ["contrail-control"]}) | 271 | |
260 | 272 | @restart_on_change( | ||
261 | 273 | {"/etc/contrail/contrail-control.conf": ["contrail-control"]}) | ||
262 | 203 | def write_config(): | 274 | def write_config(): |
263 | 204 | write_control_config() | 275 | write_control_config() |
264 | 205 | 276 | ||
265 | 277 | |||
266 | 206 | if __name__ == "__main__": | 278 | if __name__ == "__main__": |
267 | 207 | main() | 279 | main() |
268 | 208 | 280 | ||
269 | === modified file 'hooks/contrail_control_utils.py' | |||
270 | --- hooks/contrail_control_utils.py 2017-03-10 12:54:47 +0000 | |||
271 | +++ hooks/contrail_control_utils.py 2018-02-28 14:49:00 +0000 | |||
272 | @@ -21,26 +21,32 @@ | |||
273 | 21 | log, | 21 | log, |
274 | 22 | related_units, | 22 | related_units, |
275 | 23 | relation_get, | 23 | relation_get, |
276 | 24 | relation_set, | ||
277 | 24 | relation_ids, | 25 | relation_ids, |
278 | 25 | relation_type, | 26 | relation_type, |
279 | 26 | remote_unit, | 27 | remote_unit, |
281 | 27 | unit_get | 28 | unit_private_ip, |
282 | 29 | network_get_primary_address, | ||
283 | 28 | ) | 30 | ) |
284 | 29 | from charmhelpers.core.host import service_restart | 31 | from charmhelpers.core.host import service_restart |
285 | 30 | from charmhelpers.core.templating import render | 32 | from charmhelpers.core.templating import render |
286 | 31 | 33 | ||
287 | 32 | apt_pkg.init() | 34 | apt_pkg.init() |
288 | 33 | 35 | ||
289 | 36 | |||
290 | 34 | def dpkg_version(pkg): | 37 | def dpkg_version(pkg): |
291 | 35 | try: | 38 | try: |
293 | 36 | return check_output(["dpkg-query", "-f", "${Version}\\n", "-W", pkg]).rstrip() | 39 | return check_output( |
294 | 40 | ["dpkg-query", "-f", "${Version}\\n", "-W", pkg]).rstrip() | ||
295 | 37 | except CalledProcessError: | 41 | except CalledProcessError: |
296 | 38 | return None | 42 | return None |
297 | 39 | 43 | ||
298 | 44 | |||
299 | 40 | CONTRAIL_VERSION = dpkg_version("contrail-control") | 45 | CONTRAIL_VERSION = dpkg_version("contrail-control") |
300 | 41 | 46 | ||
301 | 42 | config = config() | 47 | config = config() |
302 | 43 | 48 | ||
303 | 49 | |||
304 | 44 | def retry(f=None, timeout=10, delay=2): | 50 | def retry(f=None, timeout=10, delay=2): |
305 | 45 | """Retry decorator. | 51 | """Retry decorator. |
306 | 46 | 52 | ||
307 | @@ -64,6 +70,7 @@ | |||
308 | 64 | """ | 70 | """ |
309 | 65 | if not f: | 71 | if not f: |
310 | 66 | return functools.partial(retry, timeout=timeout, delay=delay) | 72 | return functools.partial(retry, timeout=timeout, delay=delay) |
311 | 73 | |||
312 | 67 | @functools.wraps(f) | 74 | @functools.wraps(f) |
313 | 68 | def func(*args, **kwargs): | 75 | def func(*args, **kwargs): |
314 | 69 | start = time() | 76 | start = time() |
315 | @@ -84,29 +91,36 @@ | |||
316 | 84 | raise error | 91 | raise error |
317 | 85 | return func | 92 | return func |
318 | 86 | 93 | ||
319 | 94 | |||
320 | 87 | def contrail_api_ctx(): | 95 | def contrail_api_ctx(): |
327 | 88 | ctxs = [ { "api_server": gethostbyname(relation_get("private-address", unit, rid)), | 96 | ctxs = [{"api_server": gethostbyname(relation_get("private-address", |
328 | 89 | "api_port": port } | 97 | unit, rid)), |
329 | 90 | for rid in relation_ids("contrail-api") | 98 | "api_port": port} |
330 | 91 | for unit, port in | 99 | for rid in relation_ids("contrail-api") |
331 | 92 | ((unit, relation_get("port", unit, rid)) for unit in related_units(rid)) | 100 | for unit, port in |
332 | 93 | if port ] | 101 | ((unit, relation_get("port", unit, rid)) |
333 | 102 | for unit in related_units(rid)) | ||
334 | 103 | if port] | ||
335 | 94 | return ctxs[0] if ctxs else {} | 104 | return ctxs[0] if ctxs else {} |
336 | 95 | 105 | ||
337 | 106 | |||
338 | 96 | def contrail_ctx(): | 107 | def contrail_ctx(): |
340 | 97 | return { "host_ip": control_network_ip() } | 108 | return {"host_ip": control_network_ip()} |
341 | 109 | |||
342 | 98 | 110 | ||
343 | 99 | def contrail_discovery_ctx(): | 111 | def contrail_discovery_ctx(): |
352 | 100 | ctxs = [ { "discovery_server": vip if vip \ | 112 | ctxs = [{"discovery_server": vip if vip |
353 | 101 | else gethostbyname(relation_get("private-address", unit, rid)), | 113 | else gethostbyname(relation_get("private-address", unit, rid)), |
354 | 102 | "discovery_port": port } | 114 | "discovery_port": port} |
355 | 103 | for rid in relation_ids("contrail-discovery") | 115 | for rid in relation_ids("contrail-discovery") |
356 | 104 | for unit, port, vip in | 116 | for unit, port, vip in |
357 | 105 | ((unit, relation_get("port", unit, rid), relation_get("vip", unit, rid)) | 117 | ((unit, relation_get("port", unit, rid), |
358 | 106 | for unit in related_units(rid)) | 118 | relation_get("vip", unit, rid)) |
359 | 107 | if port ] | 119 | for unit in related_units(rid)) |
360 | 120 | if port] | ||
361 | 108 | return ctxs[0] if ctxs else {} | 121 | return ctxs[0] if ctxs else {} |
362 | 109 | 122 | ||
363 | 123 | |||
364 | 110 | def contrail_ifmap_ctx(): | 124 | def contrail_ifmap_ctx(): |
365 | 111 | ctxs = [] | 125 | ctxs = [] |
366 | 112 | unit = local_unit() | 126 | unit = local_unit() |
367 | @@ -123,6 +137,7 @@ | |||
368 | 123 | ctxs.append(ctx) | 137 | ctxs.append(ctx) |
369 | 124 | return ctxs[0] if ctxs else {} | 138 | return ctxs[0] if ctxs else {} |
370 | 125 | 139 | ||
371 | 140 | |||
372 | 126 | @retry(timeout=300) | 141 | @retry(timeout=300) |
373 | 127 | def contrail_provision_control(hostname, ip, router_asn, api_ip, api_port, op, | 142 | def contrail_provision_control(hostname, ip, router_asn, api_ip, api_port, op, |
374 | 128 | user, password, tenant): | 143 | user, password, tenant): |
375 | @@ -137,19 +152,42 @@ | |||
376 | 137 | "--admin_password", password, | 152 | "--admin_password", password, |
377 | 138 | "--admin_tenant_name", tenant]) | 153 | "--admin_tenant_name", tenant]) |
378 | 139 | 154 | ||
379 | 155 | |||
380 | 140 | def control_network_ip(): | 156 | def control_network_ip(): |
387 | 141 | fallback = gethostbyname(unit_get("private-address")) | 157 | ''' |
388 | 142 | network = config.get("control-network") | 158 | With Juju 2.x, uses an endpoint (relation) |
389 | 143 | if network: | 159 | network space binding if unspecified will use a "unit private address" |
390 | 144 | return get_address_in_network(network, fallback) | 160 | which is far less explicit if you look at the Juju implementation. |
391 | 145 | else: | 161 | If you use Juju 2.x and above - bind the control-node endpoint to a network |
392 | 146 | return fallback | 162 | space to get a proper address in this function. |
393 | 163 | |||
394 | 164 | If network-get throws an exception (juju 1.x or spaces are not supported) | ||
395 | 165 | will try to fall back to the control-network parameter or a private-address | ||
396 | 166 | as returned by Juju. | ||
397 | 167 | ''' | ||
398 | 168 | try: | ||
399 | 169 | address = network_get_primary_address('control-node') | ||
400 | 170 | except NotImplementedError: | ||
401 | 171 | log('Network spaces are not implemented - falling back to' | ||
402 | 172 | ' getting a private address') | ||
403 | 173 | address = None | ||
404 | 174 | |||
405 | 175 | if not address: | ||
406 | 176 | fallback = gethostbyname(unit_private_ip()) | ||
407 | 177 | network = config.get("control-network") | ||
408 | 178 | if network: | ||
409 | 179 | address = get_address_in_network(network, fallback) | ||
410 | 180 | else: | ||
411 | 181 | address = fallback | ||
412 | 182 | |||
413 | 183 | return address | ||
414 | 184 | |||
415 | 147 | 185 | ||
416 | 148 | def fix_nodemgr(): | 186 | def fix_nodemgr(): |
417 | 149 | # add files missing from contrail-nodemgr package | 187 | # add files missing from contrail-nodemgr package |
418 | 150 | dest = "/etc/contrail/supervisord_control_files/" \ | 188 | dest = "/etc/contrail/supervisord_control_files/" \ |
421 | 151 | + ("contrail-control-nodemgr.ini" \ | 189 | + ("contrail-control-nodemgr.ini" |
422 | 152 | if version_compare(CONTRAIL_VERSION, "3.1") >= 0 \ | 190 | if version_compare(CONTRAIL_VERSION, "3.1") >= 0 |
423 | 153 | else "contrail-nodemgr-control.ini") | 191 | else "contrail-nodemgr-control.ini") |
424 | 154 | shutil.copy("files/contrail-nodemgr-control.ini", dest) | 192 | shutil.copy("files/contrail-nodemgr-control.ini", dest) |
425 | 155 | pw = pwd.getpwnam("contrail") | 193 | pw = pwd.getpwnam("contrail") |
426 | @@ -159,7 +197,7 @@ | |||
427 | 159 | if version_compare(CONTRAIL_VERSION, "3.1") >= 0 \ | 197 | if version_compare(CONTRAIL_VERSION, "3.1") >= 0 \ |
428 | 160 | else "files/contrail-control-nodemgr" | 198 | else "files/contrail-control-nodemgr" |
429 | 161 | shutil.copy(src, "/etc/init.d/contrail-control-nodemgr") | 199 | shutil.copy(src, "/etc/init.d/contrail-control-nodemgr") |
431 | 162 | os.chmod("/etc/init.d/contrail-control-nodemgr", 0755) | 200 | os.chmod("/etc/init.d/contrail-control-nodemgr", 0o755) |
432 | 163 | 201 | ||
433 | 164 | # fake ntp status when inside a container | 202 | # fake ntp status when inside a container |
434 | 165 | if is_container(): | 203 | if is_container(): |
435 | @@ -167,20 +205,24 @@ | |||
436 | 167 | 205 | ||
437 | 168 | service_restart("supervisor-control") | 206 | service_restart("supervisor-control") |
438 | 169 | 207 | ||
439 | 208 | |||
440 | 170 | def fix_permissions(): | 209 | def fix_permissions(): |
442 | 171 | os.chmod("/etc/contrail", 0755) | 210 | os.chmod("/etc/contrail", 0o755) |
443 | 172 | os.chown("/etc/contrail", 0, 0) | 211 | os.chown("/etc/contrail", 0, 0) |
444 | 173 | 212 | ||
445 | 213 | |||
446 | 174 | def identity_admin_ctx(): | 214 | def identity_admin_ctx(): |
454 | 175 | ctxs = [ { "auth_host": gethostbyname(hostname), | 215 | ctxs = [{"auth_host": gethostbyname(hostname), |
455 | 176 | "auth_port": relation_get("service_port", unit, rid), | 216 | "auth_port": relation_get("service_port", unit, rid), |
456 | 177 | "auth_protocol": relation_get("service_protocol", unit, rid) } | 217 | "auth_protocol": relation_get("service_protocol", unit, rid)} |
457 | 178 | for rid in relation_ids("identity-admin") | 218 | for rid in relation_ids("identity-admin") |
458 | 179 | for unit, hostname in | 219 | for unit, hostname in |
459 | 180 | ((unit, relation_get("service_hostname", unit, rid)) for unit in related_units(rid)) | 220 | ((unit, relation_get("service_hostname", unit, rid)) |
460 | 181 | if hostname ] | 221 | for unit in related_units(rid)) |
461 | 222 | if hostname] | ||
462 | 182 | return ctxs[0] if ctxs else {} | 223 | return ctxs[0] if ctxs else {} |
463 | 183 | 224 | ||
464 | 225 | |||
465 | 184 | def is_container(): | 226 | def is_container(): |
466 | 185 | """Return boolean determining if inside container""" | 227 | """Return boolean determining if inside container""" |
467 | 186 | try: | 228 | try: |
468 | @@ -189,26 +231,36 @@ | |||
469 | 189 | except CalledProcessError: | 231 | except CalledProcessError: |
470 | 190 | return False | 232 | return False |
471 | 191 | 233 | ||
472 | 234 | |||
473 | 192 | def provision_control(): | 235 | def provision_control(): |
474 | 193 | hostname = gethostname() | 236 | hostname = gethostname() |
475 | 194 | ip = control_network_ip() | 237 | ip = control_network_ip() |
488 | 195 | api_ip, api_port = [ (gethostbyname(relation_get("private-address", unit, rid)), | 238 | api_ip, api_port = [(gethostbyname(relation_get("private-address", |
489 | 196 | port) | 239 | unit, rid)), port) |
490 | 197 | for rid in relation_ids("contrail-api") | 240 | for rid in relation_ids("contrail-api") |
491 | 198 | for unit, port in | 241 | for unit, port in |
492 | 199 | ((unit, relation_get("port", unit, rid)) for unit in related_units(rid)) | 242 | ((unit, relation_get("port", unit, rid)) |
493 | 200 | if port ][0] | 243 | for unit in related_units(rid)) |
494 | 201 | user, password, tenant = [ (relation_get("service_username", unit, rid), | 244 | if port][0] |
495 | 202 | relation_get("service_password", unit, rid), | 245 | user, password, tenant = [(relation_get("service_username", unit, rid), |
496 | 203 | relation_get("service_tenant_name", unit, rid)) | 246 | relation_get("service_password", unit, rid), |
497 | 204 | for rid in relation_ids("identity-admin") | 247 | relation_get("service_tenant_name", unit, rid)) |
498 | 205 | for unit in related_units(rid) | 248 | for rid in relation_ids("identity-admin") |
499 | 206 | if relation_get("service_hostname", unit, rid) ][0] | 249 | for unit in related_units(rid) |
500 | 250 | if relation_get("service_hostname", | ||
501 | 251 | unit, rid)][0] | ||
502 | 207 | log("Provisioning control {}".format(ip)) | 252 | log("Provisioning control {}".format(ip)) |
503 | 208 | contrail_provision_control(hostname, ip, 64512, api_ip, api_port, "add", | 253 | contrail_provision_control(hostname, ip, 64512, api_ip, api_port, "add", |
504 | 209 | user, password, tenant) | 254 | user, password, tenant) |
505 | 255 | |||
506 | 256 | # support for network spaces | ||
507 | 257 | # see control_network_ip implementation | ||
508 | 258 | for rid in relation_ids("control-node"): | ||
509 | 259 | relation_set(rid, {'control_node_ip': ip}) | ||
510 | 260 | |||
511 | 210 | config["provisioned-host-ip"] = ip | 261 | config["provisioned-host-ip"] = ip |
512 | 211 | 262 | ||
513 | 263 | |||
514 | 212 | def remove_ssl_ca_certificate(): | 264 | def remove_ssl_ca_certificate(): |
515 | 213 | if os.path.exists("/usr/local/share/ca-certificates/contrail-juju.crt"): | 265 | if os.path.exists("/usr/local/share/ca-certificates/contrail-juju.crt"): |
516 | 214 | os.remove("/usr/local/share/ca-certificates/contrail-juju.crt") | 266 | os.remove("/usr/local/share/ca-certificates/contrail-juju.crt") |
517 | @@ -217,10 +269,12 @@ | |||
518 | 217 | else: | 269 | else: |
519 | 218 | return False | 270 | return False |
520 | 219 | 271 | ||
521 | 272 | |||
522 | 220 | def units(relation): | 273 | def units(relation): |
523 | 221 | """Return a list of units for the specified relation""" | 274 | """Return a list of units for the specified relation""" |
526 | 222 | return [ unit for rid in relation_ids(relation) | 275 | return [unit for rid in relation_ids(relation) |
527 | 223 | for unit in related_units(rid) ] | 276 | for unit in related_units(rid)] |
528 | 277 | |||
529 | 224 | 278 | ||
530 | 225 | def unprovision_control(): | 279 | def unprovision_control(): |
531 | 226 | relation = relation_type() | 280 | relation = relation_type() |
532 | @@ -234,10 +288,11 @@ | |||
533 | 234 | api_ip = gethostbyname(relation_get("private-address")) | 288 | api_ip = gethostbyname(relation_get("private-address")) |
534 | 235 | api_port = relation_get("port") | 289 | api_port = relation_get("port") |
535 | 236 | else: | 290 | else: |
540 | 237 | api_ip, api_port = [ (gethostbyname(relation_get("private-address", unit, rid)), | 291 | api_ip, api_port = [(gethostbyname(relation_get("private-address", |
541 | 238 | relation_get("port", unit, rid)) | 292 | unit, rid)), |
542 | 239 | for rid in relation_ids("contrail-api") | 293 | relation_get("port", unit, rid)) |
543 | 240 | for unit in related_units(rid) ][0] | 294 | for rid in relation_ids("contrail-api") |
544 | 295 | for unit in related_units(rid)][0] | ||
545 | 241 | user = None | 296 | user = None |
546 | 242 | password = None | 297 | password = None |
547 | 243 | tenant = None | 298 | tenant = None |
548 | @@ -246,37 +301,58 @@ | |||
549 | 246 | password = relation_get("service_password") | 301 | password = relation_get("service_password") |
550 | 247 | tenant = relation_get("service_tenant_name") | 302 | tenant = relation_get("service_tenant_name") |
551 | 248 | else: | 303 | else: |
557 | 249 | user, password, tenant = [ (relation_get("service_username", unit, rid), | 304 | user, password, tenant = [(relation_get("service_username", unit, rid), |
558 | 250 | relation_get("service_password", unit, rid), | 305 | relation_get("service_password", unit, rid), |
559 | 251 | relation_get("service_tenant_name", unit, rid)) | 306 | relation_get("service_tenant_name", |
560 | 252 | for rid in relation_ids("identity-admin") | 307 | unit, rid)) |
561 | 253 | for unit in related_units(rid) ][0] | 308 | for rid in relation_ids("identity-admin") |
562 | 309 | for unit in related_units(rid)][0] | ||
563 | 254 | log("Unprovisioning control {}".format(ip)) | 310 | log("Unprovisioning control {}".format(ip)) |
564 | 255 | try: | 311 | try: |
567 | 256 | contrail_provision_control(hostname, ip, 64512, api_ip, api_port, "del", | 312 | contrail_provision_control( |
568 | 257 | user, password, tenant) | 313 | hostname, |
569 | 314 | ip, | ||
570 | 315 | 64512, | ||
571 | 316 | api_ip, | ||
572 | 317 | api_port, | ||
573 | 318 | "del", | ||
574 | 319 | user, | ||
575 | 320 | password, | ||
576 | 321 | tenant) | ||
577 | 258 | except CalledProcessError: | 322 | except CalledProcessError: |
578 | 259 | pass | 323 | pass |
579 | 260 | del config["provisioned-host-ip"] | 324 | del config["provisioned-host-ip"] |
580 | 261 | 325 | ||
581 | 326 | |||
582 | 262 | def write_control_config(): | 327 | def write_control_config(): |
583 | 263 | ctx = {} | 328 | ctx = {} |
584 | 264 | ctx.update(contrail_ctx()) | 329 | ctx.update(contrail_ctx()) |
585 | 265 | ctx.update(contrail_discovery_ctx()) | 330 | ctx.update(contrail_discovery_ctx()) |
586 | 266 | ctx.update(contrail_ifmap_ctx()) | 331 | ctx.update(contrail_ifmap_ctx()) |
587 | 332 | |||
588 | 333 | xmpp_auth_enable = config.get('xmpp_auth') | ||
589 | 334 | ctx.update({"xmpp_auth_enable": xmpp_auth_enable}) | ||
590 | 335 | |||
591 | 336 | # a tls-certificates guard here is for the upgrade scenario | ||
592 | 337 | tls_implemented = version_compare(CONTRAIL_VERSION, "3.0") >= 0 and\ | ||
593 | 338 | config.get("tls-certificates-ready") | ||
594 | 339 | ctx.update({'tls_implemented': tls_implemented}) | ||
595 | 267 | target = "/etc/contrail/contrail-control.conf" \ | 340 | target = "/etc/contrail/contrail-control.conf" \ |
596 | 268 | if version_compare(CONTRAIL_VERSION, "2.0") >= 0 \ | 341 | if version_compare(CONTRAIL_VERSION, "2.0") >= 0 \ |
597 | 269 | else "/etc/contrail/control-node.conf" | 342 | else "/etc/contrail/control-node.conf" |
599 | 270 | render("control-node.conf", target, ctx, "root", "contrail", 0440) | 343 | render("control-node.conf", target, ctx, "root", "contrail", 0o440) |
600 | 344 | |||
601 | 271 | 345 | ||
602 | 272 | def write_nodemgr_config(): | 346 | def write_nodemgr_config(): |
603 | 273 | ctx = contrail_discovery_ctx() | 347 | ctx = contrail_discovery_ctx() |
604 | 274 | render("contrail-control-nodemgr.conf", | 348 | render("contrail-control-nodemgr.conf", |
605 | 275 | "/etc/contrail/contrail-control-nodemgr.conf", ctx) | 349 | "/etc/contrail/contrail-control-nodemgr.conf", ctx) |
606 | 276 | 350 | ||
607 | 351 | |||
608 | 277 | def write_ssl_ca_certificate(cert): | 352 | def write_ssl_ca_certificate(cert): |
609 | 278 | if os.path.exists("/usr/local/share/ca-certificates/contrail-juju.crt"): | 353 | if os.path.exists("/usr/local/share/ca-certificates/contrail-juju.crt"): |
611 | 279 | with open("/usr/local/share/ca-certificates/contrail-juju.crt", "r") as f: | 354 | with open("/usr/local/share/ca-certificates/contrail-juju.crt", "r") \ |
612 | 355 | as f: | ||
613 | 280 | c = f.read() | 356 | c = f.read() |
614 | 281 | if c == cert: | 357 | if c == cert: |
615 | 282 | return False | 358 | return False |
616 | @@ -285,6 +361,30 @@ | |||
617 | 285 | check_call(["update-ca-certificates"]) | 361 | check_call(["update-ca-certificates"]) |
618 | 286 | return True | 362 | return True |
619 | 287 | 363 | ||
620 | 364 | |||
621 | 365 | def write_xmpp_tls_files(serv_cert, priv_key, ca): | ||
622 | 366 | prefix = '/etc/contrail/ssl' | ||
623 | 367 | certs = os.path.join(prefix, 'certs') | ||
624 | 368 | private = os.path.join(prefix, 'private') | ||
625 | 369 | |||
626 | 370 | entry = pwd.getpwnam('contrail') | ||
627 | 371 | for p in [prefix, certs, private]: | ||
628 | 372 | if not os.path.exists(p): | ||
629 | 373 | os.makedirs(p, 0o750) | ||
630 | 374 | os.chown(p, entry.pw_uid, entry.pw_gid) | ||
631 | 375 | |||
632 | 376 | fcontent = { | ||
633 | 377 | os.path.join(certs, 'server.pem'): serv_cert, | ||
634 | 378 | os.path.join(private, 'server-privkey.pem'): priv_key, | ||
635 | 379 | os.path.join(certs, 'ca-cert.pem'): ca, | ||
636 | 380 | } | ||
637 | 381 | |||
638 | 382 | for filepath, content in fcontent.iteritems(): | ||
639 | 383 | with open(filepath, 'w+') as f: | ||
640 | 384 | f.truncate(0) | ||
641 | 385 | f.write(content) | ||
642 | 386 | |||
643 | 387 | |||
644 | 288 | def write_vnc_api_config(): | 388 | def write_vnc_api_config(): |
645 | 289 | ctx = {} | 389 | ctx = {} |
646 | 290 | ctx.update(contrail_api_ctx()) | 390 | ctx.update(contrail_api_ctx()) |
647 | 291 | 391 | ||
648 | === added symlink 'hooks/tls-certificates-relation-changed' | |||
649 | === target is u'contrail_control_hooks.py' | |||
650 | === added symlink 'hooks/tls-certificates-relation-departed' | |||
651 | === target is u'contrail_control_hooks.py' | |||
652 | === added symlink 'hooks/tls-certificates-relation-joined' | |||
653 | === target is u'contrail_control_hooks.py' | |||
654 | === modified file 'metadata.yaml' | |||
655 | --- metadata.yaml 2015-09-17 21:09:59 +0000 | |||
656 | +++ metadata.yaml 2018-02-28 14:49:00 +0000 | |||
657 | @@ -20,3 +20,5 @@ | |||
658 | 20 | interface: contrail-ifmap | 20 | interface: contrail-ifmap |
659 | 21 | identity-admin: | 21 | identity-admin: |
660 | 22 | interface: keystone-admin | 22 | interface: keystone-admin |
661 | 23 | tls-certificates: | ||
662 | 24 | interface: tls-certificates | ||
663 | 23 | 25 | ||
664 | === modified file 'templates/control-node.conf' | |||
665 | --- templates/control-node.conf 2015-10-01 12:29:04 +0000 | |||
666 | +++ templates/control-node.conf 2018-02-28 14:49:00 +0000 | |||
667 | @@ -6,6 +6,16 @@ | |||
668 | 6 | [DEFAULT] | 6 | [DEFAULT] |
669 | 7 | hostip = {{ host_ip }} | 7 | hostip = {{ host_ip }} |
670 | 8 | 8 | ||
671 | 9 | {% if xmpp_auth_enable -%} | ||
672 | 10 | xmpp_auth_enable=true | ||
673 | 11 | {% endif -%} | ||
674 | 12 | |||
675 | 13 | {% if tls_implemented -%} | ||
676 | 14 | xmpp_server_cert=/etc/contrail/ssl/certs/server.pem | ||
677 | 15 | xmpp_server_key=/etc/contrail/ssl/private/server-privkey.pem | ||
678 | 16 | xmpp_ca_cert=/etc/contrail/ssl/certs/ca-cert.pem | ||
679 | 17 | {% endif -%} | ||
680 | 18 | |||
681 | 9 | [DISCOVERY] | 19 | [DISCOVERY] |
682 | 10 | server = {{ discovery_server }} | 20 | server = {{ discovery_server }} |
683 | 11 | port = {{ discovery_port }} | 21 | port = {{ discovery_port }} |
684 | @@ -13,4 +23,3 @@ | |||
685 | 13 | [IFMAP] | 23 | [IFMAP] |
686 | 14 | user = {{ ifmap_user }} | 24 | user = {{ ifmap_user }} |
687 | 15 | password = {{ ifmap_password }} | 25 | password = {{ ifmap_password }} |
688 | 16 |
Please merge