local image mirror and manage boot-sources

PROD-19211

Change-Id: I2cef9a36b7ea33545f56653e35b786aa67b2918d
diff --git a/README.rst b/README.rst
index f8ef542..6180179 100644
--- a/README.rst
+++ b/README.rst
@@ -64,6 +64,13 @@
           description: Test snippet
           enabled: true
           subnet: subnet1
+      boot_sources:
+        maas_mirror:
+          url: http://images.maas.io/ephemeral-v3/daily/
+          keyring_file: /usr/share/keyrings/ubuntu-cloudimage-keyring.gpg
+        local_mirror:
+          url: http://127.0.0.1/maas/images/ephemeral-v3/daily
+          keyring_file: /usr/share/keyrings/ubuntu-cloudimage-keyring.gpg
       boot_resources:
         bootscript1:
           title: bootscript
@@ -262,6 +269,23 @@
                     type: ext4
                     mount: '/var/log'
 
+Setup image mirror
+
+.. code-block:: yaml
+
+  maas:
+    mirror:
+      enabled: true
+      image:
+        release:
+          xenial:
+          keyring: /usr/share/keyrings/ubuntu-cloudimage-keyring.gpg
+          upstream: http://images.maas.io/ephemeral-v3/daily/
+          local_dir: /var/www/html/maas/images/ephemeral-v3/daily
+          arch: amd64
+          subarch: 'generic|hwe-t'
+          count: 1
+
 Usage of local repos
 
 .. code-block:: yaml
diff --git a/_modules/maas.py b/_modules/maas.py
index ee110c8..329a467 100644
--- a/_modules/maas.py
+++ b/_modules/maas.py
@@ -176,7 +176,11 @@
             LOG.exception('Error Global')
             raise
         if ret['errors']:
-            raise Exception(ret)
+            if 'already exists' in str(ret['errors']):
+                ret['success'] = ret['errors']
+                ret['errors'] = {}
+            else:
+                raise Exception(ret)
         return ret
 
 
@@ -281,6 +285,27 @@
         return new
 
 
+class Boot_source(MaasObject):
+    def __init__(self):
+        super(Boot_source, self).__init__()
+        self._all_elements_url = u'api/2.0/boot-sources/'
+        self._create_url = u'api/2.0/boot-sources/'
+        self._update_url = u'api/2.0/boot-sources/{0}/'
+        self._config_path = 'region.boot_sources'
+        self._element_key = 'id'
+
+    def fill_data(self, name, boot_source):
+        data = {
+            'name': name,
+            'url': boot_source.get('url', ''),
+            'keyring_filename': boot_source.get('keyring_file', ''),
+        }
+        return data
+
+    def update(self, new, old):
+        new['id'] = str(old['id'])
+        return new
+
 class PacketRepository(MaasObject):
     def __init__(self):
         super(PacketRepository, self).__init__()
@@ -892,6 +917,8 @@
 def process_fabrics():
     return Fabric().process()
 
+def process_boot_sources():
+    return Boot_source().process()
 
 def process_subnets():
     return Subnet().process()
diff --git a/maas/client/init.sls b/maas/client/init.sls
deleted file mode 100644
index 9826979..0000000
--- a/maas/client/init.sls
+++ /dev/null
@@ -1 +0,0 @@
-init.sls
diff --git a/maas/init.sls b/maas/init.sls
index 878705e..da8376b 100644
--- a/maas/init.sls
+++ b/maas/init.sls
@@ -3,6 +3,9 @@
 {%- if pillar.maas.cluster is defined %}
 - maas.cluster
 {%- endif %}
+{%- if pillar.maas.mirror is defined %}
+- maas.mirror
+{%- endif %}
 {%- if pillar.maas.region is defined %}
 - maas.region
 {%- endif %}
diff --git a/maas/map.jinja b/maas/map.jinja
index 8c0a7d9..bcf8a48 100644
--- a/maas/map.jinja
+++ b/maas/map.jinja
@@ -24,3 +24,11 @@
 {%- endload %}
 
 {%- set region = salt['grains.filter_by'](region_defaults, merge=salt['pillar.get']('maas:region', {})) %}
+
+{%- load_yaml as mirror_defaults %}
+Debian:
+  pkgs:
+  - simplestreams
+{%- endload %}
+
+{%- set mirror = salt['grains.filter_by'](mirror_defaults, merge=salt['pillar.get']('maas:mirror', {})) %}
diff --git a/maas/mirror.sls b/maas/mirror.sls
new file mode 100644
index 0000000..f7c586d
--- /dev/null
+++ b/maas/mirror.sls
@@ -0,0 +1,23 @@
+{%- from "maas/map.jinja" import mirror with context %}
+
+{%- if mirror.get('enabled') %}
+
+{%- if mirror.get('image') %}
+
+maas_mirror_packages:
+  pkg.installed:
+    - names: {{ mirror.pkgs }}
+
+{%- for release_name, release in mirror.image.release.iteritems() %}
+
+mirror_image_{{ release_name }}:
+  cmd.run:
+  - name: "sstream-mirror --keyring={{ release.keyring }} {{ release.upstream }} {{ release.local_dir }} {%- if release.get('arch') %} 'arch={{ release.arch }}'{%- endif %} {%- if release.get('subarch') %} 'subarch~({{ release.subarch }})'{%- endif %} 'release~({{ release_name }})' {%- if release.get('count') %} --max={{ release.count }}{%- endif %}"
+  - require:
+    - pkg: maas_mirror_packages
+
+{%- endfor %}
+
+{%- endif %}
+
+{%- endif %}
diff --git a/maas/region.sls b/maas/region.sls
index a27142d..60ccfdd 100644
--- a/maas/region.sls
+++ b/maas/region.sls
@@ -193,6 +193,14 @@
     {%- endif %}
 {%- endif %}
 
+{%- if region.get('boot_sources', False)  %}
+maas_boot_sources:
+  module.run:
+  - name: maas.process_boot_sources
+  - require:
+    - module: maas_config
+{%- endif %}
+
 {%- if region.get('dhcp_snippets', False)  %}
 maas_dhcp_snippets:
   module.run:
diff --git a/tests/pillar/maas_mirror.sls b/tests/pillar/maas_mirror.sls
new file mode 100644
index 0000000..090c794
--- /dev/null
+++ b/tests/pillar/maas_mirror.sls
@@ -0,0 +1,12 @@
+maas:
+  mirror:
+    enabled: true
+    image:
+      release:
+        xenial:
+          keyring: '/usr/share/keyrings/ubuntu-cloudimage-keyring.gpg'
+          upstream: 'http://images.maas.io/ephemeral-v3/daily/'
+          local_dir: '/var/www/html/maas/images/ephemeral-v3/daily'
+          arch: amd64
+          subarch: 'generic|hwe-t'
+          count: '1'