resync ceph-iscsi-gw with old upstream

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>
pull/1747/head
Sébastien Han 2017-08-04 20:18:11 +02:00
parent 358b3b588d
commit aa364264cd
33 changed files with 1860 additions and 23 deletions

22
Vagrantfile vendored
View File

@ -50,15 +50,15 @@ ansible_provision = proc do |ansible|
# these aren't supported by Vagrant, see # these aren't supported by Vagrant, see
# https://github.com/mitchellh/vagrant/issues/3539 # https://github.com/mitchellh/vagrant/issues/3539
ansible.groups = { ansible.groups = {
'mons' => (0..NMONS - 1).map { |j| "#{LABEL_PREFIX}mon#{j}" }, 'mons' => (0..NMONS - 1).map { |j| "#{LABEL_PREFIX}mon#{j}" },
'osds' => (0..NOSDS - 1).map { |j| "#{LABEL_PREFIX}osd#{j}" }, 'osds' => (0..NOSDS - 1).map { |j| "#{LABEL_PREFIX}osd#{j}" },
'mdss' => (0..NMDSS - 1).map { |j| "#{LABEL_PREFIX}mds#{j}" }, 'mdss' => (0..NMDSS - 1).map { |j| "#{LABEL_PREFIX}mds#{j}" },
'rgws' => (0..NRGWS - 1).map { |j| "#{LABEL_PREFIX}rgw#{j}" }, 'rgws' => (0..NRGWS - 1).map { |j| "#{LABEL_PREFIX}rgw#{j}" },
'nfss' => (0..NNFSS - 1).map { |j| "#{LABEL_PREFIX}nfs#{j}" }, 'nfss' => (0..NNFSS - 1).map { |j| "#{LABEL_PREFIX}nfs#{j}" },
'rbd_mirrors' => (0..NRBD_MIRRORS - 1).map { |j| "#{LABEL_PREFIX}rbd_mirror#{j}" }, 'rbd_mirrors' => (0..NRBD_MIRRORS - 1).map { |j| "#{LABEL_PREFIX}rbd_mirror#{j}" },
'clients' => (0..CLIENTS - 1).map { |j| "#{LABEL_PREFIX}client#{j}" }, 'clients' => (0..CLIENTS - 1).map { |j| "#{LABEL_PREFIX}client#{j}" },
'iscsi_gw' => (0..NISCSI_GWS - 1).map { |j| "#{LABEL_PREFIX}iscsi_gw#{j}" }, 'iscsi_gws' => (0..NISCSI_GWS - 1).map { |j| "#{LABEL_PREFIX}iscsi_gw#{j}" },
'mgrs' => (0..MGRS - 1).map { |j| "#{LABEL_PREFIX}mgr#{j}" } 'mgrs' => (0..MGRS - 1).map { |j| "#{LABEL_PREFIX}mgr#{j}" }
} }
if RESTAPI then if RESTAPI then
@ -397,7 +397,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
end end
(0..NISCSI_GWS - 1).each do |i| (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}" iscsi_gw.vm.hostname = "#{LABEL_PREFIX}iscsi-gw#{i}"
if ASSIGN_STATIC_IP if ASSIGN_STATIC_IP
iscsi_gw.vm.network :private_network, iscsi_gw.vm.network :private_network,
@ -420,7 +420,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
end end
# Parallels # Parallels
iscsi_gw.vm.provider "parallels" do |prl| iscsi_gw.vm.provider "parallels" do |prl|
prl.name = "ceph-iscsi-gw#{i}" prl.name = "iscsi-gw#{i}"
prl.memory = "#{MEMORY}" prl.memory = "#{MEMORY}"
end end

View File

@ -46,13 +46,6 @@ pushd %{buildroot}%{_datarootdir}/ceph-ansible
rm -r infrastructure-playbooks/untested-by-ci rm -r infrastructure-playbooks/untested-by-ci
popd 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 %check
# Borrowed from upstream's .travis.yml: # Borrowed from upstream's .travis.yml:
ansible-playbook -i dummy-ansible-hosts test.yml --syntax-check ansible-playbook -i dummy-ansible-hosts test.yml --syntax-check

View File

@ -51,7 +51,7 @@ dummy:
#restapi_group_name: restapis #restapi_group_name: restapis
#rbdmirror_group_name: rbdmirrors #rbdmirror_group_name: rbdmirrors
#client_group_name: clients #client_group_name: clients
#iscsi_group_name: iscsigws #iscsi_gw_group_name: iscsi_gws
#mgr_group_name: mgrs #mgr_group_name: mgrs
# If check_firewall is true, then ansible will try to determine if the # 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 # flavors so far include: ceph_master, ceph_jewel, ceph_kraken, ceph_luminous
#nfs_ganesha_flavor: "ceph_master" #nfs_ganesha_flavor: "ceph_master"
#ceph_iscsi_config_dev: true # special repo for deploying iSCSI gateways
# REPOSITORY: CUSTOM # REPOSITORY: CUSTOM
# #

View File

@ -7,4 +7,45 @@
# file as a good configuration file when no variable in it. # file as a good configuration file when no variable in it.
dummy: 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' }

View File

@ -51,7 +51,7 @@ fetch_directory: ~/ceph-ansible-keys
#restapi_group_name: restapis #restapi_group_name: restapis
#rbdmirror_group_name: rbdmirrors #rbdmirror_group_name: rbdmirrors
#client_group_name: clients #client_group_name: clients
#iscsi_group_name: iscsigws #iscsi_gw_group_name: iscsi_gws
#mgr_group_name: mgrs #mgr_group_name: mgrs
# If check_firewall is true, then ansible will try to determine if the # 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 # flavors so far include: ceph_master, ceph_jewel, ceph_kraken, ceph_luminous
#nfs_ganesha_flavor: "ceph_master" #nfs_ganesha_flavor: "ceph_master"
#ceph_iscsi_config_dev: true # special repo for deploying iSCSI gateways
# REPOSITORY: CUSTOM # REPOSITORY: CUSTOM
# #

View File

@ -426,7 +426,7 @@
hosts: hosts:
- "{{ mon_group_name|default('mons') }}" - "{{ mon_group_name|default('mons') }}"
gather_facts: false # Already gathered previously gather_facts: false # already gathered previously
become: true become: true
@ -453,6 +453,38 @@
path: /var/lib/ceph/ path: /var/lib/ceph/
state: absent 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 - name: final cleanup - check any running ceph, purge ceph packages, purge config and remove data
vars: vars:

View File

@ -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()

View File

@ -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()

166
library/igw_lun.py 100644
View File

@ -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()

View File

@ -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()

View File

@ -66,3 +66,18 @@
fail: fail:
msg: "Systemd must be present" msg: "Systemd must be present"
when: ansible_service_mgr != 'systemd' 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

View File

@ -97,3 +97,15 @@
when: when:
- mgr_group_name in group_names - mgr_group_name in group_names
- ceph_release_num.{{ ceph_release }} > ceph_release_num.jewel - 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

View File

@ -21,7 +21,7 @@
state: present state: present
gpgkey: "{{ ceph_stable_key }}" gpgkey: "{{ ceph_stable_key }}"
baseurl: "{{ ceph_mirror }}/nfs-ganesha/rpm-{{ nfs_ganesha_stable_branch }}/{{ ceph_stable_release }}/$basearch" baseurl: "{{ ceph_mirror }}/nfs-ganesha/rpm-{{ nfs_ganesha_stable_branch }}/{{ ceph_stable_release }}/$basearch"
when: when:
- nfs_group_name in group_names - nfs_group_name in group_names
- nfs_ganesha_stable - nfs_ganesha_stable

View File

@ -33,3 +33,22 @@
- nfs_group_name in group_names - nfs_group_name in group_names
- nfs_ganesha_dev - 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

View File

@ -43,7 +43,7 @@ nfs_group_name: nfss
restapi_group_name: restapis restapi_group_name: restapis
rbdmirror_group_name: rbdmirrors rbdmirror_group_name: rbdmirrors
client_group_name: clients client_group_name: clients
iscsi_group_name: iscsigws iscsi_gw_group_name: iscsi_gws
mgr_group_name: mgrs mgr_group_name: mgrs
# If check_firewall is true, then ansible will try to determine if the # 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 # flavors so far include: ceph_master, ceph_jewel, ceph_kraken, ceph_luminous
nfs_ganesha_flavor: "ceph_master" nfs_ganesha_flavor: "ceph_master"
ceph_iscsi_config_dev: true # special repo for deploying iSCSI gateways
# REPOSITORY: CUSTOM # REPOSITORY: CUSTOM
# #

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View 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' }

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View 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: []

View File

@ -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

View File

@ -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

View File

@ -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 }}"

View 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

View File

@ -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

View File

@ -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

View File

@ -12,6 +12,7 @@
- rbdmirrors - rbdmirrors
- clients - clients
- mgrs - mgrs
- iscsi_gws
gather_facts: false gather_facts: false
@ -140,3 +141,13 @@
- { role: ceph-common, when: "ceph_release_num.{{ ceph_stable_release }} > ceph_release_num.jewel" } - { 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-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" } - { 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" }

View File

@ -23,3 +23,6 @@ ceph-nfs0
[rbdmirrors] [rbdmirrors]
ceph-rbd-mirror0 ceph-rbd-mirror0
#[iscsi_gws]
#ceph-iscsi-gw0 ceph_repository="dev"