Merge lp:~xavpaice/glance-sync-slave-layer/queens into lp:glance-sync-slave-layer

Proposed by Xav Paice
Status: Rejected
Rejected by: Haw Loeung
Proposed branch: lp:~xavpaice/glance-sync-slave-layer/queens
Merge into: lp:glance-sync-slave-layer
Diff against target: 528 lines (+193/-111)
9 files modified
README.md (+55/-9)
config.yaml (+15/-15)
files/glance_sync_slave.py (+36/-58)
metadata.yaml (+8/-0)
reactive/glance-sync-slave.py (+55/-20)
templates/clouds.yaml.j2 (+13/-0)
templates/glance_sync_slave_cron.j2 (+1/-1)
templates/glancesync.novarc.j2 (+0/-5)
templates/novarc.j2 (+10/-3)
To merge this branch: bzr merge lp:~xavpaice/glance-sync-slave-layer/queens
Reviewer Review Type Date Requested Status
Alvaro Uria (community) Needs Fixing
Review via email: mp+351740@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Alvaro Uria (aluria) wrote :

Please find comment inline (re: master_creds param to align with os-credentials one in prometheus-openstack-exporter-charm).

Other than that, it looks good and I would like to test it in a slave T2 cloud with the current glance-master-sync service that is running in the master one.

review: Needs Fixing

Unmerged revisions

53. By Xav Paice

[xavpaice, r=] Updates for Queens and Keystone v3

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.md'
2--- README.md 2016-05-05 22:49:53 +0000
3+++ README.md 2018-07-30 06:16:31 +0000
4@@ -20,16 +20,62 @@
5
6 Glance credentials:
7
8-Both the i-age--ync-master and glance-sync-slave units can pull admin credentials from their local glance instances through a keystone-admin relation. This can be overridden with a custom novarc that can be set through a base64-encoded juju config setting. If a pre-existing custom novarc is removed the charm will fall back to using the relation and will display an error in juju status if neither are available.
9+Both the glance-sync-master and glance-sync-slave units can pull admin credentials from their local glance instances through a keystone-admin relation. This can be overridden with a custom novarc that can be set through a base64-encoded juju config setting. If a pre-existing custom novarc is removed the charm will fall back to using the relation and will display an error in juju status if neither are available.
10+
11+To allow the slave to download images from the master Glance API, you'll need
12+to create a user to allow that to happen:
13+
14+```
15+openstack user create --password glancesyncpassword --domain Default --project admin --project-domain Default glancesync
16+openstack role add --user glancesync --user-domain Default --project someproject Member
17+```
18+
19
20 # Usage
21
22-juju deploy glance-sync-slave
23-
24-# after the deploy, make sure to set an rsync source:
25-# You can find the configured data directory with juju get glance-sync-master in the data_dir config option. The default is /srv/glance_sync_master/data
26-juju set glance-sync-slave sync_source=<IP_of_master_unit>:<path_to_data_directory>
27-
28-# Then enable the sync which enables a sync cronjob on the slave unit (in /etc/cron.d)
29-juju set sync_enabled=True
30+
31+Deploy the master charm to the region which will be used to supply the images
32+to the other regions.
33+```
34+juju deploy cs:~canonical-bootstack/glance-sync-master
35+juju add-relation glance-sync-master keystone
36+```
37+
38+Deploy the slave charm to the regions where you wish to collect images:
39+```
40+juju deploy cs:~canonical-bootstack/glance-sync-slave
41+juju add-relation glance-sync-slave keystone
42+```
43+
44+After the deploy, make sure to create an authorized_keys file for any slaves you want to authorize to sync.
45+
46+Example authorized_keys file:
47+```
48+command="rsync --server --sender -az . /srv/glance_sync_master/data/",no-X11-forwarding,no-port-forwarding,no-pty,no-agent-forwarding <ssh_public_key>
49+command="rsync --server --sender -az . /srv/glance_sync_master/data/",no-X11-forwarding,no-port-forwarding,no-pty,no-agent-forwarding <another_ssh_public_key>
50+```
51+
52+Configure the key on the glance-sync-master charm:
53+
54+```
55+juju config glance-sync-master authorized_keys=$(base64 -w 0 <your_authorized_keys_file>)
56+```
57+
58+Then enable the sync which enables a sync cronjob on the master unit (in /etc/cron.d)
59+
60+```
61+juju config glance-sync-master sync_enabled=True
62+```
63+
64+# Slave charm configuration
65+
66+Collect the IP address of the glance-sync-master unit, and the credentials
67+created above.
68+
69+Set these in the glance-sync-slave charm:
70+
71+```
72+juju config glance-sync-slave master_creds='username=glancesync, password=glancesyncpassword, project=admin, region=RegionOne, auth_url=http://10.0.8.178:5000/v3, domain=Default'
73+juju config glance-sync-slave sync_source=ubuntu@172.16.109.180:/srv/glance_sync_master/data
74+```
75
76
77=== modified file 'config.yaml'
78--- config.yaml 2017-02-22 18:04:08 +0000
79+++ config.yaml 2018-07-30 06:16:31 +0000
80@@ -24,21 +24,15 @@
81 default: /srv/glance_sync_slave/logs
82 description: directory to store sync logfiles
83 type: string
84- master_password:
85- default: ''
86- description: auth password against glance master service
87- type: string
88- master_auth_url:
89- default: ''
90- description: master keystone endpoint
91- type: string
92- master_glance_url:
93- default: ''
94- description: master glance service endpoint
95- type: string
96- master_region:
97- default: 'RegionOne'
98- description: name of the master region
99+ master_creds:
100+ default: ''
101+ description: |
102+ Comma separated OpenStack credentials to be used to download images
103+ from the master region.
104+ It is strongly recommended this be a user with a dedicated role,
105+ and not a full admin. Takes the format of
106+ username=foo, password=bar, project=baz, region=Region1,
107+ auth_url=https://127.0.0.1:35357/v3, domain=Default
108 type: string
109 nagios_context:
110 default: "juju"
111@@ -64,4 +58,10 @@
112 description: |
113 rsync URL of master to sync images from
114 for example: ubuntu@172.16.109.180:/srv/glance_sync_master/data
115+ trusted_ssl_ca:
116+ type: string
117+ default: ''
118+ description: |
119+ base64 encoded SSL ca cert to use for OpenStack API client connections.
120+ This relies on the same CA being used for both the master and slave region.
121
122
123=== modified file 'files/glance_sync_slave.py'
124--- files/glance_sync_slave.py 2017-08-03 15:24:15 +0000
125+++ files/glance_sync_slave.py 2018-07-30 06:16:31 +0000
126@@ -1,3 +1,4 @@
127+#!/usr/bin/env python
128 # pulls glance images from a master openstack installation via rsync and
129 # imports them into local glance
130
131@@ -11,12 +12,7 @@
132 import atexit
133 # import re
134 import shlex
135-from glanceclient import Client
136-from glanceclient.exc import (
137- HTTPConflict,
138- HTTPForbidden,
139-)
140-from keystoneclient.v2_0 import client
141+import os_client_config
142 import subprocess
143
144
145@@ -81,7 +77,7 @@
146 self.extra_properties))
147 self.set_filelock()
148 self.glance_connect_slave()
149- # self.glance_connect_master()
150+ self.glance_connect_master()
151
152 def download_metadata_from_master(self):
153 """rsync metadata files from source to data_dir"""
154@@ -195,14 +191,12 @@
155
156 self.log('INFO: creating image {0}'.format(clean_metadata['id']))
157 try:
158- # FIXME
159 self.glance_slave.images.create(**clean_metadata)
160 self.log('DEBUG: create image: {0}'.format(clean_metadata))
161- except HTTPConflict as e:
162+ except Exception as e: # TODO narrow this exception down
163 self.log('EXCEPTION: upload_to_slave :: {0}'.format(e))
164 try:
165 # update metadata
166- # FIXME
167 self.glance_slave.images.update(clean_metadata['id'],
168 remove_props=removed_props,
169 **clean_metadata)
170@@ -217,7 +211,6 @@
171 return False
172 try:
173 # upload
174- # FIXME
175 self.glance_slave.images.upload(clean_metadata['id'],
176 open(tmp_image))
177 os.remove(tmp_image)
178@@ -246,7 +239,6 @@
179 except Exception as e:
180 self.log('ERROR: download_from_master :: {0}'.format(e))
181 return False
182- self.glance_connect_master()
183 downloaded = False
184 retries = 3
185 for i in range(0, retries):
186@@ -277,7 +269,6 @@
187 '{3}'.format(i, retries, metadata_local['id'], e))
188 if os.path.exists(tmp_image):
189 os.remove(tmp_image)
190- self.glance_connect_master()
191
192 return downloaded
193
194@@ -293,10 +284,9 @@
195 for image_id in to_delete_images_ids:
196 self.log('INFO: removing image {0}'.format(image_id))
197 try:
198- # FIXME
199 self.glance_slave.images.delete(image_id)
200 self.log('DEBUG: image {0} removed'.format(image_id))
201- except (HTTPConflict, HTTPForbidden) as e:
202+ except Exception as e: # TODO narrow the exception down
203 self.log('ERROR: could not delete {0} :: '
204 '{1}'.format(image_id, e))
205
206@@ -356,8 +346,8 @@
207
208 # XXX(aluria): no image on slave service
209 # XXX(aluria): look for similar project on slave
210- for slave_project_id, slave_project_name in \
211- self.projects_slave.items():
212+ for slave_project_id, slave_project_name in (
213+ self.projects_slave.items()):
214 slave_to_master = slave_project_name.replace(self.REGION_SLAVE,
215 self.REGION_MASTER)
216 # XXX(aluria): pitfall, if on master service there are
217@@ -398,59 +388,45 @@
218
219 def glance_connect_slave(self):
220 try:
221- username = os.environ['OS_USERNAME']
222- password = os.environ['OS_PASSWORD']
223- tenant_name = os.environ['OS_TENANT_NAME']
224- auth_url = os.environ['OS_AUTH_URL']
225- self.REGION_MASTER = os.environ['OS_REGION_NAME_MASTER'].upper()
226+ self.keystone = os_client_config.session_client(
227+ 'identity',
228+ cloud='envvars'
229+ )
230+ self.glance_slave = os_client_config.make_client(
231+ 'image',
232+ cloud='envvars'
233+ )
234 except Exception as e:
235 self.log('EXCEPTION: {0}'.format(e))
236 self.log('ERROR: unable to load environment variables, please '
237 'source novarc')
238 self.release_lock()
239 sys.exit(2)
240- self.keystone = client.Client(username=username, password=password,
241- tenant_name=tenant_name,
242- auth_url=auth_url)
243-
244 if not self.projects_slave:
245- self.projects_slave = dict([(tenant.id, tenant.name) for tenant in
246- self.keystone.tenants.list() if
247- tenant.enabled])
248- token = self.keystone.auth_token
249- service = self.keystone.services.find(name='glance')
250- endpoint = self.keystone.endpoints.find(service_id=service.id)
251- glance_url = endpoint.internalurl
252- self.REGION_SLAVE = endpoint.region.upper()
253- self.glance_slave = Client('2', endpoint=glance_url, token=token)
254+ self.projects_slave = dict(
255+ [(tenant['id'], tenant['name']) for tenant in
256+ self.keystone.get('/projects').json()['projects'] if
257+ tenant['enabled']])
258+ self.REGION_SLAVE = os.environ['OS_REGION_NAME'].upper()
259 return self.glance_slave
260
261 def glance_connect_master(self):
262 try:
263- username = 'glancesync'
264- password = os.environ['OS_PASSWORD_MASTER']
265- tenant_name = os.environ['OS_TENANT_NAME']
266- auth_url = os.environ['OS_AUTH_URL_MASTER']
267- glance_url_master = os.environ['OS_GLANCE_URL_MASTER']
268+ self.glance_master = os_client_config.make_client(
269+ 'image',
270+ cloud='master',
271+ )
272+ self.keystone_master = os_client_config.make_client(
273+ 'identity',
274+ identity_api_version=3,
275+ cloud='master')
276+ self.REGION_MASTER = self.keystone_master.regions.find().id
277 except Exception as e:
278- self.log('EXCEPTION: glance_connect_master :: {0}'.format(e))
279- self.log('ERROR: unable to load environment variables, please '
280- 'source novarc')
281+ self.log('EXCEPTION: {0}'.format(e))
282+ self.log('ERROR: unable to load master cloud environment '
283+ 'variables, please source glancesync.novarc')
284 self.release_lock()
285 sys.exit(2)
286- self.keystone_master = client.Client(username=username,
287- password=password,
288- tenant_name=tenant_name,
289- auth_url=auth_url)
290- token = self.keystone_master.auth_token
291- # XXX: uses adminURL, which is OS-MGMT
292- # XXX: publicURL should be used
293- # service = self.keystone_master.services.find(name='glance')
294- # endpoint = self.keystone_master.endpoints.find(service_id=service.id)
295- # glance_url = endpoint.publicurl
296- # self.REGION_MASTER = endpoint.region.upper()
297- self.glance_master = Client('2', endpoint=glance_url_master,
298- token=token)
299 return self.glance_master
300
301 def timestamp_now(self):
302@@ -507,6 +483,7 @@
303 ['user_id',
304 'image_type',
305 'image_state',
306+
307 'image_location',
308 'base_image_ref',
309 'owner_id']
310@@ -537,8 +514,8 @@
311 self.projects_slave[metadata_slave['owner']]
312 # ie. admin, services, MASTER-CENTRAL
313 slave_to_master = \
314- slave_project_name.replace(self.REGION_SLAVE,
315- self.REGION_MASTER)
316+ slave_project_name.replace(self.REGION_SLAVE,
317+ self.REGION_MASTER)
318 # ie. admin, services, MASTER-CENTRAL
319 if master_project_name in (slave_project_name,
320 slave_to_master):
321@@ -606,6 +583,7 @@
322 self.log('ending glance image sync slave run')
323 self.release_lock()
324
325+
326 if __name__ == '__main__':
327 parser = argparse.ArgumentParser(description='Synchronize remote images '
328 'metadata to disk and import into glance')
329
330=== modified file 'metadata.yaml'
331--- metadata.yaml 2016-04-30 00:06:33 +0000
332+++ metadata.yaml 2018-07-30 06:16:31 +0000
333@@ -6,6 +6,9 @@
334 description: |
335 This charm will import glance images from a master openstack installations.
336 It will copy image files and metadata with rsync over ssh to local disk and import into glance from there.
337+series:
338+ - xenial
339+ - trusty
340 provides:
341 nrpe-external-master:
342 interface: nrpe-external-master
343@@ -13,3 +16,8 @@
344 requires:
345 keystone-admin:
346 interface: keystone-admin
347+extra-bindings:
348+ public:
349+ admin:
350+ internal:
351+
352
353=== modified file 'reactive/glance-sync-slave.py'
354--- reactive/glance-sync-slave.py 2017-02-22 18:04:08 +0000
355+++ reactive/glance-sync-slave.py 2018-07-30 06:16:31 +0000
356@@ -9,15 +9,15 @@
357 from charmhelpers.core import hookenv, templating
358 from charmhelpers import fetch
359 from charms.reactive import hook, when, set_state, remove_state
360+from charmhelpers.contrib.openstack.utils import config_flags_parser
361
362
363 @hook('install')
364 def install_glance_sync_slave():
365 hookenv.status_set('maintenance', 'Installing')
366 fetch.apt_update()
367- fetch.apt_install('python-glanceclient')
368- fetch.apt_install('python-nose')
369- fetch.apt_install('python-dateutil')
370+ packages = ['python-nose', 'python-dateutil', 'python-openstackclient']
371+ fetch.apt_install(packages)
372
373 # configure the variables that have a default value
374 configure_config_dir()
375@@ -152,26 +152,48 @@
376 def configure_custom_novarc():
377 configure_novarc()
378
379-@when('config.changed.master_{password,auth_url,glance_url,region}')
380+
381+@when('config.changed.master_creds')
382 def configure_master_novarc():
383- context = {}
384- for i in ('master_password', 'master_auth_url',
385- 'master_glance_url', 'master_region'):
386- value = hookenv.config(i)
387- if not value:
388- hookenv.status_set('blocked', 'master service credentials '
389- 'missing')
390- return
391- context[i] = value
392-
393- glancesync_novarc_file = os.path.join(config_dir, 'glancesync.novarc')
394-
395- templating.render(source='glancesync.novarc.j2',
396- target=glancesync_novarc_file,
397+ # get context from master_novarc in config
398+ # Puth the info in clouds.yaml for openstacksdk
399+ # TODO combine novarc with this, one file for both clouds
400+ creds = {}
401+ keystone_creds = config_flags_parser(hookenv.config('master_creds'))
402+ if not keystone_creds:
403+ hookenv.status_set('blocked', 'master service credentials '
404+ 'missing')
405+ return
406+ else:
407+ if '/v3' in keystone_creds['auth_url']:
408+ creds = {
409+ 'master_username': keystone_creds['username'],
410+ 'master_password': keystone_creds['password'],
411+ 'master_project': keystone_creds['project'],
412+ 'master_region': keystone_creds['region'],
413+ 'master_auth_url': keystone_creds['auth_url'],
414+ 'master_auth_version': '3',
415+ 'master_user_domain': keystone_creds['domain'],
416+ 'master_project_domain': keystone_creds['domain'],
417+ }
418+ else:
419+ creds = {
420+ 'master_username': keystone_creds['username'],
421+ 'master_password': keystone_creds['password'],
422+ 'master_project': keystone_creds['project'],
423+ 'master_region': keystone_creds['region'],
424+ 'master_auth_url': keystone_creds['auth_url'],
425+ }
426+
427+ config_dir = '/etc/openstack'
428+ clouds_yaml = os.path.join(config_dir, 'clouds.yaml')
429+ templating.render(source='clouds.yaml.j2',
430+ target=clouds_yaml,
431 owner='ubuntu',
432 perms=0o600,
433- context=relation)
434- hookenv.status_set('active', 'glancesync.novarc configured')
435+ context=creds)
436+ hookenv.status_set('active', 'clouds.yaml configured')
437+
438
439 @hook('{requires:keystone-admin}-relation-{joined,changed}')
440 def configure_relation_novarc(relation=None):
441@@ -250,6 +272,7 @@
442
443
444 @when('config.changed.sync_enabled')
445+@when('config.changed.cron_frequency')
446 def configure_cron():
447 hookenv.status_set('maintenance', 'enabling sync')
448 sync_enabled = hookenv.config('sync_enabled')
449@@ -279,6 +302,18 @@
450 hookenv.status_set('active', 'sync configured')
451
452
453+@when('config.changed.trusted_ssl_ca')
454+def fix_ssl():
455+ config = hookenv.config()
456+ cert_file = '/usr/local/share/ca-certificates/openstack-ca.crt'
457+ trusted_ssl_ca = config.get('trusted_ssl_ca').strip()
458+ hookenv.log("Writing ssl ca cert:{}".format(trusted_ssl_ca))
459+ cert_content = base64.b64decode(trusted_ssl_ca).decode()
460+ with open(cert_file, 'w') as f:
461+ print(cert_content, file=f)
462+ subprocess.call(["/usr/sbin/update-ca-certificates"])
463+
464+
465 @hook('nrpe-external-master-relation-changed')
466 def setup_nrpe_checks(nagios):
467 hookenv.status_set('maintenance', 'Configuring nrpe checks')
468
469=== added file 'templates/clouds.yaml.j2'
470--- templates/clouds.yaml.j2 1970-01-01 00:00:00 +0000
471+++ templates/clouds.yaml.j2 2018-07-30 06:16:31 +0000
472@@ -0,0 +1,13 @@
473+clouds:
474+ master:
475+ region_name: {{ master_region }}
476+ auth:
477+ username: {{ master_username }}
478+ password: {{ master_password }}
479+ project_name: {{ master_project }}
480+ auth_url: {{ master_auth_url }}
481+{%- if master_auth_version %}
482+ user_domain_name: {{ master_user_domain }}
483+ project_domain_name: {{ master_project_domain }}
484+{%- endif %}
485+
486
487=== modified file 'templates/glance_sync_slave_cron.j2'
488--- templates/glance_sync_slave_cron.j2 2017-02-22 18:04:08 +0000
489+++ templates/glance_sync_slave_cron.j2 2018-07-30 06:16:31 +0000
490@@ -6,7 +6,7 @@
491 MAILTO={{ admin_email }}
492 SHELL=/bin/bash
493
494-{{ cron_frequency }} ubuntu bash -c 'source {{ config_dir }}/glancesync.novarc && source {{ config_dir}}/novarc && python {{ script_dir }}/glance_sync_slave.py -d {{ data_dir }} -s {{ sync_source }} >> {{ log_dir }}/glance_sync_slave.log 2>&1'
495+{{ cron_frequency }} ubuntu bash -c 'source {{ config_dir}}/novarc && python {{ script_dir }}/glance_sync_slave.py -d {{ data_dir }} -s {{ sync_source }} >> {{ log_dir }}/glance_sync_slave.log 2>&1'
496
497 {% endif %}
498
499
500=== removed file 'templates/glancesync.novarc.j2'
501--- templates/glancesync.novarc.j2 2017-02-22 18:04:08 +0000
502+++ templates/glancesync.novarc.j2 1970-01-01 00:00:00 +0000
503@@ -1,5 +0,0 @@
504-export OS_PASSWORD_MASTER={{ master_password }}
505-export OS_AUTH_URL_MASTER={{ master_auth_url }}
506-export OS_GLANCE_URL_MASTER={{ master_glance_url }}
507-export OS_REGION_NAME_MASTER={{ master_region }}
508-
509
510=== modified file 'templates/novarc.j2'
511--- templates/novarc.j2 2017-02-17 17:39:38 +0000
512+++ templates/novarc.j2 2018-07-30 06:16:31 +0000
513@@ -1,6 +1,13 @@
514-#export OS_AUTH_URL=http://{{ service_hostname }}:{{ service_port }}/v2.0
515-export OS_AUTH_URL=https://{{ service_hostname }}:{{ service_port }}/v2.0
516+export OS_AUTH_URL={{ service_protocol }}://{{ service_hostname }}:{{ service_port }}/v{{ api_version }}
517+export OS_TENANT_NAME={{ service_tenant_name }}
518 export OS_USERNAME={{ service_username }}
519 export OS_PASSWORD={{ service_password }}
520-export OS_TENANT_NAME={{ service_tenant_name }}
521+export OS_PROJECT_NAME={{ service_project_name }}
522+export OS_REGION_NAME={{ service_region }}
523+{%- if api_version == '3' %}
524+export OS_IDENTITY_API_VERSION={{ api_version }}
525+export OS_AUTH_VERSION={{ api_version }}
526+export OS_USER_DOMAIN_NAME={{ service_user_domain_name }}
527+export OS_PROJECT_DOMAIN_NAME={{ service_user_domain_name }}
528+{%- endif %}
529

Subscribers

People subscribed via source and target branches