=== modified file 'README.md'
--- README.md	2016-05-05 22:49:53 +0000
+++ README.md	2018-07-30 06:16:31 +0000
@@ -20,16 +20,62 @@
 
 Glance credentials:
 
-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.
+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.
+
+To allow the slave to download images from the master Glance API, you'll need
+to create a user to allow that to happen:
+
+```
+openstack user create --password glancesyncpassword --domain Default --project admin --project-domain Default glancesync
+openstack role add --user glancesync --user-domain Default --project someproject Member
+```
+
 
 # Usage
 
-juju deploy glance-sync-slave
-
-# after the deploy, make sure to set an rsync source:
-# 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
-juju set glance-sync-slave sync_source=<IP_of_master_unit>:<path_to_data_directory>
-
-# Then enable the sync which enables a sync cronjob on the slave unit (in /etc/cron.d)
-juju set sync_enabled=True
+
+Deploy the master charm to the region which will be used to supply the images
+to the other regions.
+```
+juju deploy cs:~canonical-bootstack/glance-sync-master
+juju add-relation glance-sync-master keystone
+```
+
+Deploy the slave charm to the regions where you wish to collect images:
+```
+juju deploy cs:~canonical-bootstack/glance-sync-slave
+juju add-relation glance-sync-slave keystone
+```
+
+After the deploy, make sure to create an authorized_keys file for any slaves you want to authorize to sync.
+
+Example authorized_keys file:
+```
+command="rsync --server --sender -az . /srv/glance_sync_master/data/",no-X11-forwarding,no-port-forwarding,no-pty,no-agent-forwarding <ssh_public_key>
+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>
+```
+
+Configure the key on the glance-sync-master charm:
+
+```
+juju config glance-sync-master authorized_keys=$(base64 -w 0 <your_authorized_keys_file>)
+```
+
+Then enable the sync which enables a sync cronjob on the master unit (in /etc/cron.d)
+
+```
+juju config glance-sync-master sync_enabled=True
+```
+
+# Slave charm configuration
+
+Collect the IP address of the glance-sync-master unit, and the credentials
+created above.
+
+Set these in the glance-sync-slave charm:
+
+```
+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'
+juju config glance-sync-slave sync_source=ubuntu@172.16.109.180:/srv/glance_sync_master/data
+```
 

=== modified file 'config.yaml'
--- config.yaml	2017-02-22 18:04:08 +0000
+++ config.yaml	2018-07-30 06:16:31 +0000
@@ -24,21 +24,15 @@
         default: /srv/glance_sync_slave/logs
         description: directory to store sync logfiles
         type: string
-    master_password:
-        default: ''
-        description: auth password against glance master service
-        type: string
-    master_auth_url:
-        default: ''
-        description: master keystone endpoint
-        type: string
-    master_glance_url:
-        default: ''
-        description: master glance service endpoint
-        type: string
-    master_region:
-        default: 'RegionOne'
-        description: name of the master region
+    master_creds:
+        default: ''
+        description: |
+            Comma separated OpenStack credentials to be used to download images
+            from the master region.
+            It is strongly recommended this be a user with a dedicated role,
+            and not a full admin.  Takes the format of
+            username=foo, password=bar, project=baz, region=Region1,
+            auth_url=https://127.0.0.1:35357/v3, domain=Default
         type: string
     nagios_context:
         default: "juju"
@@ -64,4 +58,10 @@
         description: |
             rsync URL of master to sync images from
             for example: ubuntu@172.16.109.180:/srv/glance_sync_master/data
+    trusted_ssl_ca:
+        type: string
+        default: ''
+        description: |
+            base64 encoded SSL ca cert to use for OpenStack API client connections.
+            This relies on the same CA being used for both the master and slave region.
 

=== modified file 'files/glance_sync_slave.py'
--- files/glance_sync_slave.py	2017-08-03 15:24:15 +0000
+++ files/glance_sync_slave.py	2018-07-30 06:16:31 +0000
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 # pulls glance images from a master openstack installation via rsync and
 # imports them into local glance
 
@@ -11,12 +12,7 @@
 import atexit
 # import re
 import shlex
-from glanceclient import Client
-from glanceclient.exc import (
-    HTTPConflict,
-    HTTPForbidden,
-)
-from keystoneclient.v2_0 import client
+import os_client_config
 import subprocess
 
 
@@ -81,7 +77,7 @@
                 self.extra_properties))
         self.set_filelock()
         self.glance_connect_slave()
-        # self.glance_connect_master()
+        self.glance_connect_master()
 
     def download_metadata_from_master(self):
         """rsync metadata files from source to data_dir"""
@@ -195,14 +191,12 @@
 
         self.log('INFO: creating image {0}'.format(clean_metadata['id']))
         try:
-            # FIXME
             self.glance_slave.images.create(**clean_metadata)
             self.log('DEBUG: create image: {0}'.format(clean_metadata))
-        except HTTPConflict as e:
+        except Exception as e:  # TODO narrow this exception down
             self.log('EXCEPTION: upload_to_slave :: {0}'.format(e))
             try:
                 # update metadata
-                # FIXME
                 self.glance_slave.images.update(clean_metadata['id'],
                                                 remove_props=removed_props,
                                                 **clean_metadata)
@@ -217,7 +211,6 @@
                 return False
         try:
             # upload
-            # FIXME
             self.glance_slave.images.upload(clean_metadata['id'],
                                             open(tmp_image))
             os.remove(tmp_image)
@@ -246,7 +239,6 @@
                 except Exception as e:
                     self.log('ERROR: download_from_master :: {0}'.format(e))
                     return False
-        self.glance_connect_master()
         downloaded = False
         retries = 3
         for i in range(0, retries):
@@ -277,7 +269,6 @@
                          '{3}'.format(i, retries, metadata_local['id'], e))
                 if os.path.exists(tmp_image):
                     os.remove(tmp_image)
-                self.glance_connect_master()
 
         return downloaded
 
@@ -293,10 +284,9 @@
         for image_id in to_delete_images_ids:
             self.log('INFO: removing image {0}'.format(image_id))
             try:
-                # FIXME
                 self.glance_slave.images.delete(image_id)
                 self.log('DEBUG: image {0} removed'.format(image_id))
-            except (HTTPConflict, HTTPForbidden) as e:
+            except Exception as e:  # TODO narrow the exception down
                 self.log('ERROR: could not delete {0} :: '
                          '{1}'.format(image_id, e))
 
@@ -356,8 +346,8 @@
 
         # XXX(aluria): no image on slave service
         # XXX(aluria): look for similar project on slave
-        for slave_project_id, slave_project_name in \
-          self.projects_slave.items():
+        for slave_project_id, slave_project_name in (
+                self.projects_slave.items()):
             slave_to_master = slave_project_name.replace(self.REGION_SLAVE,
                                                          self.REGION_MASTER)
             # XXX(aluria): pitfall, if on master service there are
@@ -398,59 +388,45 @@
 
     def glance_connect_slave(self):
         try:
-            username = os.environ['OS_USERNAME']
-            password = os.environ['OS_PASSWORD']
-            tenant_name = os.environ['OS_TENANT_NAME']
-            auth_url = os.environ['OS_AUTH_URL']
-            self.REGION_MASTER = os.environ['OS_REGION_NAME_MASTER'].upper()
+            self.keystone = os_client_config.session_client(
+                    'identity',
+                    cloud='envvars'
+                    )
+            self.glance_slave = os_client_config.make_client(
+                    'image',
+                    cloud='envvars'
+                    )
         except Exception as e:
             self.log('EXCEPTION: {0}'.format(e))
             self.log('ERROR: unable to load environment variables, please '
                      'source novarc')
             self.release_lock()
             sys.exit(2)
-        self.keystone = client.Client(username=username, password=password,
-                                      tenant_name=tenant_name,
-                                      auth_url=auth_url)
-
         if not self.projects_slave:
-            self.projects_slave = dict([(tenant.id, tenant.name) for tenant in
-                                       self.keystone.tenants.list() if
-                                       tenant.enabled])
-        token = self.keystone.auth_token
-        service = self.keystone.services.find(name='glance')
-        endpoint = self.keystone.endpoints.find(service_id=service.id)
-        glance_url = endpoint.internalurl
-        self.REGION_SLAVE = endpoint.region.upper()
-        self.glance_slave = Client('2', endpoint=glance_url, token=token)
+            self.projects_slave = dict(
+                    [(tenant['id'], tenant['name']) for tenant in
+                     self.keystone.get('/projects').json()['projects'] if
+                     tenant['enabled']])
+        self.REGION_SLAVE = os.environ['OS_REGION_NAME'].upper()
         return self.glance_slave
 
     def glance_connect_master(self):
         try:
-            username = 'glancesync'
-            password = os.environ['OS_PASSWORD_MASTER']
-            tenant_name = os.environ['OS_TENANT_NAME']
-            auth_url = os.environ['OS_AUTH_URL_MASTER']
-            glance_url_master = os.environ['OS_GLANCE_URL_MASTER']
+            self.glance_master = os_client_config.make_client(
+                'image',
+                cloud='master',
+                )
+            self.keystone_master = os_client_config.make_client(
+                    'identity',
+                    identity_api_version=3,
+                    cloud='master')
+            self.REGION_MASTER = self.keystone_master.regions.find().id
         except Exception as e:
-            self.log('EXCEPTION: glance_connect_master :: {0}'.format(e))
-            self.log('ERROR: unable to load environment variables, please '
-                     'source novarc')
+            self.log('EXCEPTION: {0}'.format(e))
+            self.log('ERROR: unable to load master cloud environment '
+                     'variables, please source glancesync.novarc')
             self.release_lock()
             sys.exit(2)
-        self.keystone_master = client.Client(username=username,
-                                             password=password,
-                                             tenant_name=tenant_name,
-                                             auth_url=auth_url)
-        token = self.keystone_master.auth_token
-        # XXX: uses adminURL, which is OS-MGMT
-        # XXX: publicURL should be used
-        # service = self.keystone_master.services.find(name='glance')
-        # endpoint = self.keystone_master.endpoints.find(service_id=service.id)
-        # glance_url = endpoint.publicurl
-        # self.REGION_MASTER = endpoint.region.upper()
-        self.glance_master = Client('2', endpoint=glance_url_master,
-                                    token=token)
         return self.glance_master
 
     def timestamp_now(self):
@@ -507,6 +483,7 @@
         ['user_id',
          'image_type',
          'image_state',
+
          'image_location',
          'base_image_ref',
          'owner_id']
@@ -537,8 +514,8 @@
                         self.projects_slave[metadata_slave['owner']]
                 # ie. admin, services, MASTER-CENTRAL
                 slave_to_master = \
-                        slave_project_name.replace(self.REGION_SLAVE,
-                                                   self.REGION_MASTER)
+                    slave_project_name.replace(self.REGION_SLAVE,
+                                               self.REGION_MASTER)
                 # ie. admin, services, MASTER-CENTRAL
                 if master_project_name in (slave_project_name,
                                            slave_to_master):
@@ -606,6 +583,7 @@
         self.log('ending glance image sync slave run')
         self.release_lock()
 
+
 if __name__ == '__main__':
     parser = argparse.ArgumentParser(description='Synchronize remote images '
                                      'metadata to disk and import into glance')

=== modified file 'metadata.yaml'
--- metadata.yaml	2016-04-30 00:06:33 +0000
+++ metadata.yaml	2018-07-30 06:16:31 +0000
@@ -6,6 +6,9 @@
 description: |
   This charm will import  glance images from a master  openstack installations.
   It will copy image files and metadata with rsync over ssh to local disk and import into glance from there.
+series:
+  - xenial
+  - trusty
 provides:
   nrpe-external-master:
     interface: nrpe-external-master
@@ -13,3 +16,8 @@
 requires:
   keystone-admin:
     interface: keystone-admin
+extra-bindings:
+  public:
+  admin:
+  internal:
+

=== modified file 'reactive/glance-sync-slave.py'
--- reactive/glance-sync-slave.py	2017-02-22 18:04:08 +0000
+++ reactive/glance-sync-slave.py	2018-07-30 06:16:31 +0000
@@ -9,15 +9,15 @@
 from charmhelpers.core import hookenv, templating
 from charmhelpers import fetch
 from charms.reactive import hook, when, set_state, remove_state
+from charmhelpers.contrib.openstack.utils import config_flags_parser
 
 
 @hook('install')
 def install_glance_sync_slave():
     hookenv.status_set('maintenance', 'Installing')
     fetch.apt_update()
-    fetch.apt_install('python-glanceclient')
-    fetch.apt_install('python-nose')
-    fetch.apt_install('python-dateutil')
+    packages = ['python-nose', 'python-dateutil', 'python-openstackclient']
+    fetch.apt_install(packages)
 
     # configure the variables that have a default value
     configure_config_dir()
@@ -152,26 +152,48 @@
 def configure_custom_novarc():
     configure_novarc()
 
-@when('config.changed.master_{password,auth_url,glance_url,region}')
+
+@when('config.changed.master_creds')
 def configure_master_novarc():
-    context = {}
-    for i in ('master_password', 'master_auth_url',
-              'master_glance_url', 'master_region'):
-        value = hookenv.config(i)
-        if not value:
-            hookenv.status_set('blocked', 'master service credentials '
-                               'missing')
-            return
-        context[i] = value
-
-    glancesync_novarc_file = os.path.join(config_dir, 'glancesync.novarc')
-
-    templating.render(source='glancesync.novarc.j2',
-                      target=glancesync_novarc_file,
+    # get context from master_novarc in config
+    # Puth the info in clouds.yaml for openstacksdk
+    # TODO combine novarc with this, one file for both clouds
+    creds = {}
+    keystone_creds = config_flags_parser(hookenv.config('master_creds'))
+    if not keystone_creds:
+        hookenv.status_set('blocked', 'master service credentials '
+                           'missing')
+        return
+    else:
+        if '/v3' in keystone_creds['auth_url']:
+            creds = {
+                'master_username': keystone_creds['username'],
+                'master_password': keystone_creds['password'],
+                'master_project': keystone_creds['project'],
+                'master_region': keystone_creds['region'],
+                'master_auth_url': keystone_creds['auth_url'],
+                'master_auth_version': '3',
+                'master_user_domain': keystone_creds['domain'],
+                'master_project_domain': keystone_creds['domain'],
+            }
+        else:
+            creds = {
+                'master_username': keystone_creds['username'],
+                'master_password': keystone_creds['password'],
+                'master_project': keystone_creds['project'],
+                'master_region': keystone_creds['region'],
+                'master_auth_url': keystone_creds['auth_url'],
+            }
+
+    config_dir = '/etc/openstack'
+    clouds_yaml = os.path.join(config_dir, 'clouds.yaml')
+    templating.render(source='clouds.yaml.j2',
+                      target=clouds_yaml,
                       owner='ubuntu',
                       perms=0o600,
-                      context=relation)
-    hookenv.status_set('active', 'glancesync.novarc configured')
+                      context=creds)
+    hookenv.status_set('active', 'clouds.yaml configured')
+
 
 @hook('{requires:keystone-admin}-relation-{joined,changed}')
 def configure_relation_novarc(relation=None):
@@ -250,6 +272,7 @@
 
 
 @when('config.changed.sync_enabled')
+@when('config.changed.cron_frequency')
 def configure_cron():
     hookenv.status_set('maintenance', 'enabling sync')
     sync_enabled = hookenv.config('sync_enabled')
@@ -279,6 +302,18 @@
     hookenv.status_set('active', 'sync configured')
 
 
+@when('config.changed.trusted_ssl_ca')
+def fix_ssl():
+    config = hookenv.config()
+    cert_file = '/usr/local/share/ca-certificates/openstack-ca.crt'
+    trusted_ssl_ca = config.get('trusted_ssl_ca').strip()
+    hookenv.log("Writing ssl ca cert:{}".format(trusted_ssl_ca))
+    cert_content = base64.b64decode(trusted_ssl_ca).decode()
+    with open(cert_file, 'w') as f:
+        print(cert_content, file=f)
+    subprocess.call(["/usr/sbin/update-ca-certificates"])
+
+
 @hook('nrpe-external-master-relation-changed')
 def setup_nrpe_checks(nagios):
     hookenv.status_set('maintenance', 'Configuring nrpe checks')

=== added file 'templates/clouds.yaml.j2'
--- templates/clouds.yaml.j2	1970-01-01 00:00:00 +0000
+++ templates/clouds.yaml.j2	2018-07-30 06:16:31 +0000
@@ -0,0 +1,13 @@
+clouds:
+  master:
+    region_name: {{ master_region }}
+    auth:
+      username: {{ master_username }}
+      password: {{ master_password }}
+      project_name: {{ master_project }}
+      auth_url: {{ master_auth_url }}
+{%- if master_auth_version %}
+      user_domain_name: {{ master_user_domain }}
+      project_domain_name: {{ master_project_domain }}
+{%- endif %}
+

=== modified file 'templates/glance_sync_slave_cron.j2'
--- templates/glance_sync_slave_cron.j2	2017-02-22 18:04:08 +0000
+++ templates/glance_sync_slave_cron.j2	2018-07-30 06:16:31 +0000
@@ -6,7 +6,7 @@
 MAILTO={{ admin_email }}
 SHELL=/bin/bash
 
-{{ 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'
+{{ 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'
 
 {% endif %}
 

=== removed file 'templates/glancesync.novarc.j2'
--- templates/glancesync.novarc.j2	2017-02-22 18:04:08 +0000
+++ templates/glancesync.novarc.j2	1970-01-01 00:00:00 +0000
@@ -1,5 +0,0 @@
-export OS_PASSWORD_MASTER={{ master_password }}
-export OS_AUTH_URL_MASTER={{ master_auth_url }}
-export OS_GLANCE_URL_MASTER={{ master_glance_url }}
-export OS_REGION_NAME_MASTER={{ master_region }}
-

=== modified file 'templates/novarc.j2'
--- templates/novarc.j2	2017-02-17 17:39:38 +0000
+++ templates/novarc.j2	2018-07-30 06:16:31 +0000
@@ -1,6 +1,13 @@
-#export OS_AUTH_URL=http://{{ service_hostname }}:{{ service_port }}/v2.0
-export OS_AUTH_URL=https://{{ service_hostname }}:{{ service_port }}/v2.0
+export OS_AUTH_URL={{ service_protocol }}://{{ service_hostname }}:{{ service_port }}/v{{ api_version }}
+export OS_TENANT_NAME={{ service_tenant_name }}
 export OS_USERNAME={{ service_username }}
 export OS_PASSWORD={{ service_password }}
-export OS_TENANT_NAME={{ service_tenant_name }}
+export OS_PROJECT_NAME={{ service_project_name }}
+export OS_REGION_NAME={{ service_region }}
+{%- if api_version == '3' %}
+export OS_IDENTITY_API_VERSION={{ api_version }}
+export OS_AUTH_VERSION={{ api_version }}
+export OS_USER_DOMAIN_NAME={{ service_user_domain_name }}
+export OS_PROJECT_DOMAIN_NAME={{ service_user_domain_name }}
+{%- endif %}
 

