]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ansible.git/commitdiff
resync ceph-iscsi-gw with old upstream 1747/head
authorSébastien Han <seb@redhat.com>
Fri, 4 Aug 2017 18:18:11 +0000 (20:18 +0200)
committerSébastien Han <seb@redhat.com>
Wed, 13 Sep 2017 00:06:10 +0000 (18:06 -0600)
Taken from https://github.com/pcuzner/ceph-iscsi-ansible/tree/tcmu-fixes

Closes: https://bugzilla.redhat.com/show_bug.cgi?id=1454945 and
https://bugzilla.redhat.com/show_bug.cgi?id=1484083
Signed-off-by: Sébastien Han <seb@redhat.com>
33 files changed:
Vagrantfile
ceph-ansible.spec.in
group_vars/all.yml.sample
group_vars/iscsi-gws.yml.sample
group_vars/rhcs.yml.sample
infrastructure-playbooks/purge-cluster.yml
library/igw_client.py [new file with mode: 0644]
library/igw_gateway.py [new file with mode: 0644]
library/igw_lun.py [new file with mode: 0644]
library/igw_purge.py [new file with mode: 0644]
roles/ceph-common/tasks/checks/check_system.yml
roles/ceph-common/tasks/installs/install_redhat_packages.yml
roles/ceph-common/tasks/installs/redhat_community_repository.yml
roles/ceph-common/tasks/installs/redhat_dev_repository.yml
roles/ceph-defaults/defaults/main.yml
roles/ceph-iscsi-gw/LICENSE [new file with mode: 0644]
roles/ceph-iscsi-gw/README [new file with mode: 0644]
roles/ceph-iscsi-gw/README.md [new file with mode: 0644]
roles/ceph-iscsi-gw/ceph-iscsi-ansible.spec [new file with mode: 0644]
roles/ceph-iscsi-gw/defaults/main.yml
roles/ceph-iscsi-gw/library/igw_client.py [new file with mode: 0644]
roles/ceph-iscsi-gw/library/igw_gateway.py [new file with mode: 0644]
roles/ceph-iscsi-gw/library/igw_lun.py [new file with mode: 0644]
roles/ceph-iscsi-gw/library/igw_purge.py [new file with mode: 0644]
roles/ceph-iscsi-gw/meta/main.yml
roles/ceph-iscsi-gw/tasks/configure_iscsi.yml [new file with mode: 0644]
roles/ceph-iscsi-gw/tasks/deploy_ssl_keys.yml [new file with mode: 0644]
roles/ceph-iscsi-gw/tasks/generate_crt.yml [new file with mode: 0644]
roles/ceph-iscsi-gw/tasks/main.yml
roles/ceph-iscsi-gw/tasks/prerequisites.yml [new file with mode: 0644]
roles/ceph-iscsi-gw/templates/iscsi-gateway.cfg.j2 [new file with mode: 0644]
site.yml.sample
tests/functional/centos/7/cluster/hosts

index 5b0962ed9590844d6c09c6777171ea0e28d6019d..ae02f838ce89e2fca300f878f74269b2185d5f0c 100644 (file)
@@ -50,15 +50,15 @@ ansible_provision = proc do |ansible|
   # these aren't supported by Vagrant, see
   # https://github.com/mitchellh/vagrant/issues/3539
   ansible.groups = {
-    'mons'            => (0..NMONS - 1).map { |j| "#{LABEL_PREFIX}mon#{j}" },
-    'osds'            => (0..NOSDS - 1).map { |j| "#{LABEL_PREFIX}osd#{j}" },
-    'mdss'            => (0..NMDSS - 1).map { |j| "#{LABEL_PREFIX}mds#{j}" },
-    'rgws'            => (0..NRGWS - 1).map { |j| "#{LABEL_PREFIX}rgw#{j}" },
-    'nfss'            => (0..NNFSS - 1).map { |j| "#{LABEL_PREFIX}nfs#{j}" },
-    'rbd_mirrors'     => (0..NRBD_MIRRORS - 1).map { |j| "#{LABEL_PREFIX}rbd_mirror#{j}" },
-    'clients'         => (0..CLIENTS - 1).map { |j| "#{LABEL_PREFIX}client#{j}" },
-    'iscsi_gw'        => (0..NISCSI_GWS - 1).map { |j| "#{LABEL_PREFIX}iscsi_gw#{j}" },
-    'mgrs'            => (0..MGRS - 1).map { |j| "#{LABEL_PREFIX}mgr#{j}" }
+    'mons'             => (0..NMONS - 1).map { |j| "#{LABEL_PREFIX}mon#{j}" },
+    'osds'             => (0..NOSDS - 1).map { |j| "#{LABEL_PREFIX}osd#{j}" },
+    'mdss'             => (0..NMDSS - 1).map { |j| "#{LABEL_PREFIX}mds#{j}" },
+    'rgws'             => (0..NRGWS - 1).map { |j| "#{LABEL_PREFIX}rgw#{j}" },
+    'nfss'             => (0..NNFSS - 1).map { |j| "#{LABEL_PREFIX}nfs#{j}" },
+    'rbd_mirrors'      => (0..NRBD_MIRRORS - 1).map { |j| "#{LABEL_PREFIX}rbd_mirror#{j}" },
+    'clients'          => (0..CLIENTS - 1).map { |j| "#{LABEL_PREFIX}client#{j}" },
+    'iscsi_gws'        => (0..NISCSI_GWS - 1).map { |j| "#{LABEL_PREFIX}iscsi_gw#{j}" },
+    'mgrs'             => (0..MGRS - 1).map { |j| "#{LABEL_PREFIX}mgr#{j}" }
   }
 
   if RESTAPI then
@@ -397,7 +397,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
   end
 
   (0..NISCSI_GWS - 1).each do |i|
-    config.vm.define "#{LABEL_PREFIX}iscsi_gw#{i}" do |iscsi_gw|
+    config.vm.define "#{LABEL_PREFIX}iscsi-gw#{i}" do |iscsi_gw|
       iscsi_gw.vm.hostname = "#{LABEL_PREFIX}iscsi-gw#{i}"
       if ASSIGN_STATIC_IP
         iscsi_gw.vm.network :private_network,
@@ -420,7 +420,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
       end
       # Parallels
       iscsi_gw.vm.provider "parallels" do |prl|
-        prl.name = "ceph-iscsi-gw#{i}"
+        prl.name = "iscsi-gw#{i}"
         prl.memory = "#{MEMORY}"
       end
 
index ea10d1d436e8f769a661522da975678f47422b9b..0baee039072ed53a4f01b36d3e7db6c7879c6478 100644 (file)
@@ -46,13 +46,6 @@ pushd %{buildroot}%{_datarootdir}/ceph-ansible
   rm -r infrastructure-playbooks/untested-by-ci
 popd
 
-# Strip iscsi files.
-# These are just placeholders until ceph-iscsi-gw can merge into
-# ceph-ansible. (See https://bugzilla.redhat.com/1454945).
-pushd %{buildroot}%{_datarootdir}/ceph-ansible
-  rm -r roles/ceph-iscsi-gw
-popd
-
 %check
 # Borrowed from upstream's .travis.yml:
 ansible-playbook -i dummy-ansible-hosts test.yml --syntax-check
index 22529125daa6ecf6d23f5d60d3eb21ba16961562..c648417c6c5bb2c4ab1d9f162e089f8ab24fb2ed 100644 (file)
@@ -51,7 +51,7 @@ dummy:
 #restapi_group_name: restapis
 #rbdmirror_group_name: rbdmirrors
 #client_group_name: clients
-#iscsi_group_name: iscsigws
+#iscsi_gw_group_name: iscsi_gws
 #mgr_group_name: mgrs
 
 # If check_firewall is true, then ansible will try to determine if the
@@ -205,6 +205,8 @@ dummy:
 # flavors so far include: ceph_master, ceph_jewel, ceph_kraken, ceph_luminous
 #nfs_ganesha_flavor: "ceph_master"
 
+#ceph_iscsi_config_dev: true # special repo for deploying iSCSI gateways
+
 
 # REPOSITORY: CUSTOM
 #
index 1a03a20472ad0815646a188cf3af86c23f2f733e..e196e39cd47add0581fc28ef82d1ccef59fd220c 100644 (file)
@@ -7,4 +7,45 @@
 # file as a good configuration file when no variable in it.
 dummy:
 
+# You can override vars by using host or group vars
+
+# Specify the iqn for ALL gateways. This iqn is shared across the gateways, so an iscsi
+# client sees the gateway group as a single storage subsystem.
+#gateway_iqn: "iqn.2003-01.com.redhat.iscsi-gw:ceph-igw"
+
+# gateway_ip_list provides a list of the IP Addrresses - one per gateway - that will be used
+# as an iscsi target portal ip. The list must be comma separated - and the order determines
+# the sequence of TPG's within the iscsi target across each gateway. Once set, additional
+# gateways can be added, but the order must *not* be changed.
+#gateway_ip_list: "192.168.122.101,192.168.122.102,192.168.122.103"
+
+# rbd_devices defines the images that should be created and exported from the iscsi gateways.
+# If the rbd does not exist, it will be created for you. In addition you may increase the
+# size of rbd's by changing the size parameter and rerunning the playbook. A size value lower
+# than the current size of the rbd is ignored.
+#
+# the 'host' parameter defines which of the gateway nodes should handle the physical
+# allocation/expansion or removal of the rbd
+# to remove an image, simply use a state of 'absent'. This will first check the rbd is not allocated
+# to any client, and the remove it from LIO and then delete the rbd image
+#
+# NB. this variable definition can be commented out to bypass LUN management
+#rbd_devices:
+#  - { pool: 'rbd', image: 'ansible1', size: '30G', host: 'ceph-1', state: 'present' }
+#  - { pool: 'rbd', image: 'ansible2', size: '15G', host: 'ceph-1', state: 'present' }
+#  - { pool: 'rbd', image: 'ansible3', size: '30G', host: 'ceph-1', state: 'present' }
+#  - { pool: 'rbd', image: 'ansible4', size: '50G', host: 'ceph-1', state: 'present' }
+
+
+# client_connections defines the client ACL's to restrict client access to specific LUNs
+# The settings are as follows;
+# - image_list is a comma separated list of rbd images of the form <pool name>.<rbd_image_name>
+# - chap supplies the user and password the client will use for authentication of the
+#   form <user>/<password>
+# - status shows the intended state of this client definition - 'present' or 'absent'
+#
+# NB. this definition can be commented out to skip client (nodeACL) management
+#client_connections:
+#  - { client: 'iqn.1994-05.com.redhat:rh7-iscsi-client', image_list: 'rbd.ansible1,rbd.ansible2', chap: 'rh7-iscsi-client/redhat', status: 'present' }
+#  - { client: 'iqn.1991-05.com.microsoft:w2k12r2', image_list: 'rbd.ansible4', chap: 'w2k12r2/microsoft_w2k12', status: 'absent' }
 
index ee92c71df54a0e9aec9840ca283ddc670cdb6b9d..1c255fac668b9241aeaeaab3baed6d78d9b2da96 100644 (file)
@@ -51,7 +51,7 @@ fetch_directory: ~/ceph-ansible-keys
 #restapi_group_name: restapis
 #rbdmirror_group_name: rbdmirrors
 #client_group_name: clients
-#iscsi_group_name: iscsigws
+#iscsi_gw_group_name: iscsi_gws
 #mgr_group_name: mgrs
 
 # If check_firewall is true, then ansible will try to determine if the
@@ -205,6 +205,8 @@ ceph_repository: rhcs
 # flavors so far include: ceph_master, ceph_jewel, ceph_kraken, ceph_luminous
 #nfs_ganesha_flavor: "ceph_master"
 
+#ceph_iscsi_config_dev: true # special repo for deploying iSCSI gateways
+
 
 # REPOSITORY: CUSTOM
 #
index 42c4acb462864393739165a342fb2c08dc31d29b..759c1652b0ba7d9fb48864fc5183b686181974d3 100644 (file)
   hosts:
     - "{{ mon_group_name|default('mons') }}"
 
-  gather_facts: false # Already gathered previously
+  gather_facts: false # already gathered previously
 
   become: true
 
       path: /var/lib/ceph/
       state: absent
 
+- name: purge iscsi gateway(s)
+
+  vars:
+    igw_purge_type: all
+
+  hosts:
+    - "{{ iscsi_gw_group_name|default('iscsi-gw') }}"
+
+  gather_facts: false # already gathered previously
+
+  become: true
+
+  tasks:
+
+  - name: igw_purge | purging the gateway configuration
+    igw_purge:
+      mode: "gateway"
+
+  - name: igw_purge | deleting configured rbd devices
+    igw_purge:
+      mode: "disks"
+    when:
+      - igw_purge_type == 'all'
+
+  - name: restart rbd-target-gw daemons
+    service:
+      name: rbd-target-gw
+      state: restarted
+    when:
+      - ansible_service_mgr == 'systemd'
+
+
 - name: final cleanup - check any running ceph, purge ceph packages, purge config and remove data
 
   vars:
diff --git a/library/igw_client.py b/library/igw_client.py
new file mode 100644 (file)
index 0000000..cdd095b
--- /dev/null
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+
+__author__ = 'pcuzner@redhat.com'
+
+DOCUMENTATION = """
+---
+module: igw_client
+short_description: Manage iscsi gateway client definitions
+description:
+  - This module calls the 'client' configuration management module installed
+    on the iscsi gateway node to handle the definition of iscsi clients on the
+    gateway(s). This definition will setup iscsi authentication (e.g. chap),
+    and mask the required rbd images to the client.
+
+    The 'client' configuration module is provided by ceph-iscsi-config
+    rpm which is installed on the gateway nodes.
+
+    To support module debugging, this module logs to
+    /var/log/ansible-module-igw_config.log on the target machine(s).
+
+option:
+  client_iqn:
+    description:
+      - iqn of the client machine which should be connected or removed from the
+        iscsi gateway environment
+    required: true
+
+  image_list:
+    description:
+      - comma separated string providing the rbd images that this
+        client definition should have. The rbd images provided must use the
+        following format <pool_name>.<rbd_image_name>
+        e.g. rbd.disk1,rbd.disk2
+    required: true
+
+  chap:
+    description:
+      - chap credentials for the client to authenticate to the gateways
+        to gain access to the exported rbds (LUNs). The credentials is a string
+        value of the form 'username/password'. The iscsi client must then use
+        these settings to gain access to any LUN resources.
+    required: true
+
+  state:
+    description:
+      - desired state for this client - absent or present
+    required: true
+
+requirements: ['ceph-iscsi-config']
+
+author:
+  - 'Paul Cuzner'
+
+"""
+
+import os
+import logging
+from logging.handlers import RotatingFileHandler
+from ansible.module_utils.basic import *
+
+from ceph_iscsi_config.client import GWClient
+import ceph_iscsi_config.settings as settings
+
+# the main function is called ansible_main to allow the call stack
+# to be checked to determine whether the call to the ceph_iscsi_config
+# modules is from ansible or not
+def ansible_main():
+
+    fields = {
+        "client_iqn": {"required": True, "type": "str"},
+        "image_list": {"required": True, "type": "str"},
+        "chap": {"required": True, "type": "str"},
+        "state": {
+            "required": True,
+            "choices": ['present', 'absent'],
+            "type": "str"
+            },
+        }
+
+    module = AnsibleModule(argument_spec=fields,
+                           supports_check_mode=False)
+
+    client_iqn = module.params['client_iqn']
+
+    if module.params['image_list']:
+        image_list = module.params['image_list'].split(',')
+    else:
+        image_list = []
+
+    chap = module.params['chap']
+    desired_state = module.params['state']
+
+    logger.info("START - Client configuration started : {}".format(client_iqn))
+
+    # The client is defined using the GWClient class. This class handles
+    # client attribute updates, rados configuration object updates and LIO
+    # settings. Since the logic is external to this custom module, clients
+    # can be created/deleted by other methods in the same manner.
+    client = GWClient(logger, client_iqn, image_list, chap)
+    if client.error:
+        module.fail_json(msg=client.error_msg)
+
+    client.manage(desired_state)
+    if client.error:
+        module.fail_json(msg=client.error_msg)
+
+    logger.info("END   - Client configuration complete - {} "
+                "changes made".format(client.change_count))
+
+    changes_made = True if client.change_count > 0 else False
+
+    module.exit_json(changed=changes_made,
+                     meta={"msg": "Client definition completed {} "
+                                  "changes made".format(client.change_count)})
+
+if __name__ == '__main__':
+
+    module_name = os.path.basename(__file__).replace('ansible_module_', '')
+    logger = logging.getLogger(os.path.basename(module_name))
+    logger.setLevel(logging.DEBUG)
+    handler = RotatingFileHandler('/var/log/ansible-module-igw_config.log',
+                                  maxBytes=5242880,
+                                  backupCount=7)
+    log_fmt = logging.Formatter('%(asctime)s %(name)s %(levelname)-8s : '
+                                '%(message)s')
+    handler.setFormatter(log_fmt)
+    logger.addHandler(handler)
+
+    # initialise global variables used by all called modules
+    # e.g. ceph conffile, keyring etc
+    settings.init()
+
+    ansible_main()
diff --git a/library/igw_gateway.py b/library/igw_gateway.py
new file mode 100644 (file)
index 0000000..9c3a1d0
--- /dev/null
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+__author__ = 'pcuzner@redhat.com'
+
+
+DOCUMENTATION = """
+---
+module: igw_gateway
+short_description: Manage the iscsi gateway definition
+description:
+  - This module calls the 'gateway' configuration management module installed
+    on the iscsi gateway node(s) to handle the definition of iscsi gateways.
+    The module will configure;
+    * the iscsi target and target portal group (TPG)
+    * rbd maps to the gateway and registration of those rbds as LUNs to the
+      kernels LIO subsystem
+
+    The actual configuration modules are provided by ceph-iscsi-config rpm
+    which is installed on the gateway nodes.
+
+    To support module debugging, this module logs to
+    /var/log/ansible-module-igw_config.log on the target machine(s).
+
+option:
+  gateway_iqn:
+    description:
+      - iqn that all gateway nodes will use to present a common system image
+        name to iscsi clients
+    required: true
+
+  gateway_ip_list:
+    description:
+      - comma separated string providing the IP addresses that will be used
+        as iSCSI portal IPs to accept iscsi client connections. Each IP address
+        should equate to an IP on a gateway node - typically dedicated to iscsi
+        traffic. The order of the IP addresses determines the TPG sequence
+        within the target definition - so once defined, new gateways can be
+        added but *must* be added to the end of this list to preserve the tpg
+        sequence
+
+        e.g. 192.168.122.101,192.168.122.103
+    required: true
+
+  mode:
+    description:
+      - mode in which to run the gateway module. Two modes are supported
+        target ... define the iscsi target iqn, tpg's and portals
+        map ...... map luns to the tpg's, and also define the ALUA path setting
+                   for each LUN (activeOptimized/activenonoptimized)
+    required: true
+
+
+requirements: ['ceph-iscsi-config']
+
+author:
+  - 'Paul Cuzner'
+
+"""
+
+import os
+import logging
+
+from logging.handlers import RotatingFileHandler
+from ansible.module_utils.basic import *
+
+import ceph_iscsi_config.settings as settings
+
+from ceph_iscsi_config.gateway import GWTarget
+from ceph_iscsi_config.utils import valid_ip
+
+
+# the main function is called ansible_main to allow the call stack
+# to be checked to determine whether the call to the ceph_iscsi_config
+# modules is from ansible or not
+def ansible_main():
+    # Configures the gateway on the host. All images defined are added to
+    # the default tpg for later allocation to clients
+    fields = {"gateway_iqn": {"required": True, "type": "str"},
+              "gateway_ip_list": {"required": True},    # "type": "list"},
+              "mode": {
+                  "required": True,
+                  "choices": ['target', 'map']
+                  }
+              }
+
+    module = AnsibleModule(argument_spec=fields,
+                           supports_check_mode=False)
+
+    gateway_iqn = module.params['gateway_iqn']
+    gateway_ip_list = module.params['gateway_ip_list'].split(',')
+    mode = module.params['mode']
+
+    if not valid_ip(gateway_ip_list):
+        module.fail_json(msg="Invalid gateway IP address(es) provided - port "
+                             "22 check failed ({})".format(gateway_ip_list))
+
+    logger.info("START - GATEWAY configuration started - mode {}".format(mode))
+
+    gateway = GWTarget(logger, gateway_iqn, gateway_ip_list)
+    if gateway.error:
+        logger.critical("(ansible_main) Gateway init failed - "
+                        "{}".format(gateway.error_msg))
+        module.fail_json(msg="iSCSI gateway initialisation failed "
+                             "({})".format(gateway.error_msg))
+
+    gateway.manage(mode)
+
+    if gateway.error:
+        logger.critical("(main) Gateway creation or load failed, "
+                        "unable to continue")
+        module.fail_json(msg="iSCSI gateway creation/load failure "
+                             "({})".format(gateway.error_msg))
+
+
+    logger.info("END - GATEWAY configuration complete")
+    module.exit_json(changed=gateway.changes_made,
+                     meta={"msg": "Gateway setup complete"})
+
+
+if __name__ == '__main__':
+
+    module_name = os.path.basename(__file__).replace('ansible_module_', '')
+    logger = logging.getLogger(os.path.basename(module_name))
+    logger.setLevel(logging.DEBUG)
+    handler = RotatingFileHandler('/var/log/ansible-module-igw_config.log',
+                                  maxBytes=5242880,
+                                  backupCount=7)
+    log_fmt = logging.Formatter('%(asctime)s %(name)s %(levelname)-8s : '
+                                '%(message)s')
+    handler.setFormatter(log_fmt)
+    logger.addHandler(handler)
+
+    # initialise global variables used by all called modules
+    # e.g. ceph conffile, keyring etc
+    settings.init()
+
+    ansible_main()
diff --git a/library/igw_lun.py b/library/igw_lun.py
new file mode 100644 (file)
index 0000000..6554f3d
--- /dev/null
@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+
+__author__ = 'pcuzner@redhat.com'
+
+DOCUMENTATION = """
+---
+module: igw_lun
+short_description: Manage ceph rbd images to present as iscsi LUNs to clients
+description:
+  - This module calls the 'lun' configuration management module installed
+    on the iscsi gateway node(s). The lun module handles the creation and resize
+    of rbd images, and then maps these rbd devices to the gateway node(s) to be
+    exposed through the kernel's LIO target.
+
+    To support module debugging, this module logs to /var/log/ansible-module-igw_config.log
+    on the target machine(s).
+
+option:
+  pool:
+    description:
+      - The ceph pool where the image should exist or be created in.
+
+        NOTE - The pool *must* exist prior to the Ansible run.
+
+    required: true
+
+  image:
+    description:
+      - this is the rbd image name to create/resize - if the rbd does not exist it
+        is created for you with the settings optimised for exporting over iscsi.
+    required: true
+
+  size:
+    description:
+      - The size of the rbd image to create/resize. The size is numeric suffixed by
+        G or T (GB or TB). Increasing the size of a LUN is supported, but if a size
+        is provided that is smaller that the current size, the request is simply ignored.
+
+        e.g. 100G
+    required: true
+
+  host:
+    description:
+      - the host variable defines the name of the gateway node that will be
+        the allocation host for this rbd image. RBD creation and resize can
+        only be performed by one gateway, the other gateways in the
+        configuration will wait for the operation to complete.
+    required: true
+
+  features:
+    description:
+      - placeholder to potentially allow different rbd features to be set at
+        allocation time by Ansible. NOT CURRENTLY USED
+    required: false
+
+  state:
+    description:
+      - desired state for this LUN - absent or present. For a state='absent'
+      request, the lun module will verify that the rbd image is not allocated to
+      a client. As long as the rbd image is not in use, the LUN definition will be
+      removed from LIO, unmapped from all gateways AND DELETED.
+
+      USE WITH CARE!
+    required: true
+
+requirements: ['ceph-iscsi-config']
+
+author:
+  - 'Paul Cuzner'
+
+"""
+import os
+import logging
+from logging.handlers import RotatingFileHandler
+
+from ansible.module_utils.basic import *
+
+from ceph_iscsi_config.lun import LUN
+from ceph_iscsi_config.utils import valid_size
+import ceph_iscsi_config.settings as settings
+
+# the main function is called ansible_main to allow the call stack
+# to be checked to determine whether the call to the ceph_iscsi_config
+# modules is from ansible or not
+def ansible_main():
+
+    # Define the fields needs to create/map rbd's the the host(s)
+    # NB. features and state are reserved/unused
+    fields = {
+        "pool": {"required": False, "default": "rbd", "type": "str"},
+        "image": {"required": True, "type": "str"},
+        "size": {"required": True, "type": "str"},
+        "host": {"required": True, "type": "str"},
+        "features": {"required": False, "type": "str"},
+        "state": {
+            "required": False,
+            "default": "present",
+            "choices": ['present', 'absent'],
+            "type": "str"
+        },
+    }
+
+    # not supporting check mode currently
+    module = AnsibleModule(argument_spec=fields,
+                           supports_check_mode=False)
+
+    pool = module.params["pool"]
+    image = module.params['image']
+    size = module.params['size']
+    allocating_host = module.params['host']
+    desired_state = module.params['state']
+
+    ################################################
+    # Validate the parameters passed from Ansible  #
+    ################################################
+    if not valid_size(size):
+        logger.critical("image '{}' has an invalid size specification '{}' "
+                        "in the ansible configuration".format(image,
+                                                              size))
+        module.fail_json(msg="(main) Unable to use the size parameter '{}' "
+                             "for image '{}' from the playbook - "
+                             "must be a number suffixed by M,G "
+                             "or T".format(size,
+                                           image))
+
+    # define a lun object and perform some initial parameter validation
+    lun = LUN(logger, pool, image, size, allocating_host)
+    if lun.error:
+        module.fail_json(msg=lun.error_msg)
+
+    logger.info("START - LUN configuration started for {}/{}".format(pool,
+                                                                     image))
+
+    # attempt to create/allocate the LUN for LIO
+    lun.manage(desired_state)
+    if lun.error:
+        module.fail_json(msg=lun.error_msg)
+
+    if lun.num_changes == 0:
+        logger.info("END   - No changes needed")
+    else:
+        logger.info("END   - {} configuration changes "
+                    "made".format(lun.num_changes))
+
+    module.exit_json(changed=(lun.num_changes > 0),
+                     meta={"msg": "Configuration updated"})
+
+
+if __name__ == '__main__':
+
+    module_name = os.path.basename(__file__).replace('ansible_module_', '')
+    logger = logging.getLogger(os.path.basename(module_name))
+    logger.setLevel(logging.DEBUG)
+    handler = RotatingFileHandler('/var/log/ansible-module-igw_config.log',
+                                  maxBytes=5242880,
+                                  backupCount=7)
+    log_fmt = logging.Formatter('%(asctime)s %(name)s %(levelname)-8s : '
+                                '%(message)s')
+    handler.setFormatter(log_fmt)
+    logger.addHandler(handler)
+
+    # initialise global variables used by all called modules
+    # e.g. ceph conffile, keyring etc
+    settings.init()
+
+    ansible_main()
diff --git a/library/igw_purge.py b/library/igw_purge.py
new file mode 100644 (file)
index 0000000..cffc4f4
--- /dev/null
@@ -0,0 +1,212 @@
+#!/usr/bin/env python
+
+DOCUMENTATION = """
+---
+module: igw_purge
+short_description: Provide a purge capability to remove an iSCSI gateway
+environment
+description:
+  - This module handles the removal of a gateway configuration from a ceph
+    environment.
+    The playbook that calls this module prompts the user for the type of purge
+    to perform.
+    The purge options are;
+    all ... purge all LIO configuration *and* delete all defined rbd images
+    lio ... purge only the LIO configuration (rbd's are left intact)
+
+    USE WITH CAUTION
+
+    To support module debugging, this module logs to
+    /var/log/ansible-module-igw_config.log on each target machine(s).
+
+option:
+  mode:
+    description:
+      - the mode defines the type of purge requested
+        gateway ... remove the LIO configuration only
+        disks   ... remove the rbd disks defined to the gateway
+    required: true
+
+requirements: ['ceph-iscsi-config', 'python-rtslib']
+
+author:
+  - 'Paul Cuzner'
+
+"""
+
+import os
+import logging
+import socket
+
+from logging.handlers import RotatingFileHandler
+from ansible.module_utils.basic import *
+
+import ceph_iscsi_config.settings as settings
+from ceph_iscsi_config.common import Config
+from ceph_iscsi_config.lio import LIO, Gateway
+from ceph_iscsi_config.utils import ipv4_addresses, get_ip
+
+__author__ = 'pcuzner@redhat.com'
+
+
+def delete_group(module, image_list, cfg):
+
+    logger.debug("RBD Images to delete are : {}".format(','.join(image_list)))
+    pending_list = list(image_list)
+
+    for rbd_path in image_list:
+        if delete_rbd(module, rbd_path):
+            disk_key = rbd_path.replace('/', '.', 1)
+            cfg.del_item('disks', disk_key)
+            pending_list.remove(rbd_path)
+            cfg.changed = True
+
+    if cfg.changed:
+        cfg.commit()
+
+    return pending_list
+
+
+def delete_rbd(module, rbd_path):
+
+    logger.debug("issuing delete for {}".format(rbd_path))
+    rm_cmd = 'rbd --no-progress rm {}'.format(rbd_path)
+    rc, rm_out, err = module.run_command(rm_cmd, use_unsafe_shell=True)
+    logger.debug("delete RC = {}, {}".format(rc, rm_out, err))
+
+    return True if rc == 0 else False
+
+
+def is_cleanup_host(config):
+    """
+    decide which gateway host should be responsible for any non-specific
+    updates to the config object
+    :param config: configuration dict from the rados pool
+    :return: boolean indicating whether the addition cleanup should be
+    performed by the running host
+    """
+    cleanup = False
+
+    if 'ip_list' in config.config["gateways"]:
+
+        gw_1 = config.config["gateways"]["ip_list"][0]
+
+        usable_ip = get_ip(gw_1)
+        if usable_ip != '0.0.0.0':
+            if usable_ip in ipv4_addresses():
+                cleanup = True
+
+    return cleanup
+
+
+def ansible_main():
+
+    fields = {"mode": {"required": True,
+                       "type": "str",
+                       "choices": ["gateway", "disks"]
+                       }
+              }
+
+    module = AnsibleModule(argument_spec=fields,
+                           supports_check_mode=False)
+
+    run_mode = module.params['mode']
+    changes_made = False
+
+    logger.info("START - GATEWAY configuration PURGE started, run mode "
+                "is {}".format(run_mode))
+    cfg = Config(logger)
+    this_host = socket.gethostname().split('.')[0]
+    perform_cleanup_tasks = is_cleanup_host(cfg)
+
+    #
+    # Purge gateway configuration, if the config has gateways
+    if run_mode == 'gateway' and len(cfg.config['gateways'].keys()) > 0:
+
+        lio = LIO()
+        gateway = Gateway(cfg)
+
+        if gateway.session_count() > 0:
+            module.fail_json(msg="Unable to purge - gateway still has active "
+                                 "sessions")
+
+        gateway.drop_target(this_host)
+        if gateway.error:
+            module.fail_json(msg=gateway.error_msg)
+
+        lio.drop_lun_maps(cfg, perform_cleanup_tasks)
+        if lio.error:
+            module.fail_json(msg=lio.error_msg)
+
+        if gateway.changed or lio.changed:
+
+            # each gateway removes it's own entry from the config
+            cfg.del_item("gateways", this_host)
+
+            if perform_cleanup_tasks:
+                cfg.reset = True
+
+                # drop all client definitions from the configuration object
+                client_names = cfg.config["clients"].keys()
+                for client in client_names:
+                    cfg.del_item("clients", client)
+
+                cfg.del_item("gateways", "iqn")
+                cfg.del_item("gateways", "created")
+                cfg.del_item("gateways", "ip_list")
+
+            cfg.commit()
+
+            changes_made = True
+
+    elif run_mode == 'disks' and len(cfg.config['disks'].keys()) > 0:
+        #
+        # Remove the disks on this host, that have been registered in the
+        # config object
+        #
+        # if the owner field for a disk is set to this host, this host can
+        # safely delete it
+        # nb. owner gets set at rbd allocation and mapping time
+        images_left = []
+
+        # delete_list will contain a list of pool/image names where the owner
+        # is this host
+        delete_list = [key.replace('.', '/', 1) for key in cfg.config['disks']
+                       if cfg.config['disks'][key]['owner'] == this_host]
+
+        if delete_list:
+            images_left = delete_group(module, delete_list, cfg)
+
+        # if the delete list still has entries we had problems deleting the
+        # images
+        if images_left:
+            module.fail_json(msg="Problems deleting the following rbd's : "
+                                 "{}".format(','.join(images_left)))
+
+        changes_made = cfg.changed
+
+        logger.debug("ending lock state variable {}".format(cfg.config_locked))
+
+    logger.info("END   - GATEWAY configuration PURGE complete")
+
+    module.exit_json(changed=changes_made,
+                     meta={"msg": "Purge of iSCSI settings ({}) "
+                                  "complete".format(run_mode)})
+
+
+if __name__ == '__main__':
+
+    module_name = os.path.basename(__file__).replace('ansible_module_', '')
+    logger = logging.getLogger(os.path.basename(module_name))
+    logger.setLevel(logging.DEBUG)
+    handler = RotatingFileHandler('/var/log/ansible-module-igw_config.log',
+                                  maxBytes=5242880,
+                                  backupCount=7)
+    log_fmt = logging.Formatter('%(asctime)s %(name)s %(levelname)-8s : '
+                                '%(message)s')
+    handler.setFormatter(log_fmt)
+    logger.addHandler(handler)
+
+    settings.init()
+
+    ansible_main()
index ecc180e3025b7ae581ca4fd2846d06f134553f91..9d909a6fdc2bc147aef5a8385cb6f54d7e9e786c 100644 (file)
   fail:
     msg: "Systemd must be present"
   when: ansible_service_mgr != 'systemd'
+
+- name: fail on unsupported distribution for iscsi gateways
+  fail:
+    msg: "iSCSI gateways can only be deployed on Red Hat Enterprise Linux or CentOS"
+  when:
+    - ansible_distribution not in  ['Red Hat Enterprise Linux', 'CentOS']
+    - iscsi_gw_group_name in group_names
+
+- name: fail on unsupported distribution version for iscsi gateways
+  fail:
+    msg: "iSCSI gateways can only be deployed on Red Hat Enterprise Linux or CentOS >= 7.4"
+  when:
+    - ansible_distribution == 'Red Hat Enterprise Linux' or ansible_distribution == 'CentOS'
+    - ansible_distribution_version < '7.4'
+    - iscsi_gw_group_name in group_names
index 0688b3db404e7b6d5e5bbb0dc254e9a60e4c2b59..9d7c6c0c6fad174563598bae790c4410f672b5d9 100644 (file)
   when:
     - mgr_group_name in group_names
     - ceph_release_num.{{ ceph_release }} > ceph_release_num.jewel
+
+- name: install redhat ceph iscsi package
+  package:
+    name: "{{ item }}"
+    state: "{{ (upgrade_ceph_packages|bool) | ternary('latest','present') }}"
+  with_items:
+    - tcmu-runner
+    - ceph-iscsi-config
+    - targetcli
+  when:
+    - iscsi_gw_group_name in group_names
+    - ceph_release_num.{{ ceph_release }} >= ceph_release_num.luminous
index 2928dc0d027ddb404b33235b50c0c4e3d520a8cc..8d261e5dd4944f897ee89ba44dffa3ea7dd82214 100644 (file)
@@ -21,7 +21,7 @@
     state: present
     gpgkey: "{{ ceph_stable_key }}"
     baseurl: "{{ ceph_mirror }}/nfs-ganesha/rpm-{{ nfs_ganesha_stable_branch }}/{{ ceph_stable_release }}/$basearch"
-  when: 
+  when:
     - nfs_group_name in group_names
     - nfs_ganesha_stable
 
index 989a604ac2d4c87fe91b5662668f3b145d7685d6..7aa053715d03bb854322b70c3a48fa4ffcfa1cb9 100644 (file)
     - nfs_group_name in group_names
     - nfs_ganesha_dev
 
+- name: fetch ceph-iscsi-config red hat development repository
+  uri:
+    url: https://shaman.ceph.com/api/repos/ceph-iscsi-config/{{ ceph_dev_branch }}/{{ ceph_dev_sha1 }}/{{ ansible_distribution | lower }}/{{ ansible_distribution_major_version }}/repo
+    return_content: yes
+  register: ceph_iscsi_config_dev_yum_repo
+  when:
+    - ceph_iscsi_config_dev
+    - iscsi_gw_group_name in group_names
+
+- name: configure ceph-iscsi-config red hat development repository
+  copy:
+    content: "{{ ceph_iscsi_config_dev_yum_repo.content }}"
+    dest: /etc/yum.repos.d/ceph-iscsi-config-dev.repo
+    owner: root
+    group: root
+    backup: yes
+  when:
+    - ceph_iscsi_config_dev
+    - iscsi_gw_group_name in group_names
index 6d973677b4d1f18130e8ba7a52e75e2ab45dea93..13c50256830e841e08581d3c7c618643b520255f 100644 (file)
@@ -43,7 +43,7 @@ nfs_group_name: nfss
 restapi_group_name: restapis
 rbdmirror_group_name: rbdmirrors
 client_group_name: clients
-iscsi_group_name: iscsigws
+iscsi_gw_group_name: iscsi_gws
 mgr_group_name: mgrs
 
 # If check_firewall is true, then ansible will try to determine if the
@@ -197,6 +197,8 @@ nfs_ganesha_dev: false # use development repos for nfs-ganesha
 # flavors so far include: ceph_master, ceph_jewel, ceph_kraken, ceph_luminous
 nfs_ganesha_flavor: "ceph_master"
 
+ceph_iscsi_config_dev: true # special repo for deploying iSCSI gateways
+
 
 # REPOSITORY: CUSTOM
 #
diff --git a/roles/ceph-iscsi-gw/LICENSE b/roles/ceph-iscsi-gw/LICENSE
new file mode 100644 (file)
index 0000000..ff84f66
--- /dev/null
@@ -0,0 +1,13 @@
+Copyright 2016 Paul Cuzner pcuzner at redhat dot com
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/roles/ceph-iscsi-gw/README b/roles/ceph-iscsi-gw/README
new file mode 100644 (file)
index 0000000..e55a124
--- /dev/null
@@ -0,0 +1,7 @@
+This package installs the playbooks/tasks necessary to configure LIO gateways that provide a front-end to a ceph cluster.
+
+The playbooks use custom modules that are just wrappers calling python modules installed on the gateways nodes. These
+python modules handle the configuration interaction for;
+- the iscsi target definition (target iqn, target portal groups and portal IP's)
+- LUN's including RBD create and resize
+- client definition
diff --git a/roles/ceph-iscsi-gw/README.md b/roles/ceph-iscsi-gw/README.md
new file mode 100644 (file)
index 0000000..3ba0e31
--- /dev/null
@@ -0,0 +1,70 @@
+# ceph-iscsi-ansible
+This project provides a mechanism to deploy iSCSI gateways in front of a ceph cluster using Ansible. The ansible playbooks  
+provided rely upon configuration logic from the "ceph-iscsi-config" project. This separation provides independence to the   
+configuration logic, potentially opening up the possibility for puppet/chef to create/manage ceph/iSCSI gateways in the  
+same way.  
+
+## Introduction
+At a high level, this project provides custom modules which are responsible for calling the configuration logic, together  
+with the relevant playbooks. The project defines a new ceph-ansible role; ceph-iscsi-gw, together with two playbooks;  
+
+- **ceph-iscsi-gw-yml** ... to define our change the gateway configuration based on group_vars/ceph-iscsi-gw.yml  
+- **purge_gateways.yml** .. to destroy the LIO configuration, or the LIO and any associated rbd images.
+
+## Features    
+The combination of the playbooks and the configuration logic deliver the following features;  
+
+- confirms RHEL7.3 and aborts if necessary  
+- ensures targetcli/device-mapper-multipath is installed (for rtslib support)  
+- configures multipath.conf  
+- creates rbd's if needed - at allocation time, each rbd is assigned an owner, which will become the preferred path    
+- checks the size of the rbds at run time and expands if necessary  
+- maps the rbd's to the host (gateway)  
+- enables the rbdmap service to start on boot, and reconfigures the target service to be dependent on rbdmap  
+- adds the rbd's to the /etc/ceph/rbdmap file ensuring the devices are automatically mapped following a gateway reboot  
+- maps these rbds to LIO  
+- once mapped, the alua preferred path state is set or cleared (supporting an active/passive topology)    
+- creates an iscsi target - common iqn, and multiple tpg's  
+- adds a portal ip based on a the provided IP addresses defined in the group vars to each tpg  
+- enables the local tpg, other gateways are defined as disabled  
+- adds all the mapped luns to ALL tpg's (ready for client assignment)  
+- add clients to the active/enabled tpg, with/without CHAP  
+- images mapped to clients can be added/removed by changing image_list and rerunning the playbook  
+- clients can be removed using the state=absent variable and rerunning the playbook. At this point the entry can be  
+  removed from the group variables file
+- configuration can be wiped with the purge_gateway playbook  
+- current state can be seen by looking at the configuration object (stored in the rbd pool)  
+
+### Why RHEL 7.3?
+There are several system dependencies that are required to ensure the correct (i.e. don't eat my data!) behaviors when OSD connectivity  
+or gateway nodes fail. RHEL 7.3 delivers the necessary kernel changes, and also provides an updated multipathd, enabling rbd images  
+to be managed by multipathd.
+
+## Prerequisites  
+* a working ceph cluster ( *rbd pool defined* )
+* a server/host with ceph-ansible installed and working
+* nodes intended to be gateways should be at least ceph clients, with the ability to create and map rbd images  
+  
+
+## Testing So far
+The solution has been tested on a collocated cluster where the osd/mons and gateways all reside on the same node.  
+
+## Quick Start
+### Prepare the iSCSI Gateway Nodes  
+1. install the ceph-iscsi-config package on the nodes, intended to be gateways. NB. The playbook includes a check for the presence  
+ of this rpm (https://github.com/pcuzner/ceph-iscsi-config)
+
+### Install the ansible playbooks
+1. install the ceph-iscsi-ansible rpm from the packages directory on the node where you already have ceph-ansible installed.  
+2. update /etc/ansible/hosts to include a host group (ceph-iscsi-gw) for the nodes that you want to become iscsi gateways  
+3. make a copy of the group_vars/ceph-iscsi-gw.sample file called ceph-iscsi-gw, and update it to define the environment you want  
+4. run the playbook  
+  ```> ansible-playbook ceph-iscsi-gw.yml```
+  
+## Purging the configuration
+As mentioned above, the project provides a purge-gateways.yml playbook which can remove the LIO configuration alone, or remove   
+both LIO and all associated rbd images that have been declared in the group_vars/ceph-iscsi-gw file. The purge playbook will  
+check for any active iscsi sessions, and abort if any are found.
+    
+## Known Issues and Considerations  
+1. the ceph cluster name is the default 'ceph', so the corresponding configuration file /etc/ceph/ceph.conf is assumed to be valid
\ No newline at end of file
diff --git a/roles/ceph-iscsi-gw/ceph-iscsi-ansible.spec b/roles/ceph-iscsi-gw/ceph-iscsi-ansible.spec
new file mode 100644 (file)
index 0000000..c33d018
--- /dev/null
@@ -0,0 +1,109 @@
+Name:           ceph-iscsi-ansible
+Version:        2.0
+Release:        1%{?dist}
+Summary:        Ansible playbooks for deploying LIO iscsi gateways in front of a Ceph cluster
+License:        ASL 2.0
+URL:            https://github.com/pcuzner/ceph-iscsi-ansible
+Source0:        https://github.com/pcuzner/ceph-iscsi-ansible/archive/%{version}/%{name}-%{version}.tar.gz
+BuildArch:      noarch
+
+Requires: ansible >= 1.9
+Requires: ceph-ansible >= 1.0.5
+
+%description
+Ansible playbooks that define nodes as iSCSI gateways (LIO). Once complete, the
+LIO instance on each node provides an ISCSI endpoint for clients to connect to.
+The playbook defines the front-end iSCSI environment (target -> tpgN ->
+NodeACLS/client), as well as the underlying rbd definition for the rbd images
+to be exported over iSCSI.
+
+ceph-iscsi-gw.yml ......... defines the LIO configuration(defined by
+                            group_vars/ceph-iscsi-gw.yml)
+purge-iscsi-gateways.yml .. deletes the LIO configuration, and optionally rbd's
+                            from the environment
+
+NB: The playbooks are dependent upon the ceph-iscsi-config package being
+installed/available to the hosts that will become iSCSI gateways.
+
+%prep
+%setup -q
+
+%build
+
+%install
+mkdir -p %{buildroot}%{_datarootdir}/ceph-ansible
+
+for f in group_vars library roles ceph-iscsi-gw.yml purge-iscsi-gateways.yml; do
+  cp -a $f %{buildroot}%{_datarootdir}/ceph-ansible
+done
+
+%files
+%doc LICENSE
+%doc README
+%{_datarootdir}/ceph-ansible/ceph-iscsi-gw.yml
+%{_datarootdir}/ceph-ansible/purge-iscsi-gateways.yml
+%{_datarootdir}/ceph-ansible/group_vars/ceph-iscsi-gw.sample
+%{_datarootdir}/ceph-ansible/roles/ceph-iscsi-gw
+%{_datarootdir}/ceph-ansible/library/igw*
+%exclude %{_datarootdir}/ceph-ansible/library/igw*.pyo
+%exclude %{_datarootdir}/ceph-ansible/library/igw*.pyc
+
+%changelog
+* Fri Jan 13 2017 Paul Cuzner <pcuzner@redhat.com> - 2.0-1
+- converted from device-mapper/krbd to TCMU based rbd configurations
+- renamed iscsi-gateway config file to use .cfg extension
+- renamed purge playbook to match naming in ceph-ansible
+
+* Fri Nov 04 2016 Paul Cuzner <pcuzner@redhat.com> - 1.5-1
+- playbook now seeds the configuration directory on ansible host (rhbz 1390026)
+- resolve a 1.4 regression affecting the igw_purge module
+- fail gracefully if bogus client name is given (rhbz 1390023)
+
+* Thu Oct 27 2016 Paul Cuzner <pcuzner@redhat.com> - 1.4-1
+- clients can now be added without images or chap defined using null strings
+- changed parameters for client definition to position for other auth mechanisms
+- adapt purge module to use revised disk naming scheme within config object
+- updated group_vars sample to reflect name changes
+- added state= setting to LUN definitions (enabling disks to be add/removed)
+- fix syntax issue during ceph.conf seed process
+- documentation added to the Ansible modules
+
+* Fri Oct 21 2016 Paul Cuzner <pcuzner@redhat.com> - 1.3-1
+- removed rsync rpm dependency (BZ 1386090)
+- ceph.conf pulled from seed monitor, then pushed to gateways using copy task
+- add a template based config file to each gateway for runtime info
+- add additional variables allowing non-default ceph cluster names/keyrings (BZ 1386617)
+
+* Sat Oct 15 2016 Paul Cuzner <pcuzner@redhat.com> - 1.2-1
+- documented the passwordless ssh requirement for the seed_monitor node
+- fix BZ 1384505 mask the target service preventing manual start/stop
+- fix BZ 1384858 when the admin updates ansible hosts but not the gateway_ip_list variable
+
+* Wed Oct 12 2016 Paul Cuzner <pcuzner@redhat.com> - 1.1-1
+- updated playbook to modify lvm.conf to exclude the dm devices created for mapped rbds
+
+* Mon Oct 10 2016 Paul Cuzner <pcuzner@redhat.com> - 1.0-1
+- fix : allow client_connections and rbd_devices to be be empty to skip those steps
+- add usage guidelines to the group_vars/ceph-iscsi-gw.sample file
+- added variable to allow pre-req checks to be bypassed during a run
+- updated list of rpm pre-req that ansible checks for
+- add synchronize task to the playbook to copy admin keyring to gateway node(s)
+- updated igw_purge module to allow for the deletion of the alua port groups
+
+* Thu Oct 06 2016 Paul Cuzner <pcuzner@redhat.com> - 0.8-1
+- fix : purge_gateways.yml was missing
+- removed packages directory to clean up the source archive
+- spec file updates (dependencies)
+
+* Wed Oct 05 2016 Paul Cuzner <pcuzner@redhat.com> - 0.7-1
+- removed service dependencies for rbdmap/target (replaced by rbd-target-gw form ceph-iscsi-config rpm)
+- removed target overrides files
+- updated playbook to add skip_partx yes to multipath.conf
+
+* Mon Oct 03 2016 Paul Cuzner <pcuzner@redhat.com> - 0.6-1
+- changed the main function to have an ansible prefix to allow the code to know where it is invoked from
+- updated the purge module to support image names being prefixed by a pool i.e. pool/image
+
+* Tue Sep 27 2016 Paul Cuzner <pcuzner@redhat.com> - 0.5-1
+- initial rpm package
+
index ed97d539c095cf1413af30cc23dea272095b97dd..a67d864dd46060e2e481c6349dfc426bd66d2f63 100644 (file)
@@ -1 +1,42 @@
 ---
+# You can override vars by using host or group vars
+
+# Specify the iqn for ALL gateways. This iqn is shared across the gateways, so an iscsi
+# client sees the gateway group as a single storage subsystem.
+gateway_iqn: "iqn.2003-01.com.redhat.iscsi-gw:ceph-igw"
+
+# gateway_ip_list provides a list of the IP Addrresses - one per gateway - that will be used
+# as an iscsi target portal ip. The list must be comma separated - and the order determines
+# the sequence of TPG's within the iscsi target across each gateway. Once set, additional
+# gateways can be added, but the order must *not* be changed.
+gateway_ip_list: "192.168.122.101,192.168.122.102,192.168.122.103"
+
+# rbd_devices defines the images that should be created and exported from the iscsi gateways.
+# If the rbd does not exist, it will be created for you. In addition you may increase the
+# size of rbd's by changing the size parameter and rerunning the playbook. A size value lower
+# than the current size of the rbd is ignored.
+#
+# the 'host' parameter defines which of the gateway nodes should handle the physical
+# allocation/expansion or removal of the rbd
+# to remove an image, simply use a state of 'absent'. This will first check the rbd is not allocated
+# to any client, and the remove it from LIO and then delete the rbd image
+#
+# NB. this variable definition can be commented out to bypass LUN management
+rbd_devices:
+  - { pool: 'rbd', image: 'ansible1', size: '30G', host: 'ceph-1', state: 'present' }
+  - { pool: 'rbd', image: 'ansible2', size: '15G', host: 'ceph-1', state: 'present' }
+  - { pool: 'rbd', image: 'ansible3', size: '30G', host: 'ceph-1', state: 'present' }
+  - { pool: 'rbd', image: 'ansible4', size: '50G', host: 'ceph-1', state: 'present' }
+
+
+# client_connections defines the client ACL's to restrict client access to specific LUNs
+# The settings are as follows;
+# - image_list is a comma separated list of rbd images of the form <pool name>.<rbd_image_name>
+# - chap supplies the user and password the client will use for authentication of the
+#   form <user>/<password>
+# - status shows the intended state of this client definition - 'present' or 'absent'
+#
+# NB. this definition can be commented out to skip client (nodeACL) management
+client_connections:
+  - { client: 'iqn.1994-05.com.redhat:rh7-iscsi-client', image_list: 'rbd.ansible1,rbd.ansible2', chap: 'rh7-iscsi-client/redhat', status: 'present' }
+  - { client: 'iqn.1991-05.com.microsoft:w2k12r2', image_list: 'rbd.ansible4', chap: 'w2k12r2/microsoft_w2k12', status: 'absent' }
diff --git a/roles/ceph-iscsi-gw/library/igw_client.py b/roles/ceph-iscsi-gw/library/igw_client.py
new file mode 100644 (file)
index 0000000..cdd095b
--- /dev/null
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+
+__author__ = 'pcuzner@redhat.com'
+
+DOCUMENTATION = """
+---
+module: igw_client
+short_description: Manage iscsi gateway client definitions
+description:
+  - This module calls the 'client' configuration management module installed
+    on the iscsi gateway node to handle the definition of iscsi clients on the
+    gateway(s). This definition will setup iscsi authentication (e.g. chap),
+    and mask the required rbd images to the client.
+
+    The 'client' configuration module is provided by ceph-iscsi-config
+    rpm which is installed on the gateway nodes.
+
+    To support module debugging, this module logs to
+    /var/log/ansible-module-igw_config.log on the target machine(s).
+
+option:
+  client_iqn:
+    description:
+      - iqn of the client machine which should be connected or removed from the
+        iscsi gateway environment
+    required: true
+
+  image_list:
+    description:
+      - comma separated string providing the rbd images that this
+        client definition should have. The rbd images provided must use the
+        following format <pool_name>.<rbd_image_name>
+        e.g. rbd.disk1,rbd.disk2
+    required: true
+
+  chap:
+    description:
+      - chap credentials for the client to authenticate to the gateways
+        to gain access to the exported rbds (LUNs). The credentials is a string
+        value of the form 'username/password'. The iscsi client must then use
+        these settings to gain access to any LUN resources.
+    required: true
+
+  state:
+    description:
+      - desired state for this client - absent or present
+    required: true
+
+requirements: ['ceph-iscsi-config']
+
+author:
+  - 'Paul Cuzner'
+
+"""
+
+import os
+import logging
+from logging.handlers import RotatingFileHandler
+from ansible.module_utils.basic import *
+
+from ceph_iscsi_config.client import GWClient
+import ceph_iscsi_config.settings as settings
+
+# the main function is called ansible_main to allow the call stack
+# to be checked to determine whether the call to the ceph_iscsi_config
+# modules is from ansible or not
+def ansible_main():
+
+    fields = {
+        "client_iqn": {"required": True, "type": "str"},
+        "image_list": {"required": True, "type": "str"},
+        "chap": {"required": True, "type": "str"},
+        "state": {
+            "required": True,
+            "choices": ['present', 'absent'],
+            "type": "str"
+            },
+        }
+
+    module = AnsibleModule(argument_spec=fields,
+                           supports_check_mode=False)
+
+    client_iqn = module.params['client_iqn']
+
+    if module.params['image_list']:
+        image_list = module.params['image_list'].split(',')
+    else:
+        image_list = []
+
+    chap = module.params['chap']
+    desired_state = module.params['state']
+
+    logger.info("START - Client configuration started : {}".format(client_iqn))
+
+    # The client is defined using the GWClient class. This class handles
+    # client attribute updates, rados configuration object updates and LIO
+    # settings. Since the logic is external to this custom module, clients
+    # can be created/deleted by other methods in the same manner.
+    client = GWClient(logger, client_iqn, image_list, chap)
+    if client.error:
+        module.fail_json(msg=client.error_msg)
+
+    client.manage(desired_state)
+    if client.error:
+        module.fail_json(msg=client.error_msg)
+
+    logger.info("END   - Client configuration complete - {} "
+                "changes made".format(client.change_count))
+
+    changes_made = True if client.change_count > 0 else False
+
+    module.exit_json(changed=changes_made,
+                     meta={"msg": "Client definition completed {} "
+                                  "changes made".format(client.change_count)})
+
+if __name__ == '__main__':
+
+    module_name = os.path.basename(__file__).replace('ansible_module_', '')
+    logger = logging.getLogger(os.path.basename(module_name))
+    logger.setLevel(logging.DEBUG)
+    handler = RotatingFileHandler('/var/log/ansible-module-igw_config.log',
+                                  maxBytes=5242880,
+                                  backupCount=7)
+    log_fmt = logging.Formatter('%(asctime)s %(name)s %(levelname)-8s : '
+                                '%(message)s')
+    handler.setFormatter(log_fmt)
+    logger.addHandler(handler)
+
+    # initialise global variables used by all called modules
+    # e.g. ceph conffile, keyring etc
+    settings.init()
+
+    ansible_main()
diff --git a/roles/ceph-iscsi-gw/library/igw_gateway.py b/roles/ceph-iscsi-gw/library/igw_gateway.py
new file mode 100644 (file)
index 0000000..9c3a1d0
--- /dev/null
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+__author__ = 'pcuzner@redhat.com'
+
+
+DOCUMENTATION = """
+---
+module: igw_gateway
+short_description: Manage the iscsi gateway definition
+description:
+  - This module calls the 'gateway' configuration management module installed
+    on the iscsi gateway node(s) to handle the definition of iscsi gateways.
+    The module will configure;
+    * the iscsi target and target portal group (TPG)
+    * rbd maps to the gateway and registration of those rbds as LUNs to the
+      kernels LIO subsystem
+
+    The actual configuration modules are provided by ceph-iscsi-config rpm
+    which is installed on the gateway nodes.
+
+    To support module debugging, this module logs to
+    /var/log/ansible-module-igw_config.log on the target machine(s).
+
+option:
+  gateway_iqn:
+    description:
+      - iqn that all gateway nodes will use to present a common system image
+        name to iscsi clients
+    required: true
+
+  gateway_ip_list:
+    description:
+      - comma separated string providing the IP addresses that will be used
+        as iSCSI portal IPs to accept iscsi client connections. Each IP address
+        should equate to an IP on a gateway node - typically dedicated to iscsi
+        traffic. The order of the IP addresses determines the TPG sequence
+        within the target definition - so once defined, new gateways can be
+        added but *must* be added to the end of this list to preserve the tpg
+        sequence
+
+        e.g. 192.168.122.101,192.168.122.103
+    required: true
+
+  mode:
+    description:
+      - mode in which to run the gateway module. Two modes are supported
+        target ... define the iscsi target iqn, tpg's and portals
+        map ...... map luns to the tpg's, and also define the ALUA path setting
+                   for each LUN (activeOptimized/activenonoptimized)
+    required: true
+
+
+requirements: ['ceph-iscsi-config']
+
+author:
+  - 'Paul Cuzner'
+
+"""
+
+import os
+import logging
+
+from logging.handlers import RotatingFileHandler
+from ansible.module_utils.basic import *
+
+import ceph_iscsi_config.settings as settings
+
+from ceph_iscsi_config.gateway import GWTarget
+from ceph_iscsi_config.utils import valid_ip
+
+
+# the main function is called ansible_main to allow the call stack
+# to be checked to determine whether the call to the ceph_iscsi_config
+# modules is from ansible or not
+def ansible_main():
+    # Configures the gateway on the host. All images defined are added to
+    # the default tpg for later allocation to clients
+    fields = {"gateway_iqn": {"required": True, "type": "str"},
+              "gateway_ip_list": {"required": True},    # "type": "list"},
+              "mode": {
+                  "required": True,
+                  "choices": ['target', 'map']
+                  }
+              }
+
+    module = AnsibleModule(argument_spec=fields,
+                           supports_check_mode=False)
+
+    gateway_iqn = module.params['gateway_iqn']
+    gateway_ip_list = module.params['gateway_ip_list'].split(',')
+    mode = module.params['mode']
+
+    if not valid_ip(gateway_ip_list):
+        module.fail_json(msg="Invalid gateway IP address(es) provided - port "
+                             "22 check failed ({})".format(gateway_ip_list))
+
+    logger.info("START - GATEWAY configuration started - mode {}".format(mode))
+
+    gateway = GWTarget(logger, gateway_iqn, gateway_ip_list)
+    if gateway.error:
+        logger.critical("(ansible_main) Gateway init failed - "
+                        "{}".format(gateway.error_msg))
+        module.fail_json(msg="iSCSI gateway initialisation failed "
+                             "({})".format(gateway.error_msg))
+
+    gateway.manage(mode)
+
+    if gateway.error:
+        logger.critical("(main) Gateway creation or load failed, "
+                        "unable to continue")
+        module.fail_json(msg="iSCSI gateway creation/load failure "
+                             "({})".format(gateway.error_msg))
+
+
+    logger.info("END - GATEWAY configuration complete")
+    module.exit_json(changed=gateway.changes_made,
+                     meta={"msg": "Gateway setup complete"})
+
+
+if __name__ == '__main__':
+
+    module_name = os.path.basename(__file__).replace('ansible_module_', '')
+    logger = logging.getLogger(os.path.basename(module_name))
+    logger.setLevel(logging.DEBUG)
+    handler = RotatingFileHandler('/var/log/ansible-module-igw_config.log',
+                                  maxBytes=5242880,
+                                  backupCount=7)
+    log_fmt = logging.Formatter('%(asctime)s %(name)s %(levelname)-8s : '
+                                '%(message)s')
+    handler.setFormatter(log_fmt)
+    logger.addHandler(handler)
+
+    # initialise global variables used by all called modules
+    # e.g. ceph conffile, keyring etc
+    settings.init()
+
+    ansible_main()
diff --git a/roles/ceph-iscsi-gw/library/igw_lun.py b/roles/ceph-iscsi-gw/library/igw_lun.py
new file mode 100644 (file)
index 0000000..6554f3d
--- /dev/null
@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+
+__author__ = 'pcuzner@redhat.com'
+
+DOCUMENTATION = """
+---
+module: igw_lun
+short_description: Manage ceph rbd images to present as iscsi LUNs to clients
+description:
+  - This module calls the 'lun' configuration management module installed
+    on the iscsi gateway node(s). The lun module handles the creation and resize
+    of rbd images, and then maps these rbd devices to the gateway node(s) to be
+    exposed through the kernel's LIO target.
+
+    To support module debugging, this module logs to /var/log/ansible-module-igw_config.log
+    on the target machine(s).
+
+option:
+  pool:
+    description:
+      - The ceph pool where the image should exist or be created in.
+
+        NOTE - The pool *must* exist prior to the Ansible run.
+
+    required: true
+
+  image:
+    description:
+      - this is the rbd image name to create/resize - if the rbd does not exist it
+        is created for you with the settings optimised for exporting over iscsi.
+    required: true
+
+  size:
+    description:
+      - The size of the rbd image to create/resize. The size is numeric suffixed by
+        G or T (GB or TB). Increasing the size of a LUN is supported, but if a size
+        is provided that is smaller that the current size, the request is simply ignored.
+
+        e.g. 100G
+    required: true
+
+  host:
+    description:
+      - the host variable defines the name of the gateway node that will be
+        the allocation host for this rbd image. RBD creation and resize can
+        only be performed by one gateway, the other gateways in the
+        configuration will wait for the operation to complete.
+    required: true
+
+  features:
+    description:
+      - placeholder to potentially allow different rbd features to be set at
+        allocation time by Ansible. NOT CURRENTLY USED
+    required: false
+
+  state:
+    description:
+      - desired state for this LUN - absent or present. For a state='absent'
+      request, the lun module will verify that the rbd image is not allocated to
+      a client. As long as the rbd image is not in use, the LUN definition will be
+      removed from LIO, unmapped from all gateways AND DELETED.
+
+      USE WITH CARE!
+    required: true
+
+requirements: ['ceph-iscsi-config']
+
+author:
+  - 'Paul Cuzner'
+
+"""
+import os
+import logging
+from logging.handlers import RotatingFileHandler
+
+from ansible.module_utils.basic import *
+
+from ceph_iscsi_config.lun import LUN
+from ceph_iscsi_config.utils import valid_size
+import ceph_iscsi_config.settings as settings
+
+# the main function is called ansible_main to allow the call stack
+# to be checked to determine whether the call to the ceph_iscsi_config
+# modules is from ansible or not
+def ansible_main():
+
+    # Define the fields needs to create/map rbd's the the host(s)
+    # NB. features and state are reserved/unused
+    fields = {
+        "pool": {"required": False, "default": "rbd", "type": "str"},
+        "image": {"required": True, "type": "str"},
+        "size": {"required": True, "type": "str"},
+        "host": {"required": True, "type": "str"},
+        "features": {"required": False, "type": "str"},
+        "state": {
+            "required": False,
+            "default": "present",
+            "choices": ['present', 'absent'],
+            "type": "str"
+        },
+    }
+
+    # not supporting check mode currently
+    module = AnsibleModule(argument_spec=fields,
+                           supports_check_mode=False)
+
+    pool = module.params["pool"]
+    image = module.params['image']
+    size = module.params['size']
+    allocating_host = module.params['host']
+    desired_state = module.params['state']
+
+    ################################################
+    # Validate the parameters passed from Ansible  #
+    ################################################
+    if not valid_size(size):
+        logger.critical("image '{}' has an invalid size specification '{}' "
+                        "in the ansible configuration".format(image,
+                                                              size))
+        module.fail_json(msg="(main) Unable to use the size parameter '{}' "
+                             "for image '{}' from the playbook - "
+                             "must be a number suffixed by M,G "
+                             "or T".format(size,
+                                           image))
+
+    # define a lun object and perform some initial parameter validation
+    lun = LUN(logger, pool, image, size, allocating_host)
+    if lun.error:
+        module.fail_json(msg=lun.error_msg)
+
+    logger.info("START - LUN configuration started for {}/{}".format(pool,
+                                                                     image))
+
+    # attempt to create/allocate the LUN for LIO
+    lun.manage(desired_state)
+    if lun.error:
+        module.fail_json(msg=lun.error_msg)
+
+    if lun.num_changes == 0:
+        logger.info("END   - No changes needed")
+    else:
+        logger.info("END   - {} configuration changes "
+                    "made".format(lun.num_changes))
+
+    module.exit_json(changed=(lun.num_changes > 0),
+                     meta={"msg": "Configuration updated"})
+
+
+if __name__ == '__main__':
+
+    module_name = os.path.basename(__file__).replace('ansible_module_', '')
+    logger = logging.getLogger(os.path.basename(module_name))
+    logger.setLevel(logging.DEBUG)
+    handler = RotatingFileHandler('/var/log/ansible-module-igw_config.log',
+                                  maxBytes=5242880,
+                                  backupCount=7)
+    log_fmt = logging.Formatter('%(asctime)s %(name)s %(levelname)-8s : '
+                                '%(message)s')
+    handler.setFormatter(log_fmt)
+    logger.addHandler(handler)
+
+    # initialise global variables used by all called modules
+    # e.g. ceph conffile, keyring etc
+    settings.init()
+
+    ansible_main()
diff --git a/roles/ceph-iscsi-gw/library/igw_purge.py b/roles/ceph-iscsi-gw/library/igw_purge.py
new file mode 100644 (file)
index 0000000..cffc4f4
--- /dev/null
@@ -0,0 +1,212 @@
+#!/usr/bin/env python
+
+DOCUMENTATION = """
+---
+module: igw_purge
+short_description: Provide a purge capability to remove an iSCSI gateway
+environment
+description:
+  - This module handles the removal of a gateway configuration from a ceph
+    environment.
+    The playbook that calls this module prompts the user for the type of purge
+    to perform.
+    The purge options are;
+    all ... purge all LIO configuration *and* delete all defined rbd images
+    lio ... purge only the LIO configuration (rbd's are left intact)
+
+    USE WITH CAUTION
+
+    To support module debugging, this module logs to
+    /var/log/ansible-module-igw_config.log on each target machine(s).
+
+option:
+  mode:
+    description:
+      - the mode defines the type of purge requested
+        gateway ... remove the LIO configuration only
+        disks   ... remove the rbd disks defined to the gateway
+    required: true
+
+requirements: ['ceph-iscsi-config', 'python-rtslib']
+
+author:
+  - 'Paul Cuzner'
+
+"""
+
+import os
+import logging
+import socket
+
+from logging.handlers import RotatingFileHandler
+from ansible.module_utils.basic import *
+
+import ceph_iscsi_config.settings as settings
+from ceph_iscsi_config.common import Config
+from ceph_iscsi_config.lio import LIO, Gateway
+from ceph_iscsi_config.utils import ipv4_addresses, get_ip
+
+__author__ = 'pcuzner@redhat.com'
+
+
+def delete_group(module, image_list, cfg):
+
+    logger.debug("RBD Images to delete are : {}".format(','.join(image_list)))
+    pending_list = list(image_list)
+
+    for rbd_path in image_list:
+        if delete_rbd(module, rbd_path):
+            disk_key = rbd_path.replace('/', '.', 1)
+            cfg.del_item('disks', disk_key)
+            pending_list.remove(rbd_path)
+            cfg.changed = True
+
+    if cfg.changed:
+        cfg.commit()
+
+    return pending_list
+
+
+def delete_rbd(module, rbd_path):
+
+    logger.debug("issuing delete for {}".format(rbd_path))
+    rm_cmd = 'rbd --no-progress rm {}'.format(rbd_path)
+    rc, rm_out, err = module.run_command(rm_cmd, use_unsafe_shell=True)
+    logger.debug("delete RC = {}, {}".format(rc, rm_out, err))
+
+    return True if rc == 0 else False
+
+
+def is_cleanup_host(config):
+    """
+    decide which gateway host should be responsible for any non-specific
+    updates to the config object
+    :param config: configuration dict from the rados pool
+    :return: boolean indicating whether the addition cleanup should be
+    performed by the running host
+    """
+    cleanup = False
+
+    if 'ip_list' in config.config["gateways"]:
+
+        gw_1 = config.config["gateways"]["ip_list"][0]
+
+        usable_ip = get_ip(gw_1)
+        if usable_ip != '0.0.0.0':
+            if usable_ip in ipv4_addresses():
+                cleanup = True
+
+    return cleanup
+
+
+def ansible_main():
+
+    fields = {"mode": {"required": True,
+                       "type": "str",
+                       "choices": ["gateway", "disks"]
+                       }
+              }
+
+    module = AnsibleModule(argument_spec=fields,
+                           supports_check_mode=False)
+
+    run_mode = module.params['mode']
+    changes_made = False
+
+    logger.info("START - GATEWAY configuration PURGE started, run mode "
+                "is {}".format(run_mode))
+    cfg = Config(logger)
+    this_host = socket.gethostname().split('.')[0]
+    perform_cleanup_tasks = is_cleanup_host(cfg)
+
+    #
+    # Purge gateway configuration, if the config has gateways
+    if run_mode == 'gateway' and len(cfg.config['gateways'].keys()) > 0:
+
+        lio = LIO()
+        gateway = Gateway(cfg)
+
+        if gateway.session_count() > 0:
+            module.fail_json(msg="Unable to purge - gateway still has active "
+                                 "sessions")
+
+        gateway.drop_target(this_host)
+        if gateway.error:
+            module.fail_json(msg=gateway.error_msg)
+
+        lio.drop_lun_maps(cfg, perform_cleanup_tasks)
+        if lio.error:
+            module.fail_json(msg=lio.error_msg)
+
+        if gateway.changed or lio.changed:
+
+            # each gateway removes it's own entry from the config
+            cfg.del_item("gateways", this_host)
+
+            if perform_cleanup_tasks:
+                cfg.reset = True
+
+                # drop all client definitions from the configuration object
+                client_names = cfg.config["clients"].keys()
+                for client in client_names:
+                    cfg.del_item("clients", client)
+
+                cfg.del_item("gateways", "iqn")
+                cfg.del_item("gateways", "created")
+                cfg.del_item("gateways", "ip_list")
+
+            cfg.commit()
+
+            changes_made = True
+
+    elif run_mode == 'disks' and len(cfg.config['disks'].keys()) > 0:
+        #
+        # Remove the disks on this host, that have been registered in the
+        # config object
+        #
+        # if the owner field for a disk is set to this host, this host can
+        # safely delete it
+        # nb. owner gets set at rbd allocation and mapping time
+        images_left = []
+
+        # delete_list will contain a list of pool/image names where the owner
+        # is this host
+        delete_list = [key.replace('.', '/', 1) for key in cfg.config['disks']
+                       if cfg.config['disks'][key]['owner'] == this_host]
+
+        if delete_list:
+            images_left = delete_group(module, delete_list, cfg)
+
+        # if the delete list still has entries we had problems deleting the
+        # images
+        if images_left:
+            module.fail_json(msg="Problems deleting the following rbd's : "
+                                 "{}".format(','.join(images_left)))
+
+        changes_made = cfg.changed
+
+        logger.debug("ending lock state variable {}".format(cfg.config_locked))
+
+    logger.info("END   - GATEWAY configuration PURGE complete")
+
+    module.exit_json(changed=changes_made,
+                     meta={"msg": "Purge of iSCSI settings ({}) "
+                                  "complete".format(run_mode)})
+
+
+if __name__ == '__main__':
+
+    module_name = os.path.basename(__file__).replace('ansible_module_', '')
+    logger = logging.getLogger(os.path.basename(module_name))
+    logger.setLevel(logging.DEBUG)
+    handler = RotatingFileHandler('/var/log/ansible-module-igw_config.log',
+                                  maxBytes=5242880,
+                                  backupCount=7)
+    log_fmt = logging.Formatter('%(asctime)s %(name)s %(levelname)-8s : '
+                                '%(message)s')
+    handler.setFormatter(log_fmt)
+    logger.addHandler(handler)
+
+    settings.init()
+
+    ansible_main()
index ed97d539c095cf1413af30cc23dea272095b97dd..b96b9cfe81b55bd294431d2900404720dffac4cb 100644 (file)
@@ -1 +1,13 @@
 ---
+galaxy_info:
+  author: Paul Cuzner
+  description: Installs Ceph iSCSI Gateways
+  license: Apache
+  min_ansible_version: 2.3
+  platforms:
+    - name: EL
+      versions:
+        - 7
+  categories:
+    - system
+dependencies: []
diff --git a/roles/ceph-iscsi-gw/tasks/configure_iscsi.yml b/roles/ceph-iscsi-gw/tasks/configure_iscsi.yml
new file mode 100644 (file)
index 0000000..d270fac
--- /dev/null
@@ -0,0 +1,33 @@
+---
+- name: igw_gateway (tgt) | configure iscsi target (gateway)
+  igw_gateway:
+    mode: "target"
+    gateway_iqn: "{{ gateway_iqn }}"
+    gateway_ip_list: "{{ gateway_ip_list }}"
+  register: target
+
+- name: igw_lun | configure luns (create/map rbds and add to lio)
+  igw_lun:
+    pool: "{{ item.pool }}"
+    image: "{{ item.image }}"
+    size: "{{ item.size }}"
+    host: "{{ item.host }}"
+    state: "{{ item.state }}"
+  with_items: "{{ rbd_devices|default([]) }}"
+  register: images
+
+- name: igw_gateway (map) | map luns to the iscsi target
+  igw_gateway:
+    mode: "map"
+    gateway_iqn: "{{ gateway_iqn }}"
+    gateway_ip_list: "{{ gateway_ip_list }}"
+  register: luns
+
+- name: igw_client | configure client connectivity
+  igw_client:
+    client_iqn: "{{ item.client }}"
+    image_list: "{{ item.image_list }}"
+    chap: "{{ item.chap }}"
+    state: "{{ item.status }}"
+  with_items: "{{ client_connections|default([]) }}"
+  register: clients
diff --git a/roles/ceph-iscsi-gw/tasks/deploy_ssl_keys.yml b/roles/ceph-iscsi-gw/tasks/deploy_ssl_keys.yml
new file mode 100644 (file)
index 0000000..0665f0a
--- /dev/null
@@ -0,0 +1,35 @@
+---
+- name: set crt path(s)
+  set_fact:
+    crt_files:
+      - "/etc/ceph/iscsi-gateway.crt"
+      - "/etc/ceph/iscsi-gateway.key"
+      - "/etc/ceph/iscsi-gateway.pem"
+      - "/etc/ceph/iscsi-gateway-pub.key"
+
+- name: stat for crt file(s)
+  local_action: stat path={{ fetch_directory }}/{{ fsid }}/{{ item }}
+  with_items: "{{ crt_files }}"
+  changed_when: false
+  failed_when: false
+  always_run: true
+  register: crt_files_exist
+
+- name: try to fetch crt file(s)
+  copy:
+    src: "{{ fetch_directory }}/{{ fsid }}/{{ item.0 }}"
+    dest: "{{ item.0 }}"
+    owner: root
+    group: root
+    mode: 0400
+  changed_when: false
+  with_together:
+    - "{{ crt_files }}"
+    - "{{ crt_files_exist.results }}"
+  when: item.1.stat.exists == true
+
+- include: generate_crt.yml
+  with_together:
+    - "{{ crt_files }}"
+    - "{{ crt_files_exist.results }}"
+  when: item.1.stat.exists == false
diff --git a/roles/ceph-iscsi-gw/tasks/generate_crt.yml b/roles/ceph-iscsi-gw/tasks/generate_crt.yml
new file mode 100644 (file)
index 0000000..db53acb
--- /dev/null
@@ -0,0 +1,33 @@
+---
+- name: (local) create ssl crt/key files
+  shell: |
+    openssl req -newkey rsa:2048 -nodes -keyout /etc/ceph/iscsi-gateway.key -x509 -days 365 -out /etc/ceph/iscsi-gateway.crt -subj "/C=US/ST=./L=./O=RedHat/OU=Linux/CN={{ ansible_hostname }}"
+  run_once: True
+
+- name: (local) create pem
+  shell: |
+    cat /etc/ceph/iscsi-gateway.crt /etc/ceph/iscsi-gateway.key > /etc/ceph/iscsi-gateway.pem
+  run_once: True
+  register: pem
+
+- name: (local) create public key from pem
+  shell: |
+    openssl x509 -inform pem -in /etc/ceph/iscsi-gateway.pem -pubkey -noout > /etc/ceph/iscsi-gateway-pub.key
+  run_once: True
+  when:
+    - pem.changed
+
+- name: lock ssl file access to root only
+  file:
+    path: "{{ item }}"
+    mode: 0400
+    owner: root
+    group: root
+  with_items: "{{ crt_files }}"
+
+- name: copy crt(s) to the ansible server
+  fetch:
+    src: "{{ item }}"
+    dest: "{{ fetch_directory }}/{{ fsid }}/{{ item }}"
+    flat: yes
+  with_items: "{{ crt_files }}"
index ed97d539c095cf1413af30cc23dea272095b97dd..87102c52337e73c40bbad6c92cf5f7adb91ca702 100644 (file)
@@ -1 +1,8 @@
 ---
+- include: prerequisites.yml
+
+# deploy_ssl_keys used the ansible controller to create self-signed crt/key/pub files
+# and transfers them to /etc/ceph directory on each controller. SSL certs are used by
+# the API for https support.
+- include: deploy_ssl_keys.yml
+- include: configure_iscsi.yml
diff --git a/roles/ceph-iscsi-gw/tasks/prerequisites.yml b/roles/ceph-iscsi-gw/tasks/prerequisites.yml
new file mode 100644 (file)
index 0000000..3cc4670
--- /dev/null
@@ -0,0 +1,34 @@
+---
+- name: check the status of the target.service override
+  stat:
+    path: /etc/systemd/system/target.service
+  register: target
+
+- name: mask the target service - preventing manual start
+  systemd:
+    name: target
+    masked: yes
+    enabled: no
+  when:
+    - target.stat.exists == False or (target.stat.exists and target.stat.islnk == False)
+
+- name: enable the rbd-target-gw service and make sure it is running
+  service:
+    name: rbd-target-gw
+    enabled: yes
+    state: started
+
+- name: copy admin key
+  copy:
+    src: "{{ fetch_directory }}/{{ fsid }}/etc/ceph/{{ cluster }}.client.admin.keyring"
+    dest: "/etc/ceph/{{ cluster }}.client.admin.keyring"
+    owner: "ceph"
+    group: "ceph"
+    mode: "0600"
+  when:
+    - cephx
+
+- name: deploy gateway settings, used by the ceph_iscsi_config modules
+  template:
+    src: "{{ role_path }}/templates/iscsi-gateway.cfg.j2"
+    dest: /etc/ceph/iscsi-gateway.cfg
diff --git a/roles/ceph-iscsi-gw/templates/iscsi-gateway.cfg.j2 b/roles/ceph-iscsi-gw/templates/iscsi-gateway.cfg.j2
new file mode 100644 (file)
index 0000000..ce0d859
--- /dev/null
@@ -0,0 +1,17 @@
+# This is seed configuration used by the ceph_iscsi_config modules
+# when handling configuration tasks for iscsi gateway(s)
+#
+# {{ ansible_managed }}
+
+[config]
+cluster_name = {{ cluster }}
+gateway_keyring = /etc/ceph/{{ cluster }}.client.admin.keyring
+
+
+# Optional settings related to the CLI/API service
+#api_user = admin
+#api_password = admin
+#api_port = 5001
+#api_secure = true
+#loop_delay = .5
+#trusted_ip_list = 192.168.122.1
index f32621250774fec74152f70603d327872af43547..113e9bb037c000c9025d0bf3368c8c1c12e1f012 100644 (file)
@@ -12,6 +12,7 @@
   - rbdmirrors
   - clients
   - mgrs
+  - iscsi_gws
 
   gather_facts: false
 
     - { role: ceph-common, when: "ceph_release_num.{{ ceph_stable_release }} > ceph_release_num.jewel" }
     - { role: ceph-config, when: "ceph_release_num.{{ ceph_stable_release }} > ceph_release_num.jewel" }
     - { role: ceph-mgr, when: "ceph_release_num.{{ ceph_stable_release }} > ceph_release_num.jewel" }
+
+- hosts: iscsi_gws
+  gather_facts: false
+  become: True
+  roles:
+    - { role: ceph-defaults, when: "ceph_release_num.{{ ceph_stable_release }} >= ceph_release_num.luminous" }
+    - { role: ceph-common, when: "ceph_release_num.{{ ceph_stable_release }} >= ceph_release_num.luminous" }
+    - { role: ceph-config, when: "ceph_release_num.{{ ceph_stable_release }} >= ceph_release_num.luminous" }
+    - { role: ceph-iscsi-gw, when: "ceph_release_num.{{ ceph_stable_release }} >= ceph_release_num.luminous" }
+
index 71408cdef3c64994a47b894649bcb22bfbd476fd..0586396469009bb0372f6924e452b02a09f3e86c 100644 (file)
@@ -23,3 +23,6 @@ ceph-nfs0
 
 [rbdmirrors]
 ceph-rbd-mirror0
+
+#[iscsi_gws]
+#ceph-iscsi-gw0 ceph_repository="dev"