Merge lp:~gandelman-a/charms/precise/nova-cloud-controller/https_endpoint into lp:~openstack-charmers/charms/precise/nova-cloud-controller/ha-support
- Precise Pangolin (12.04)
- https_endpoint
- Merge into ha-support
Status: | Merged |
---|---|
Merged at revision: | 64 |
Proposed branch: | lp:~gandelman-a/charms/precise/nova-cloud-controller/https_endpoint |
Merge into: | lp:~openstack-charmers/charms/precise/nova-cloud-controller/ha-support |
Diff against target: |
903 lines (+467/-102) 5 files modified
config.yaml (+10/-0) hooks/lib/openstack-common (+219/-18) hooks/nova-cloud-controller-common (+71/-0) hooks/nova-cloud-controller-relations (+166/-83) revision (+1/-1) |
To merge this branch: | bzr merge lp:~gandelman-a/charms/precise/nova-cloud-controller/https_endpoint |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Page | Needs Fixing | ||
Review via email: mp+150387@code.launchpad.net |
Commit message
Description of the change
nova-c-c HTTPS API support. These changes (and also those applied to the other chnages in similar merge proposals) allows the charm to dynamically manage the API listening port for each server depending on the current deployment. The canonical port in the Keystone catalog will always be the default (eg 8443) but the request pipeline on the API server differs depending on how it is deployed:
- When deployed in a single, non-https unit, the API server(s) will listen on their defualt port (eg, 8773)
- When new peers join, haproxy will be configured on every node to listen on the default port, with the local API server listening on $DEFAULT-10 (8773 -> 8763).
- When HTTPS is enabled and peers have joined, Apache is configured to terminate SSL on the default port and route requests to the local haproxy, then to the API server (8773 apache -> 8763 haproxy -> 8752 nova api).
- When HTTPS is enabled but no peers have joined, Apache terminates SSL on default port and routes requeusts directly to the API server (8773 -> 8763)
The same approach is used in the other HTTPS charms.
SSL certs are created and signed on the keystone side (when it is configured to do so) along with standard endpoint creation. An SSL cert, key and CA cert are returned to the unit along with its service credentials. Currently, nova-c-c will make a copy of the CA cert accessible to outside users at its web root.
This also adds a lot of general HA support that was committed to other charms but not to the nova-c-c (eligible_leader, etc).
James Page (james-page) wrote : | # |
James Page (james-page) wrote : | # |
I was able to get most of the https stuff working; two issues
1) Setting the config on keystone for https after building the environment configured endpoints in most places; however nova-cloud-
2) nova image-list post remove/add to fix the above resulted in:
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack Traceback (most recent call last):
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack return req.get_
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack application, catch_exc_
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack app_iter = application(
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack return resp(environ, start_response)
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack return self.app(env, start_response)
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack return resp(environ, start_response)
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack return resp(environ, start_response)
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack return resp(environ, start_response)
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack response = self.app(environ, start_response)
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack return resp(environ, start_response)
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack resp = self.call_func(req, *args, **self.kwargs)
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack File "/usr/lib/
2013-02-26 07:50:41.481 1056 TRACE nova.api.openstack return self.func(req, *ar...
James Page (james-page) : | # |
- 75. By Adam Gandelman
-
Actually send clone config to hacluster.
- 76. By Adam Gandelman
-
Send CA cert to quantum via quantum_joined, if it exists then.
- 77. By Adam Gandelman
-
Retrigger quantum_joined post-SSL.
- 78. By Adam Gandelman
-
Bump revision.
Adam Gandelman (gandelman-a) wrote : | # |
I'm having a helluva time reproducing this issue. Can you describe how you initial built your environment before enabling HTTPS? Was nova-c-c peered and/or clustered? Are you sure you were deploying the most recent charm revision /w newest branch changes? bzr revs 71 and 72 dealt with an issue of the endpoint not reconfiguring after adding clustering, since the original reconfig hooks were not sending all required settings. Late-enabling HTTPS should be limited to identity-changed, though.
I've just tested again by enabling HTTPS after the entire catalog has been populated by haclustererd services (all endpoints pointing to http://
2013-02-27 15:09:31,482: hook.output@DEBUG: Flushed values for hook 'identity-
Setting changed: 'ec2_admin_url'=u'https:/
Setting changed: 'ec2_internal_
Setting changed: 'ec2_public_url'=u'https:/
Setting changed: 'nova_admin_url'=u'https:/
Setting changed: 'nova_internal_
Setting changed: 'nova_public_
Setting changed: 'quantum_
Setting changed: 'quantum_
Setting changed: 'quantum_
Setting changed: 's3_admin_url'=u'https:/
Setting changed: 's3_internal_
Setting changed: 's3_public_url'=u'https:/
Setting changed: u'ca_cert'
Setting changed: 'quantum_url'=u'https:/
Setting changed: u'ca_cert'
Setting changed: 'quantum_url'=u'https:/
2013-02-27 15:09:31,483: hook.executor@
2/charm/
If you can test once again, and possibly keep an eye on the nova-c-c leader's charm log, to see what settings change? The r...
- 79. By Adam Gandelman
-
Rebase against current ha-support branch.
- 80. By Adam Gandelman
-
Block configure_
qunatum_ networking if no amqp relation exists. - 81. By Adam Gandelman
-
Avoid race between https + quantum network setup.
using https() in keystone_joined() is not reliable with multiple
KS peers. Only inspect local config and count on https() only from
_changed() hooks. - 82. By Adam Gandelman
-
Bump rev.
- 83. By Adam Gandelman
-
Only configurate quantum net. from keystone-changed when safe.
They ks catalog may be in a state of flux when these hooks fire.
If firing for the first time, configure quantum networking before
https frontend is setup, and before KS endpoint is reconfigured to
point to https.If firing as the result of HA reconfiguration, configure quantum
after HTTPS has been updated to include updated certificate (for VIP
address). - 84. By Adam Gandelman
-
keystone_changed: Protect late quantum config by an eligible_leader check.
- 85. By Adam Gandelman
-
Also protect early call to quantum config.
- 86. By Adam Gandelman
-
keystone_changed: Also determine HTTPS based on actual hook being fired.
- 87. By Adam Gandelman
-
Be careful of 'set -e'.
Preview Diff
1 | === modified file 'config.yaml' |
2 | --- config.yaml 2013-01-22 16:43:49 +0000 |
3 | +++ config.yaml 2013-03-09 04:30:28 +0000 |
4 | @@ -128,3 +128,13 @@ |
5 | description: | |
6 | Default multicast port number that will be used to communicate between |
7 | HA Cluster nodes. |
8 | + ssl_cert: |
9 | + type: string |
10 | + description: | |
11 | + SSL certificate to install and use for API ports. Setting this value |
12 | + and ssl_key will enable reverse proxying, point Glance's entry in the |
13 | + Keystone catalog to use https, and override any certficiate and key |
14 | + issued by Keystone (if it is configured to do so). |
15 | + ssl_key: |
16 | + type: string |
17 | + description: SSL key to use with certificate specified as ssl_cert. |
18 | |
19 | === added symlink 'hooks/cluster-relation-changed' |
20 | === target is u'nova-cloud-controller-relations' |
21 | === added symlink 'hooks/cluster-relation-departed' |
22 | === target is u'nova-cloud-controller-relations' |
23 | === added symlink 'hooks/ha-relation-changed' |
24 | === target is u'nova-cloud-controller-relations' |
25 | === added symlink 'hooks/ha-relation-joined' |
26 | === target is u'nova-cloud-controller-relations' |
27 | === modified file 'hooks/lib/openstack-common' |
28 | --- hooks/lib/openstack-common 2013-03-08 21:18:29 +0000 |
29 | +++ hooks/lib/openstack-common 2013-03-09 04:30:28 +0000 |
30 | @@ -321,7 +321,6 @@ |
31 | |
32 | HAPROXY_CFG=/etc/haproxy/haproxy.cfg |
33 | HAPROXY_DEFAULT=/etc/default/haproxy |
34 | - |
35 | ########################################################################## |
36 | # Description: Configures HAProxy services for Openstack API's |
37 | # Parameters: |
38 | @@ -330,9 +329,8 @@ |
39 | # assumes the name of the peer relation is 'cluster' and that every |
40 | # service unit in the peer relation is running the same services. |
41 | # |
42 | -# The HAProxy service will listen on port + 10000. |
43 | -# Example: |
44 | -# configure_haproxy cinder_api:12345 nova_api:9999 |
45 | +# Example |
46 | +# configure_haproxy cinder_api:8776:8756i nova_api:8774:8764 |
47 | ########################################################################## |
48 | configure_haproxy() { |
49 | local address=`unit-get private-address` |
50 | @@ -368,14 +366,18 @@ |
51 | EOF |
52 | for service in $@; do |
53 | local service_name=$(echo $service | cut -d : -f 1) |
54 | - local api_listen_port=$(echo $service | cut -d : -f 2) |
55 | - local haproxy_listen_port=$(($api_listen_port + 10000)) |
56 | + local haproxy_listen_port=$(echo $service | cut -d : -f 2) |
57 | + local api_listen_port=$(echo $service | cut -d : -f 3) |
58 | + juju-log "Adding haproxy configuration entry for $service "\ |
59 | + "($haproxy_listen_port -> $api_listen_port)" |
60 | cat >> $HAPROXY_CFG << EOF |
61 | listen $service_name 0.0.0.0:$haproxy_listen_port |
62 | balance roundrobin |
63 | option tcplog |
64 | server $name $address:$api_listen_port check |
65 | EOF |
66 | + local r_id="" |
67 | + local unit="" |
68 | for r_id in `relation-ids cluster`; do |
69 | for unit in `relation-list -r $r_id`; do |
70 | local unit_name=${unit////-} |
71 | @@ -388,6 +390,7 @@ |
72 | done |
73 | done |
74 | echo "ENABLED=1" > $HAPROXY_DEFAULT |
75 | + service haproxy restart |
76 | } |
77 | |
78 | ########################################################################## |
79 | @@ -395,18 +398,20 @@ |
80 | # Returns: 0 if configured, 1 if not configured |
81 | ########################################################################## |
82 | is_clustered() { |
83 | + local r_id="" |
84 | + local unit="" |
85 | for r_id in $(relation-ids ha); do |
86 | if [ -n "$r_id" ]; then |
87 | for unit in $(relation-list -r $r_id); do |
88 | clustered=$(relation-get -r $r_id clustered $unit) |
89 | if [ -n "$clustered" ]; then |
90 | - echo "Unit is clustered" |
91 | + juju-log "Unit is haclustered" |
92 | return 0 |
93 | fi |
94 | done |
95 | fi |
96 | done |
97 | - echo "Unit is not clustered" |
98 | + juju-log "Unit is not haclustered" |
99 | return 1 |
100 | } |
101 | |
102 | @@ -415,6 +420,7 @@ |
103 | ########################################################################## |
104 | peer_units() { |
105 | local peers="" |
106 | + local r_id="" |
107 | for r_id in $(relation-ids cluster); do |
108 | peers="$peers $(relation-list -r $r_id)" |
109 | done |
110 | @@ -433,11 +439,11 @@ |
111 | echo "Comparing $JUJU_UNIT_NAME with peers: $peers" |
112 | local r_unit_no=$(echo $peer | cut -d / -f 2) |
113 | if (($r_unit_no<$l_unit_no)); then |
114 | - echo "Not oldest peer; deferring" |
115 | + juju-log "Not oldest peer; deferring" |
116 | return 1 |
117 | fi |
118 | done |
119 | - echo "Oldest peer; might take charge?" |
120 | + juju-log "Oldest peer; might take charge?" |
121 | return 0 |
122 | } |
123 | |
124 | @@ -451,13 +457,13 @@ |
125 | eligible_leader() { |
126 | if is_clustered; then |
127 | if ! is_leader $1; then |
128 | - echo 'Deferring action to CRM leader' |
129 | + juju-log 'Deferring action to CRM leader' |
130 | return 1 |
131 | fi |
132 | else |
133 | peers=$(peer_units) |
134 | if [ -n "$peers" ] && ! oldest_peer "$peers"; then |
135 | - echo 'Deferring action to oldest service unit.' |
136 | + juju-log 'Deferring action to oldest service unit.' |
137 | return 1 |
138 | fi |
139 | fi |
140 | @@ -469,14 +475,14 @@ |
141 | # Returns: 0 if peered, 1 if not peered |
142 | ########################################################################## |
143 | is_peered() { |
144 | - r_id=$(relation-ids cluster) |
145 | + local r_id=$(relation-ids cluster) |
146 | if [ -n "$r_id" ]; then |
147 | if [ -n "$(relation-list -r $r_id)" ]; then |
148 | - echo "Unit peered" |
149 | + juju-log "Unit peered" |
150 | return 0 |
151 | fi |
152 | fi |
153 | - echo "Unit not peered" |
154 | + juju-log "Unit not peered" |
155 | return 1 |
156 | } |
157 | |
158 | @@ -489,12 +495,207 @@ |
159 | hostname=`hostname` |
160 | if [ -x /usr/sbin/crm ]; then |
161 | if crm resource show $1 | grep -q $hostname; then |
162 | - echo "$hostname is cluster leader" |
163 | + juju-log "$hostname is cluster leader." |
164 | return 0 |
165 | fi |
166 | fi |
167 | - echo "$hostname is not cluster leader" |
168 | - return 1 |
169 | + juju-log "$hostname is not cluster leader." |
170 | + return 1 |
171 | +} |
172 | + |
173 | +########################################################################## |
174 | +# Description: Determines whether enough data has been provided in |
175 | +# configuration or relation data to configure HTTPS. |
176 | +# Parameters: None |
177 | +# Returns: 0 if HTTPS can be configured, 1 if not. |
178 | +########################################################################## |
179 | +https() { |
180 | + local r_id="" |
181 | + if [[ -n "$(config-get ssl_cert)" ]] && |
182 | + [[ -n "$(config-get ssl_key)" ]] ; then |
183 | + return 0 |
184 | + fi |
185 | + for r_id in $(relation-ids identity-service) ; do |
186 | + for unit in $(relation-list -r $r_id) ; do |
187 | + if [[ "$(relation-get -r $r_id https_keystone $unit)" == "True" ]] && |
188 | + [[ -n "$(relation-get -r $r_id ssl_cert $unit)" ]] && |
189 | + [[ -n "$(relation-get -r $r_id ssl_key $unit)" ]] && |
190 | + [[ -n "$(relation-get -r $r_id ca_cert $unit)" ]] ; then |
191 | + return 0 |
192 | + fi |
193 | + done |
194 | + done |
195 | + return 1 |
196 | +} |
197 | + |
198 | +########################################################################## |
199 | +# Description: For a given number of port mappings, configures apache2 |
200 | +# HTTPs local reverse proxying using certficates and keys provided in |
201 | +# either configuration data (preferred) or relation data. Assumes ports |
202 | +# are not in use (calling charm should ensure that). |
203 | +# Parameters: Variable number of proxy port mappings as |
204 | +# $internal:$external. |
205 | +# Returns: 0 if reverse proxy(s) have been configured, 0 if not. |
206 | +########################################################################## |
207 | +enable_https() { |
208 | + local port_maps="$@" |
209 | + local http_restart="" |
210 | + juju-log "Enabling HTTPS for port mappings: $port_maps." |
211 | + |
212 | + # allow overriding of keystone provided certs with those set manually |
213 | + # in config. |
214 | + local cert=$(config-get ssl_cert) |
215 | + local key=$(config-get ssl_key) |
216 | + local ca_cert="" |
217 | + if [[ -z "$cert" ]] || [[ -z "$key" ]] ; then |
218 | + juju-log "Inspecting identity-service relations for SSL certificate." |
219 | + local r_id="" |
220 | + cert="" |
221 | + key="" |
222 | + ca_cert="" |
223 | + for r_id in $(relation-ids identity-service) ; do |
224 | + for unit in $(relation-list -r $r_id) ; do |
225 | + [[ -z "$cert" ]] && cert="$(relation-get -r $r_id ssl_cert $unit)" |
226 | + [[ -z "$key" ]] && key="$(relation-get -r $r_id ssl_key $unit)" |
227 | + [[ -z "$ca_cert" ]] && ca_cert="$(relation-get -r $r_id ca_cert $unit)" |
228 | + done |
229 | + done |
230 | + [[ -n "$cert" ]] && cert=$(echo $cert | base64 -di) |
231 | + [[ -n "$key" ]] && key=$(echo $key | base64 -di) |
232 | + [[ -n "$ca_cert" ]] && ca_cert=$(echo $ca_cert | base64 -di) |
233 | + else |
234 | + juju-log "Using SSL certificate provided in service config." |
235 | + fi |
236 | + |
237 | + [[ -z "$cert" ]] || [[ -z "$key" ]] && |
238 | + juju-log "Expected but could not find SSL certificate data, not "\ |
239 | + "configuring HTTPS!" && return 1 |
240 | + |
241 | + apt-get -y install apache2 |
242 | + a2enmod ssl proxy proxy_http | grep -v "To activate the new configuration" && |
243 | + http_restart=1 |
244 | + |
245 | + mkdir -p /etc/apache2/ssl/$CHARM |
246 | + echo "$cert" >/etc/apache2/ssl/$CHARM/cert |
247 | + echo "$key" >/etc/apache2/ssl/$CHARM/key |
248 | + if [[ -n "$ca_cert" ]] ; then |
249 | + juju-log "Installing Keystone supplied CA cert." |
250 | + echo "$ca_cert" >/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt |
251 | + update-ca-certificates --fresh |
252 | + |
253 | + # XXX TODO: Find a better way of exporting this? |
254 | + if [[ "$CHARM" == "nova-cloud-controller" ]] ; then |
255 | + [[ -e /var/www/keystone_juju_ca_cert.crt ]] && |
256 | + rm -rf /var/www/keystone_juju_ca_cert.crt |
257 | + ln -s /usr/local/share/ca-certificates/keystone_juju_ca_cert.crt \ |
258 | + /var/www/keystone_juju_ca_cert.crt |
259 | + fi |
260 | + |
261 | + fi |
262 | + for port_map in $port_maps ; do |
263 | + local ext_port=$(echo $port_map | cut -d: -f1) |
264 | + local int_port=$(echo $port_map | cut -d: -f2) |
265 | + juju-log "Creating apache2 reverse proxy vhost for $port_map." |
266 | + cat >/etc/apache2/sites-available/${CHARM}_${ext_port} <<END |
267 | +Listen $ext_port |
268 | +NameVirtualHost *:$ext_port |
269 | +<VirtualHost *:$ext_port> |
270 | + ServerName $(unit-get private-address) |
271 | + SSLEngine on |
272 | + SSLCertificateFile /etc/apache2/ssl/$CHARM/cert |
273 | + SSLCertificateKeyFile /etc/apache2/ssl/$CHARM/key |
274 | + ProxyPass / http://localhost:$int_port/ |
275 | + ProxyPassReverse / http://localhost:$int_port/ |
276 | + ProxyPreserveHost on |
277 | +</VirtualHost> |
278 | +<Proxy *> |
279 | + Order deny,allow |
280 | + Allow from all |
281 | +</Proxy> |
282 | +<Location /> |
283 | + Order allow,deny |
284 | + Allow from all |
285 | +</Location> |
286 | +END |
287 | + a2ensite ${CHARM}_${ext_port} | grep -v "To activate the new configuration" && |
288 | + http_restart=1 |
289 | + done |
290 | + if [[ -n "$http_restart" ]] ; then |
291 | + service apache2 restart |
292 | + fi |
293 | +} |
294 | + |
295 | +########################################################################## |
296 | +# Description: Ensure HTTPS reverse proxying is disabled for given port |
297 | +# mappings. |
298 | +# Parameters: Variable number of proxy port mappings as |
299 | +# $internal:$external. |
300 | +# Returns: 0 if reverse proxy is not active for all portmaps, 1 on error. |
301 | +########################################################################## |
302 | +disable_https() { |
303 | + local port_maps="$@" |
304 | + local http_restart="" |
305 | + juju-log "Ensuring HTTPS disabled for $port_maps." |
306 | + ( [[ ! -d /etc/apache2 ]] || [[ ! -d /etc/apache2/ssl/$CHARM ]] ) && return 0 |
307 | + for port_map in $port_maps ; do |
308 | + local ext_port=$(echo $port_map | cut -d: -f1) |
309 | + local int_port=$(echo $port_map | cut -d: -f2) |
310 | + if [[ -e /etc/apache2/sites-available/${CHARM}_${ext_port} ]] ; then |
311 | + juju-log "Disabling HTTPS reverse proxy for $CHARM $port_map." |
312 | + a2dissite ${CHARM}_${ext_port} | grep -v "To activate the new configuration" && |
313 | + http_restart=1 |
314 | + fi |
315 | + done |
316 | + if [[ -n "$http_restart" ]] ; then |
317 | + service apache2 restart |
318 | + fi |
319 | +} |
320 | + |
321 | + |
322 | +########################################################################## |
323 | +# Description: Ensures HTTPS is either enabled or disabled for given port |
324 | +# mapping. |
325 | +# Parameters: Variable number of proxy port mappings as |
326 | +# $internal:$external. |
327 | +# Returns: 0 if HTTPS reverse proxy is in place, 1 if it is not. |
328 | +########################################################################## |
329 | +setup_https() { |
330 | + # configure https via apache reverse proxying either |
331 | + # using certs provided by config or keystone. |
332 | + [[ -z "$CHARM" ]] && |
333 | + error_out "setup_https(): CHARM not set." |
334 | + if ! https ; then |
335 | + disable_https $@ |
336 | + else |
337 | + enable_https $@ |
338 | + fi |
339 | +} |
340 | + |
341 | +########################################################################## |
342 | +# Description: Determine correct API server listening port based on |
343 | +# existence of HTTPS reverse proxy and/or haproxy. |
344 | +# Paremeters: The standard public port for given service. |
345 | +# Returns: The correct listening port for API service. |
346 | +########################################################################## |
347 | +determine_api_port() { |
348 | + local public_port="$1" |
349 | + local i=0 |
350 | + ( [[ -n "$(peer_units)" ]] || is_clustered >/dev/null 2>&1 ) && i=$[$i + 1] |
351 | + https >/dev/null 2>&1 && i=$[$i + 1] |
352 | + echo $[$public_port - $[$i * 10]] |
353 | +} |
354 | + |
355 | +########################################################################## |
356 | +# Description: Determine correct proxy listening port based on public IP + |
357 | +# existence of HTTPS reverse proxy. |
358 | +# Paremeters: The standard public port for given service. |
359 | +# Returns: The correct listening port for haproxy service public address. |
360 | +########################################################################## |
361 | +determine_haproxy_port() { |
362 | + local public_port="$1" |
363 | + local i=0 |
364 | + https >/dev/null 2>&1 && i=$[$i + 1] |
365 | + echo $[$public_port - $[$i * 10]] |
366 | } |
367 | |
368 | ########################################################################## |
369 | |
370 | === modified file 'hooks/nova-cloud-controller-common' |
371 | --- hooks/nova-cloud-controller-common 2013-01-18 12:22:44 +0000 |
372 | +++ hooks/nova-cloud-controller-common 2013-03-09 04:30:28 +0000 |
373 | @@ -97,6 +97,7 @@ |
374 | if [ "$(config-get conf-ext-net)" != "no" ] && |
375 | [ "$QUANTUM_PLUGIN" == "ovs" ] && |
376 | [ -f /etc/quantum/novarc ] && |
377 | + [ -n "$(relation-ids amqp)" ] && |
378 | [ -n "$(relation-ids shared-db)" ]; then |
379 | juju-log "Configuring external networking for quantum" |
380 | # Use helper to create external network gateway |
381 | @@ -219,3 +220,73 @@ |
382 | known_hosts="$(base64 /etc/nova/compute_ssh/$sunit/known_hosts)" \ |
383 | authorized_keys="$(base64 /etc/nova/compute_ssh/$sunit/authorized_keys)" |
384 | } |
385 | + |
386 | +configure_https() { |
387 | + # setup https termination for all api services, depending on what is running |
388 | + # and topology of current deployment. |
389 | + local clustered="" |
390 | + ( [[ -n "$(peer_units)" ]] || is_clustered ) && clustered="1" |
391 | + local services="" |
392 | + local ssl_port_maps="" |
393 | + local haproxy_port_maps="" |
394 | + local next_server="" |
395 | + local api_port="" |
396 | + |
397 | + # upstartService:defaultPort:configOption |
398 | + local svcs="nova-api-ec2:8773:ec2_listen_port |
399 | + nova-api-os-compute:8774:osapi_compute_listen_port |
400 | + nova-objectstore:3333:s3_listen_port" |
401 | + [[ "$NET_MANAGER" == "Quantum" ]] && |
402 | + svcs="$svcs quantum-server:9696:bind_port" |
403 | + |
404 | + for s in $svcs ; do |
405 | + local service=$(echo $s | cut -d: -f1) |
406 | + local port=$(echo $s | cut -d: -f2) |
407 | + local opt=$(echo $s | cut -d: -f3) |
408 | + if [[ -n "$clustered" ]] ; then |
409 | + next_server="$(determine_haproxy_port $port)" |
410 | + api_port="$(determine_api_port $port)" |
411 | + haproxy_port_maps="$haproxy_port_maps $service:$next_server:$api_port" |
412 | + else |
413 | + api_port="$(determine_api_port $port)" |
414 | + next_server="$api_port" |
415 | + fi |
416 | + if [[ "$service" == "quantum-server" ]] ; then |
417 | + set_or_update "$opt" "$api_port" "$QUANTUM_CONF" |
418 | + else |
419 | + set_or_update "$opt" "$api_port" |
420 | + fi |
421 | + ssl_port_maps="$ssl_port_maps $port:$next_server" |
422 | + done |
423 | + |
424 | + # make sure all backend api servers are bound to new backend port |
425 | + # before setting up any frontends. |
426 | + for s in $svcs ; do |
427 | + local service=$(echo $s | cut -d: -f1) |
428 | + service_ctl $service restart |
429 | + done |
430 | + |
431 | + [[ -n "$haproxy_port_maps" ]] && configure_haproxy $haproxy_port_maps |
432 | + setup_https $ssl_port_maps |
433 | + |
434 | + # another restart to ensure api servers are now bound to frontend ports |
435 | + # that may have just been disabled. |
436 | + for s in $svcs ; do |
437 | + local service=$(echo $s | cut -d: -f1) |
438 | + service_ctl $service restart |
439 | + done |
440 | + |
441 | + local r_id="" |
442 | + # (re)configure ks endpoint accordingly |
443 | + for r_id in $(relation-ids identity-service) ; do |
444 | + keystone_joined "$r_id" |
445 | + done |
446 | + # pass on possibly updated quantum URL + ca_cert to compute nodes. |
447 | + for r_id in $(relation-ids cloud-compute) ; do |
448 | + compute_joined "$r_id" |
449 | + done |
450 | + # update the quantum relation, as well. |
451 | + for r_id in $(relation-ids quantum-network-service) ; do |
452 | + quantum_joined "$r_id" |
453 | + done |
454 | +} |
455 | |
456 | === modified file 'hooks/nova-cloud-controller-relations' |
457 | --- hooks/nova-cloud-controller-relations 2013-02-21 23:12:04 +0000 |
458 | +++ hooks/nova-cloud-controller-relations 2013-03-09 04:30:28 +0000 |
459 | @@ -44,6 +44,7 @@ |
460 | cp files/create_tenant_net.py /usr/bin/quantum-tenant-net |
461 | |
462 | service_ctl all stop |
463 | + configure_https |
464 | } |
465 | |
466 | function upgrade_charm { |
467 | @@ -70,13 +71,14 @@ |
468 | set_config_flags |
469 | |
470 | if [ "$NET_MANAGER" == "Quantum" ] && \ |
471 | - is_clustered && is_leader 'res_nova_vip' || \ |
472 | + eligible_leader 'res_nova_vip' || \ |
473 | ! is_clustered; then |
474 | configure_quantum_networking |
475 | fi |
476 | |
477 | determine_services |
478 | service_ctl all restart |
479 | + configure_https |
480 | } |
481 | |
482 | function amqp_joined { |
483 | @@ -131,6 +133,12 @@ |
484 | fi |
485 | |
486 | determine_services && service_ctl all restart |
487 | + |
488 | + if [ "$NET_MANAGER" == "Quantum" ] && \ |
489 | + eligible_leader 'res_nova_vip' || \ |
490 | + ! is_clustered; then |
491 | + configure_quantum_networking |
492 | + fi |
493 | } |
494 | |
495 | function db_joined { |
496 | @@ -170,11 +178,17 @@ |
497 | fi |
498 | determine_services |
499 | service_ctl all stop |
500 | - /usr/bin/nova-manage db sync |
501 | + |
502 | + eligible_leader 'res_nova_vip' && /usr/bin/nova-manage db sync |
503 | + |
504 | service_ctl all start |
505 | - if [ "$NET_MANAGER" == "Quantum" ]; then |
506 | + |
507 | + if [ "$NET_MANAGER" == "Quantum" ] && \ |
508 | + eligible_leader 'res_nova_vip' || \ |
509 | + ! is_clustered; then |
510 | configure_quantum_networking |
511 | fi |
512 | + |
513 | trigger_remote_service_restarts |
514 | } |
515 | |
516 | @@ -191,28 +205,37 @@ |
517 | # we need to get two entries into keystone's catalog, nova + ec2 |
518 | # group, them by prepending $service_ to each setting. the keystone |
519 | # charm will assemble settings into corresponding catalog entries |
520 | - if is_clustered && is_leader 'res_nova_vip'; then |
521 | - address=$(config-get vip) |
522 | - nova_port=18774 |
523 | - ec2_port=18773 |
524 | - s3_port=13333 |
525 | - quantum_port=19696 |
526 | - vol_port=18776 |
527 | - elif ! is_clustered; then |
528 | - address=$(unit-get private-address) |
529 | - nova_port=8774 |
530 | - ec2_port=8773 |
531 | - s3_port=3333 |
532 | - quantum_port=9696 |
533 | - vol_port=8776 |
534 | + eligible_leader 'res_nova_vip' || return 0 |
535 | + |
536 | + is_clustered && local host=$(config-get vip) || |
537 | + local host=$(unit-get private-address) |
538 | + |
539 | + if [[ "$arg0" == "identity-service-relation-joined" ]] ; then |
540 | + # determine https status based only on config at this point, |
541 | + # insepcting KS relation is not reliable. if KS has mulitple |
542 | + # units, multiple relation-joineds are fired, resulting in the |
543 | + # endpoint being configured in catalog as https before https |
544 | + # is actually setup on this end. ends with failure to configure |
545 | + # quantum network, if its enabled. |
546 | + # if specified in config, https will have already been setup in |
547 | + # install or config-changed. |
548 | + if [[ -n "$(config-get ssl_cert)" ]] && |
549 | + [[ -n "$(config-get ssl_key)" ]] ; then |
550 | + local scheme="https" |
551 | + else |
552 | + local scheme="http" |
553 | + fi |
554 | else |
555 | - # Not the leader and clustered - no action required |
556 | - return 0 |
557 | + # this function is called from other hook contexts, use normal method |
558 | + # for determining https |
559 | + https && scheme="https" || scheme="http" |
560 | fi |
561 | - nova_url="http://$address:$nova_port/v1.1/\$(tenant_id)s" |
562 | - ec2_url="http://$address:$ec2_port/services/Cloud" |
563 | - s3_url="http://$address:$s3_port" |
564 | - region="$(config-get region)" |
565 | + |
566 | + local nova_url="$scheme://$host:8774/v1.1/\$(tenant_id)s" |
567 | + local ec2_url="$scheme://$host:8773/services/Cloud" |
568 | + local s3_url="$scheme://$host:3333" |
569 | + local region="$(config-get region)" |
570 | + local quantum_url="$scheme://$host:9696" |
571 | |
572 | # these are the default endpoints |
573 | relation-set nova_service="nova" \ |
574 | @@ -232,7 +255,6 @@ |
575 | s3_internal_url="$s3_url" |
576 | |
577 | if [ "$(config-get network-manager)" == "Quantum" ]; then |
578 | - quantum_url="http://$address:$quantum_port" |
579 | relation-set quantum_service="quantum" \ |
580 | quantum_region="$region" \ |
581 | quantum_public_url="$quantum_url" \ |
582 | @@ -242,7 +264,7 @@ |
583 | |
584 | # tack on an endpoint for nova-volume a relation exists. |
585 | if [[ -n "$(relation-ids nova-volume-service)" ]] ; then |
586 | - nova_vol_url="http://$address:$vol_port/v1/\$(tenant_id)s" |
587 | + nova_vol_url="$scheme://$host:$vol_port/v1/\$(tenant_id)s" |
588 | relation-set nova-volume_service="nova-volume" \ |
589 | nova-volume_region="$region" \ |
590 | nova-volume_public_url="$nova_vol_url" \ |
591 | @@ -281,6 +303,13 @@ |
592 | sed -i '/--use_deprecated_auth/d' $NOVA_CONF |
593 | fi |
594 | |
595 | + local clustered="" |
596 | + is_clustered && clustered="1" |
597 | + |
598 | + [[ -n "$clustered" ]] && local host=$(config-get vip) || |
599 | + local host=$(unit-get private-address) |
600 | + https && local scheme="https" || local scheme="http" |
601 | + |
602 | # update keystone authtoken settings accordingly |
603 | set_or_update "service_host" "$service_host" "$API_CONF" |
604 | set_or_update "service_port" "$service_port" "$API_CONF" |
605 | @@ -296,7 +325,7 @@ |
606 | if [ "$NET_MANAGER" == "Quantum" ]; then |
607 | # Configure Nova for quantum |
608 | keystone_url="http://${auth_host}:${auth_port}/v2.0" |
609 | - set_or_update "quantum_url" "http://$(unit-get private-address):9696" |
610 | + set_or_update "quantum_url" "$scheme://$host:9696" |
611 | set_or_update "quantum_admin_tenant_name" "${service_tenant}" |
612 | set_or_update "quantum_admin_username" "${service_username}" |
613 | set_or_update "quantum_admin_password" "${service_password}" |
614 | @@ -320,11 +349,15 @@ |
615 | determine_services && service_ctl all restart |
616 | |
617 | if [ "$NET_MANAGER" == "Quantum" ]; then |
618 | - configure_quantum_networking |
619 | + # if first time here, config quantum before setting up |
620 | + # https. |
621 | + if [[ -z "$clustered" ]] && eligible_leader ; then |
622 | + configure_quantum_networking |
623 | + fi |
624 | # ripple out changes to identity to connected services |
625 | # which use cloud-controller as source of information for |
626 | # keystone |
627 | - r_ids="$(relation-ids cloud-compute) $(relation-ids quantum-network-service)" |
628 | + local r_ids="$(relation-ids cloud-compute) $(relation-ids quantum-network-service)" |
629 | for id in $r_ids ; do |
630 | relation-set -r $id \ |
631 | keystone_host=$auth_host \ |
632 | @@ -339,6 +372,15 @@ |
633 | |
634 | done |
635 | fi |
636 | + configure_https |
637 | + |
638 | + # if this changed event happens as a result of clustered VIP |
639 | + # reconfigure, configure_https needs to update VIP certificate |
640 | + # before quantumclient is used. |
641 | + if [[ "$NET_MANAGER" == "Quantum" ]] && |
642 | + [[ -n "$clustered" ]] && eligible_leader 'res_nova_vip' ; then |
643 | + configure_quantum_networking |
644 | + fi |
645 | } |
646 | |
647 | volume_joined() { |
648 | @@ -390,19 +432,19 @@ |
649 | } |
650 | |
651 | compute_joined() { |
652 | - if is_clustered && ! is_leader 'res_nova_vip'; then |
653 | - # Clustered and not current leader - do nothing |
654 | - return 0 |
655 | - fi |
656 | - |
657 | - relation-set network_manager=$(config-get network-manager) |
658 | - relation-set ec2_host=$(unit-get private-address) |
659 | + local r_id="$1" |
660 | + [[ -n "$r_id" ]] && r_id="-r $r_id" |
661 | + eligible_leader 'res_nova_vip' || return 0 |
662 | + relation-set $r_id network_manager=$(config-get network-manager) |
663 | + # XXX Should point to VIP if clustered, or this may not even be needed. |
664 | + relation-set $r_id ec2_host=$(unit-get private-address) |
665 | |
666 | local sect="filter:authtoken" |
667 | keystone_host=$(local_config_get $API_CONF auth_host $sect) |
668 | + |
669 | if [ "$NET_MANAGER" == "Quantum" ]; then |
670 | - if [ -n "$keystone_host" ]; then |
671 | - relation-set \ |
672 | + if [[ -n "$keystone_host" ]]; then |
673 | + relation-set $r_id \ |
674 | keystone_host=$keystone_host \ |
675 | auth_port=$(local_config_get $API_CONF auth_port $sect) \ |
676 | service_port=$(local_config_get $API_CONF service_port $sect) \ |
677 | @@ -410,19 +452,24 @@ |
678 | service_password=$(local_config_get $API_CONF admin_password $sect) \ |
679 | service_tenant=$(local_config_get $API_CONF admin_tenant_name $sect) \ |
680 | auth_uri=$(local_config_get $API_CONF auth_uri $sect) |
681 | - fi |
682 | - |
683 | - if is_clustered; then |
684 | - quantum_host=$(config-get vip) |
685 | - quantum_port=19696 |
686 | - else |
687 | - quantum_host=$(unit-get private-address) |
688 | - quantum_port=9696 |
689 | - fi |
690 | - |
691 | - relation-set quantum_host=$quantum_host \ |
692 | - quantum_port=$quantum_port \ |
693 | - quantum_plugin=$(config-get quantum-plugin) |
694 | + |
695 | + fi |
696 | + is_clustered && local host=$(config-get vip) || |
697 | + local host=$(unit-get private-address) |
698 | + https && local scheme="https" || local scheme="http" |
699 | + local quantum_url="$scheme://$host:9696" |
700 | + |
701 | + relation-set $r_id quantum_url=$quantum_url \ |
702 | + quantum_plugin=$(config-get quantum-plugin) \ |
703 | + region=$(config-get region) |
704 | + |
705 | + fi |
706 | + |
707 | + # must pass on the keystone CA certficiate, if it exists. |
708 | + cert="/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt" |
709 | + if [[ -n "$keystone_host" ]] && [[ -e $cert ]] ; then |
710 | + cert=$(cat $cert | base64) |
711 | + relation-set $r_id ca_cert="$cert" |
712 | fi |
713 | |
714 | # volume driver is dependent on os version, or presence |
715 | @@ -434,11 +481,10 @@ |
716 | vol_drv="nova-volume" |
717 | ;; |
718 | "folsom") |
719 | - local r_ids=$(relation-ids cinder-volume-service) |
720 | - [[ -z "$r_ids" ]] && vol_drv="nova-volume" |
721 | + [[ -z "$(relation-ids cinder-volume-service)" ]] && vol_drv="nova-volume" |
722 | ;; |
723 | esac |
724 | - relation-set volume_service="$vol_drv" |
725 | + relation-set $r_id volume_service="$vol_drv" |
726 | } |
727 | |
728 | compute_changed() { |
729 | @@ -458,15 +504,14 @@ |
730 | |
731 | function quantum_joined() { |
732 | # Tell quantum service about keystone |
733 | - if is_clustered && ! is_leader 'res_nova_vip'; then |
734 | - # Clustered and not current leader - do nothing |
735 | - return 0 |
736 | - fi |
737 | + eligible_leader || return 0 |
738 | + local r_id="$1" |
739 | + [[ -n "$r_id" ]] && r_id="-r $r_id" |
740 | |
741 | local sect="filter:authtoken" |
742 | keystone_host=$(local_config_get $API_CONF auth_host $sect) |
743 | if [ -n "$keystone_host" ]; then |
744 | - relation-set \ |
745 | + relation-set $r_id \ |
746 | keystone_host=$keystone_host \ |
747 | auth_port=$(local_config_get $API_CONF auth_port $sect) \ |
748 | service_port=$(local_config_get $API_CONF service_port $sect) \ |
749 | @@ -476,24 +521,50 @@ |
750 | auth_uri=$(local_config_get $API_CONF auth_uri $sect) |
751 | fi |
752 | |
753 | - if is_clustered; then |
754 | - quantum_host=$(config-get vip) |
755 | - quantum_port=19696 |
756 | - else |
757 | - quantum_host=$(unit-get private-address) |
758 | - quantum_port=9696 |
759 | + # must pass on the keystone CA certficiate, if it exists. |
760 | + cert="/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt" |
761 | + if [[ -n "$keystone_host" ]] && [[ -e $cert ]] ; then |
762 | + cert=$(cat $cert | base64) |
763 | + relation-set $r_id ca_cert="$cert" |
764 | fi |
765 | |
766 | - relation-set quantum_host=$quantum_host \ |
767 | - quantum_port=$quantum_port \ |
768 | - quantum_plugin=$(config-get quantum-plugin) \ |
769 | - region=$(config-get region) |
770 | + is_clustered && local host=$(config-get vip) || |
771 | + local host=$(unit-get private-address) |
772 | + https && local scheme="https" || local scheme="http" |
773 | + local quantum_url="$scheme://$host:9696" |
774 | + |
775 | + relation-set $r_id quantum_url=$quantum_url \ |
776 | + quantum_plugin=$(config-get quantum-plugin) \ |
777 | + region=$(config-get region) |
778 | + |
779 | } |
780 | |
781 | function cluster_changed() { |
782 | - configure_haproxy "quantum_api:9696" "nova_api:8774" \ |
783 | - "ec2_api:8773" "s3_api:3333" \ |
784 | - "volume_api:8776" |
785 | + [[ -z "$(peer_units)" ]] && |
786 | + juju-log "cluster_changed() with no peers." && exit 0 |
787 | + # upstartService:defaultPort:configOption |
788 | + local svcs="nova-api-ec2:8773:ec2_listen_port |
789 | + nova-api-os-compute:8774:osapi_compute_listen_port |
790 | + nova-objectstore:3333:s3_listen_port" |
791 | + [[ "$NET_MANAGER" == "Quantum" ]] && |
792 | + svcs="$svcs quantum-server:9696:bind_port" |
793 | + |
794 | + for s in $svcs ; do |
795 | + local service=$(echo $s | cut -d: -f1) |
796 | + local port=$(echo $s | cut -d: -f2) |
797 | + local opt=$(echo $s | cut -d: -f3) |
798 | + local next_server="$(determine_haproxy_port $port)" |
799 | + local api_port="$(determine_api_port $port)" |
800 | + local haproxy_port_maps="$haproxy_port_maps $service:$next_server:$api_port" |
801 | + if [[ "$service" == "quantum-server" ]] ; then |
802 | + set_or_update "$opt" "$api_port" "$QUANTUM_CONF" |
803 | + else |
804 | + set_or_update "$opt" "$api_port" |
805 | + fi |
806 | + |
807 | + service_ctl $service restart |
808 | + done |
809 | + configure_haproxy $haproxy_port_maps |
810 | } |
811 | |
812 | function ha_relation_joined() { |
813 | @@ -518,13 +589,13 @@ |
814 | init_services="{ |
815 | 'res_nova_haproxy':'haproxy' |
816 | }" |
817 | - groups="{ |
818 | -'grp_nova_haproxy':'res_nova_vip res_nova_haproxy' |
819 | + clones="{ |
820 | +'cl_nova_haproxy':'res_nova_haproxy' |
821 | }" |
822 | relation-set corosync_bindiface=$corosync_bindiface \ |
823 | corosync_mcastport=$corosync_mcastport \ |
824 | resources="$resources" resource_params="$resource_params" \ |
825 | - init_services="$init_services" groups="$groups" |
826 | + init_services="$init_services" clones="$clones" |
827 | else |
828 | juju-log "Insufficient configuration data to configure hacluster" |
829 | exit 1 |
830 | @@ -534,45 +605,57 @@ |
831 | function ha_relation_changed() { |
832 | local clustered=`relation-get clustered` |
833 | if [ -n "$clustered" ] && is_leader 'res_nova_vip'; then |
834 | + https && local scheme="https" || local scheme="http" |
835 | for r_id in `relation-ids identity-service`; do |
836 | - address=$(config-get vip) |
837 | - nova_url="http://$address:18774/v1.1/\$(tenant_id)s" |
838 | - ec2_url="http://$address:18773/services/Cloud" |
839 | - s3_url="http://$address:13333" |
840 | + local address=$(config-get vip) |
841 | + local region=$(config-get region) |
842 | + local nova_url="$scheme://$address:8774/v1.1/\$(tenant_id)s" |
843 | + local ec2_url="$scheme://$address:8773/services/Cloud" |
844 | + local s3_url="$scheme://$address:3333" |
845 | + local quantum_url="$scheme://$address:9696" |
846 | + local nova_vol_url="$scheme://$address:8776/v1/\$(tenant_id)s" |
847 | + |
848 | relation-set -r $r_id \ |
849 | + nova_service="nova" \ |
850 | + nova_region="$region" \ |
851 | nova_public_url="$nova_url" \ |
852 | nova_admin_url="$nova_url" \ |
853 | nova_internal_url="$nova_url" \ |
854 | + ec2_service="ec2" \ |
855 | + ec2_region="$region" \ |
856 | ec2_public_url="$ec2_url" \ |
857 | ec2_admin_url="$ec2_url" \ |
858 | ec2_internal_url="$ec2_url" \ |
859 | + s3_service="s3" \ |
860 | + s3_region="$region" \ |
861 | s3_public_url="$s3_url" \ |
862 | s3_admin_url="$s3_url" \ |
863 | s3_internal_url="$s3_url" |
864 | |
865 | if [ "$(config-get network-manager)" == "Quantum" ]; then |
866 | - quantum_url="http://$address:19696" |
867 | relation-set -r $r_id \ |
868 | + quantum_service="quantum" \ |
869 | + quantum_region="$region" \ |
870 | quantum_public_url="$quantum_url" \ |
871 | quantum_admin_url="$quantum_url" \ |
872 | quantum_internal_url="$quantum_url" |
873 | fi |
874 | |
875 | if [[ -n "$(relation-ids nova-volume-service)" ]] ; then |
876 | - nova_vol_url="http://$address:18776/v1/\$(tenant_id)s" |
877 | relation-set -r $r_id \ |
878 | + nova-volume_service="nova-volume" \ |
879 | + nova-volume_region="$region" \ |
880 | nova-volume_public_url="$nova_vol_url" \ |
881 | nova-volume_admin_url="$nova_vol_url" \ |
882 | nova-volume_internal_url="$nova_vol_url" |
883 | fi |
884 | done |
885 | if [ "$(config-get network-manager)" == "Quantum" ]; then |
886 | - # Let gateway nodes use the new HA address for the |
887 | + # Let gateway nodes use the new HA address for the |
888 | # quantum API server |
889 | for r_id in `relation-ids quantum-network-service`; do |
890 | relation-set -r $r_id \ |
891 | - quantum_host=$address |
892 | - quantum_port=19696 |
893 | + quantum_url="$quantum_url" region="$region" |
894 | done |
895 | fi |
896 | fi |
897 | |
898 | === modified file 'revision' |
899 | --- revision 2013-03-05 01:41:05 +0000 |
900 | +++ revision 2013-03-09 04:30:28 +0000 |
901 | @@ -1,1 +1,1 @@ |
902 | -214 |
903 | +236 |
A few bits and pieces:
nova-cloud- controller- relations: ha_joined
Clones should be set to clones, not groups
775 - init_services= "$init_ services" groups="$groups" "$init_ services" clones="$groups"
776 + init_services=
nova-cloud- controller- relations: quantum_ joined
I think the CA cert should be passed to quantum as well so it can talk back to the https server.