Merge "added chain file for salt api ssl"
diff --git a/README.rst b/README.rst
index 7ec1dfd..0619d82 100644
--- a/README.rst
+++ b/README.rst
@@ -189,7 +189,7 @@
 
     salt-call event.send 'salt/minion/install'
 
-Run any orchestration pipeline
+Run any defined orchestration pipeline
 
 .. code-block:: yaml
 
@@ -205,22 +205,49 @@
 
     salt-call event.send 'salt/orchestrate/start' "{'orchestrate': 'salt/orchestrate/infra_install.sls'}"
 
-Classify node after start
+Synchronise modules and pillars on minion start.
 
 .. code-block:: yaml
 
     salt:
       master:
         reactor:
-          reclass/minion/classify:
-          - salt://reclass/reactor/node_register.sls
+          'salt/minion/*/start':
+          - salt://salt/reactor/minion_start.sls
 
-Event to trigger the node classification
+Add and/or remove the minion key
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        reactor:
+          salt/key/create:
+          - salt://salt/reactor/key_create.sls
+          salt/key/remove:
+          - salt://salt/reactor/key_remove.sls
+
+Event to trigger the key creation
 
 .. code-block:: bash
 
-    salt-call event.send 'reclass/minion/classify' "{'node_master_ip': '$config_host', 'node_ip': '${node_ip}', 'node_domain': '$node_domain', 'node_cluster': '$node_cluster', 'node_hostname': '$node_hostname', 'node_os': '$node_os'}"
+    salt-call event.send 'salt/key/create' "{'node_name': 'id-of-minion', 'orch_post_create': 'kubernetes/orchestrate/compute_install.sls'}"
 
+.. note::
+
+    You can add pass additional `orch_pre_create`, `orch_post_create`,
+    `orch_pre_remove` or `orch_post_remove` parameters to the event to call
+    extra orchestrate files. This can be useful for example for
+    registering/unregistering nodes from the monitoring alarms or dashboards.
+
+    The key creation event needs to be run from other machine than the one
+    being registered.
+
+Event to trigger the key removal
+
+.. code-block:: bash
+
+    salt-call event.send 'salt/key/remove'
 
 Salt syndic
 -----------
diff --git a/_modules/saltkey.py b/_modules/saltkey.py
new file mode 100644
index 0000000..97dc5c9
--- /dev/null
+++ b/_modules/saltkey.py
@@ -0,0 +1,106 @@
+from __future__ import absolute_import
+
+# Import python libs
+import logging
+import os
+
+try:
+    import paramiko
+    HAS_PARAMIKO = True
+except:
+    HAS_PARAMIKO = False
+
+# Import Salt libs
+import salt.config
+import salt.wheel
+
+LOG = logging.getLogger(__name__)
+
+
+def __virtual__():
+    '''
+    Only load if paramiko library exist.
+    '''
+    if not HAS_PARAMIKO:
+        return (
+            False,
+            'Can not load module saltkey: paramiko library not found')
+    return True
+
+
+def key_create(id_, host, force=False):
+    '''
+    Generates minion keypair, accepts it on master and injects it to minion via SSH.
+
+    CLI Examples:
+
+    .. code-block:: bash
+
+        salt-call saltkey.key_create <MINION_ID> <MINION_IP_ADDRESS> force=False
+    '''
+    ret = {
+        'retcode': 0,
+        'msg': 'Salt Key for minion %s is already accepted' % id_,
+    }
+
+    opts = salt.config.master_config('/etc/salt/master')
+    wheel = salt.wheel.WheelClient(opts)
+    keys = wheel.cmd('key.gen_accept', arg=[id_], kwarg={'force': force})
+    pub_key = keys.get('pub', None)
+    priv_key = keys.get('priv', None)
+
+    if pub_key and priv_key:
+        ssh = paramiko.SSHClient()
+        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+        # Establish SSH connection to minion
+        try:
+            ssh.connect(host)
+        except paramiko.ssh_exception.AuthenticationException:
+            msg = ('Could not establish SSH connection to minion "%s" on address %s, please ensure '
+                   'that current user\'s SSH key is present in minions authorized_keys.') % (id_, host)
+            LOG.error(msg)
+            ret['retcode'] = 1
+            ret['msg'] = msg
+            wheel.cmd_async({'fun': 'key.delete', 'match': id_})
+            return ret
+        except Exception as e:
+            msg = ('Unknown error occured while establishing SSH connection '
+                   'to minion "%s" on address %s: %s') % (id_, host, repr(e))
+            LOG.error(msg)
+            ret['retcode'] = 1
+            ret['msg'] = msg
+            wheel.cmd_async({'fun': 'key.delete', 'match': id_})
+            return ret
+        # Setup the keys on minion side the ugly way, nice one didn't work
+        key_path = '/etc/salt/pki/minion'
+        command = ('echo "%(pub_key)s" > %(pub_path)s && chmod 644 %(pub_path)s && '
+                   'echo "%(priv_key)s" > %(priv_path)s && chmod 400 %(priv_path)s && '
+                   'salt-call --local service.restart salt-minion') % {
+            'pub_path': os.path.join(key_path, 'minion.pub'),
+            'pub_key': pub_key,
+            'priv_path': os.path.join(key_path, 'minion.pem'),
+            'priv_key': priv_key
+        }
+
+        ssh_chan = ssh.get_transport().open_session()
+        ssh_chan.exec_command(command)
+        # Wait for command return
+        while True:
+            if ssh_chan.exit_status_ready():
+                exit_status = ssh_chan.recv_exit_status()
+                stderr = ssh_chan.recv_stderr(1000)
+                stdout = ssh_chan.recv(1000)
+                break
+        ssh.close()
+        # Evaluate SSH command exit status
+        if exit_status != 0:
+            msg = 'Keypair injection to Salt minion failed on target with following error: %s' % stderr
+            LOG.error(msg)
+            ret['retcode'] = exit_status
+            ret['msg'] = msg
+            return ret
+
+        ret['msg'] = 'Salt Key successfully created'
+
+    return ret
+
diff --git a/metadata/service/master/reactor/minion_start.yml b/metadata/service/master/reactor/minion_start.yml
new file mode 100644
index 0000000..cbe41d9
--- /dev/null
+++ b/metadata/service/master/reactor/minion_start.yml
@@ -0,0 +1,6 @@
+parameters:
+  salt:
+    master:
+      reactor:
+        'salt/minion/*/start':
+        - salt://salt/reactor/minion_start.sls
diff --git a/salt/map.jinja b/salt/map.jinja
index 8920fa6..5df6d2b 100644
--- a/salt/map.jinja
+++ b/salt/map.jinja
@@ -98,6 +98,8 @@
   - python-m2crypto
   - python-psutil
   - python-yaml
+  cert_pkgs:
+  - ca-certificates
 Gentoo:
   pkgs:
   - app-admin/salt
@@ -110,6 +112,8 @@
   - m2crypto
   - psutils
   - PyYAML
+  cert_pkgs:
+  - ca-certificates
 {%- endload %}
 
 {%- if pillar.salt.minion is defined %}
diff --git a/salt/minion/cert.sls b/salt/minion/cert.sls
index c04215a..f37924f 100644
--- a/salt/minion/cert.sls
+++ b/salt/minion/cert.sls
@@ -171,13 +171,7 @@
 
 salt_ca_certificates_packages:
   pkg.installed:
-{%- if grains.os_family == 'Debian' %}
-    - name: ca-certificates
-{%- elif grains.os_family == 'RedHat' %}
-    - name: ca-certificates
-{%- else %}
-    - name: []
-{%- endif %}
+    - names: {{ minion.cert_pkgs }}
 
 salt_update_certificates:
   cmd.wait:
diff --git a/salt/orchestrate/key_create.sls b/salt/orchestrate/key_create.sls
new file mode 100644
index 0000000..74a8918
--- /dev/null
+++ b/salt/orchestrate/key_create.sls
@@ -0,0 +1,6 @@
+{%- set node_name = salt['pillar.get']('node_name') %}
+
+key_create_{{ node_name }}:
+  salt.wheel:
+  - name: key.gen_accept
+  - id_: {{ node_name }}
diff --git a/salt/orchestrate/key_remove.sls b/salt/orchestrate/key_remove.sls
new file mode 100644
index 0000000..f8646b1
--- /dev/null
+++ b/salt/orchestrate/key_remove.sls
@@ -0,0 +1,6 @@
+{%- set node_name = salt['pillar.get']('node_name') %}
+
+key_create_{{ node_name }}:
+  salt.wheel:
+  - name: key.delete
+  - match: {{ node_name }}
diff --git a/salt/orchestrate/node_install.sls b/salt/orchestrate/node_install.sls
index c66b4e2..c053313 100644
--- a/salt/orchestrate/node_install.sls
+++ b/salt/orchestrate/node_install.sls
@@ -2,22 +2,22 @@
 
 linux_state:
   salt.state:
-    - tgt: '{{ node_name }}'
-    - sls: linux
-    - queue: True
+  - tgt: '{{ node_name }}'
+  - sls: linux
+  - queue: True
 
 salt_state:
   salt.state:
-    - tgt: '{{ node_name }}'
-    - sls: salt.minion
-    - queue: True
-    - require:
-      - salt: linux_state
+  - tgt: '{{ node_name }}'
+  - sls: salt.minion
+  - queue: True
+  - require:
+    - salt: linux_state
 
 misc_states:
   salt.state:
-    - tgt: '{{ node_name }}'
-    - sls: ntp,openssh
-    - queue: True
-    - require:
-      - salt: salt_state
+  - tgt: '{{ node_name }}'
+  - sls: ntp,openssh
+  - queue: True
+  - require:
+    - salt: salt_state
diff --git a/salt/reactor/infra_install.sls b/salt/reactor/infra_install.sls
index 17e7d9c..5493970 100644
--- a/salt/reactor/infra_install.sls
+++ b/salt/reactor/infra_install.sls
@@ -1,5 +1,5 @@
 
 orchestrate_infra_install:
   runner.state.orchestrate:
-    - mods: salt://salt/orchestrate/infra_install.sls
-    - queue: True
+  - mods: salt://salt/orchestrate/infra_install.sls
+  - queue: True
diff --git a/salt/reactor/key_create.sls b/salt/reactor/key_create.sls
new file mode 100644
index 0000000..b74a3e7
--- /dev/null
+++ b/salt/reactor/key_create.sls
@@ -0,0 +1,29 @@
+
+{% if data.data.orch_pre_create is defined %}
+
+orchestrate_node_key_pre_create:
+  runner.state.orchestrate:
+  - mods: salt://{{ data.data.orch_pre_create }}
+  - queue: True
+  - pillar:
+      node_name: {{ data.data['node_name'] }}
+
+{% endif %}
+
+node_key_create:
+  runner.state.orchestrate:
+  - mods: salt://salt/orchestrate/key_create.sls
+  - queue: True
+  - pillar:
+      node_name: {{ data.data['node_name'] }}
+
+{% if data.data.orch_post_create is defined %}
+
+orchestrate_node_key_post_create:
+  runner.state.orchestrate:
+  - mods: salt://{{ data.data.orch_post_create }}
+  - queue: True
+  - pillar:
+      node_name: {{ data.data['node_name'] }}
+
+{% endif %}
diff --git a/salt/reactor/key_remove.sls b/salt/reactor/key_remove.sls
new file mode 100644
index 0000000..ca23bed
--- /dev/null
+++ b/salt/reactor/key_remove.sls
@@ -0,0 +1,29 @@
+
+{% if data.data.orch_pre_remove is defined %}
+
+orchestrate_node_key_pre_remove:
+  runner.state.orchestrate:
+  - mods: salt://{{ data.data.orch_pre_remove }}
+  - queue: True
+  - pillar:
+      node_name: {{ data.data['node_name'] }}
+
+{% endif %}
+
+node_key_remove:
+  runner.state.orchestrate:
+  - mods: salt://salt/orchestrate/key_remove.sls
+  - queue: True
+  - pillar:
+      node_name: {{ data.data['node_name'] }}
+
+{% if data.data.orch_post_remove is defined %}
+
+orchestrate_node_key_post_remove:
+  runner.state.orchestrate:
+  - mods: salt://{{ data.data.orch_post_remove }}
+  - queue: True
+  - pillar:
+      node_name: {{ data.data['node_name'] }}
+
+{% endif %}
diff --git a/salt/reactor/node_start.sls b/salt/reactor/minion_start.sls
similarity index 60%
rename from salt/reactor/node_start.sls
rename to salt/reactor/minion_start.sls
index 904822c..f652b5b 100644
--- a/salt/reactor/node_start.sls
+++ b/salt/reactor/minion_start.sls
@@ -1,8 +1,10 @@
 
-node_sync_all:
+minion_sync_all:
   local.saltutil.sync_all:
   - tgt: {{ data.id }}
+  - queue: True
 
-node_refresh_pillar:
+minion_refresh_pillar:
   local.saltutil.refresh_pillar:
   - tgt: {{ data.id }}
+  - queue: True
diff --git a/salt/reactor/node_install.sls b/salt/reactor/node_install.sls
index 96e3c3b..64905ed 100644
--- a/salt/reactor/node_install.sls
+++ b/salt/reactor/node_install.sls
@@ -1,7 +1,7 @@
 
 orchestrate_node_install:
   runner.state.orchestrate:
-    - mods: salt://salt/orchestrate/node_install.sls
-    - queue: True
-    - pillar:
-        event_originator: {{ data.id }}
+  - mods: salt://salt/orchestrate/node_install.sls
+  - queue: True
+  - pillar:
+      event_originator: {{ data.id }}
diff --git a/salt/reactor/orchestrate_start.sls b/salt/reactor/orchestrate_start.sls
index 752dc55..d3703c4 100644
--- a/salt/reactor/orchestrate_start.sls
+++ b/salt/reactor/orchestrate_start.sls
@@ -1,5 +1,5 @@
 
-orchestrate_orchestrate_start:
+orchestrate_orchestrate_run:
   runner.state.orchestrate:
-    - mods: salt://{{ data.data.orchestrate }}
-    - queue: {{ data.data.get('queue', True) }}
+  - mods: salt://{{ data.data.orchestrate }}
+  - queue: {{ data.data.get('queue', True) }}