diff --git a/bin/cinder-rtstool b/bin/cinder-rtstool new file mode 100755 index 00000000000..5781b7ed9e1 --- /dev/null +++ b/bin/cinder-rtstool @@ -0,0 +1,238 @@ +#!/usr/bin/env python +# vim: et tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 - 2013 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +import gettext +import re +import sys + +import rtslib + +gettext.install('cinder-rtstool', unicode=1) + + +class RtstoolError(Exception): + pass + + +class RtstoolImportError(RtstoolError): + pass + + +def create(backing_device, name, userid, password, initiator_iqns=None): + try: + rtsroot = rtslib.root.RTSRoot() + except rtslib.utils.RTSLibError: + print _('Ensure that configfs is mounted at /sys/kernel/config.') + raise + + # Look to see if BlockStorageObject already exists + for x in rtsroot.storage_objects: + if x.dump()['name'] == name: + # Already exists, use this one + return + + so_new = rtslib.BlockStorageObject(name=name, + dev=backing_device) + + target_new = rtslib.Target(rtslib.FabricModule('iscsi'), name, 'create') + + tpg_new = rtslib.TPG(target_new, mode='create') + tpg_new.set_attribute('authentication', '1') + + lun_new = rtslib.LUN(tpg_new, storage_object=so_new) + + initiator_name = None + name_file = '/etc/iscsi/initiatorname.iscsi' + + try: + with open(name_file, 'r') as f: + for line in f: + m = re.match('InitiatorName=(.+)', line) + if m != None: + initiator_name = m.group(1) + break + except IOError: + raise RtstoolError(_('Could not open %s') % name_file) + + if initiator_name == None: + raise RtstoolError(_('Could not read InitiatorName from %s') % + name_file) + + acl_new = rtslib.NodeACL(tpg_new, initiator_name, mode='create') + + acl_new.chap_userid = userid + acl_new.chap_password = password + + rtslib.MappedLUN(acl_new, lun_new.lun, lun_new.lun) + + if initiator_iqns: + initiator_iqns = initiator_iqns.strip(' ') + for i in initiator_iqns.split(','): + acl_new = rtslib.NodeACL(tpg_new, i, mode='create') + acl_new.chap_userid = userid + acl_new.chap_password = password + + rtslib.MappedLUN(acl_new, lun_new.lun, lun_new.lun) + + tpg_new.enable = 1 + + try: + rtslib.NetworkPortal(tpg_new, '0.0.0.0', 3260, mode='any') + except rtslib.utils.RTSLibError: + print _('Error creating NetworkPortal: ensure port 3260 ' + 'is not in use by another service.') + raise + + try: + rtslib.NetworkPortal(tpg_new, '::0', 3260, mode='any') + except rtslib.utils.RTSLibError: + # TODO(emh): Binding to IPv6 fails sometimes -- let pass for now. + pass + + +def add_initiator(target_iqn, initiator_iqn, userid, password): + try: + rtsroot = rtslib.root.RTSRoot() + except rtslib.utils.RTSLibError: + print _('Ensure that configfs is mounted at /sys/kernel/config.') + raise + + # Look for the target + target = None + for t in rtsroot.targets: + if t.dump()['wwn'] == target_iqn: + target = t + break + if target == None: + raise RtstoolError(_('Could not find target %s') % target_iqn) + + tpg = target.tpgs.next() # get the first one + for acl in tpg.dump()['node_acls']: + # See if this ACL configuration already exists + if acl['node_wwn'] == initiator_iqn: + # No further action required + return + + acl_new = rtslib.NodeACL(tpg, initiator_iqn, mode='create') + acl_new.chap_userid = userid + acl_new.chap_password = password + + rtslib.MappedLUN(acl_new, 0, tpg_lun=0) + + +def get_targets(): + rtsroot = rtslib.root.RTSRoot() + for x in rtsroot.targets: + print(x.dump()['wwn']) + + +def delete(iqn): + rtsroot = rtslib.root.RTSRoot() + for x in rtsroot.targets: + if x.dump()['wwn'] == iqn: + x.delete() + break + + for x in rtsroot.storage_objects: + if x.dump()['name'] == iqn: + x.delete() + break + + +def verify_rtslib(): + for member in ['BlockStorageObject', 'FabricModule', 'LUN', + 'MappedLUN', 'NetworkPortal', 'NodeACL', 'root', + 'Target', 'TPG']: + if not hasattr(rtslib, member): + raise RtstoolImportError(_("rtslib is missing member %s: " + "You may need a newer python-rtslib.") % + member) + + +def usage(): + print "Usage:" + print sys.argv[0], \ + "create [device] [name] [userid] [password]", \ + "" + print sys.argv[0], \ + "add-initiator [target_iqn] [userid] [password] [initiator_iqn]" + print sys.argv[0], "get-targets" + print sys.argv[0], "delete [iqn]" + print sys.argv[0], "verify" + sys.exit(1) + + +def main(argv=None): + if argv is None: + argv = sys.argv + + if len(argv) < 2: + usage() + + if argv[1] == 'create': + if len(argv) < 6: + usage() + + if len(argv) > 7: + usage() + + backing_device = argv[2] + name = argv[3] + userid = argv[4] + password = argv[5] + initiator_iqns = None + + if len(argv) > 6: + initiator_iqns = argv[6] + + create(backing_device, name, userid, password, initiator_iqns) + + elif argv[1] == 'add-initiator': + if len(argv) < 6: + usage() + + target_iqn = argv[2] + userid = argv[3] + password = argv[4] + initiator_iqn = argv[5] + + add_initiator(target_iqn, initiator_iqn, userid, password) + + elif argv[1] == 'get-targets': + get_targets() + + elif argv[1] == 'delete': + if len(argv) < 3: + usage() + + iqn = argv[2] + delete(iqn) + + elif argv[1] == 'verify': + # This is used to verify that this script can be called by cinder, + # and that rtslib is new enough to work. + verify_rtslib() + return 0 + + else: + usage() + + return 0 + +if __name__ == '__main__': + sys.exit(main())