Hello community,
here is the log from the commit of package ceph-iscsi for openSUSE:Factory checked in at 2019-04-09 20:18:42
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ceph-iscsi (Old)
and /work/SRC/openSUSE:Factory/.ceph-iscsi.new.3908 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ceph-iscsi"
Tue Apr 9 20:18:42 2019 rev:9 rq:692489 version:3.0+1554735444.g63aceaf
Changes:
--------
--- /work/SRC/openSUSE:Factory/ceph-iscsi/ceph-iscsi.changes 2019-03-26 15:45:13.968092820 +0100
+++ /work/SRC/openSUSE:Factory/.ceph-iscsi.new.3908/ceph-iscsi.changes 2019-04-09 20:18:42.421844161 +0200
@@ -1,0 +2,10 @@
+Mon Apr 8 14:35:04 UTC 2019 - ncutler@suse.com
+
+- Update to 3.0+1554735444.g63aceaf:
+ + Adds endpoints:
+ * /api/targetinfo/
+ * /api/gatewayinfo
+ * /api/clientinfo//
+ + Fix upgrade from config v3 when 'controls' field is missing
+
+-------------------------------------------------------------------
Old:
----
ceph-iscsi-3.0+1553528639.g1149ac6.tar.gz
New:
----
ceph-iscsi-3.0+1554735444.g63aceaf.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ ceph-iscsi.spec ++++++
--- /var/tmp/diff_new_pack.Ygo303/_old 2019-04-09 20:18:43.901846179 +0200
+++ /var/tmp/diff_new_pack.Ygo303/_new 2019-04-09 20:18:43.901846179 +0200
@@ -20,7 +20,7 @@
Name: ceph-iscsi
-Version: 3.0+1553528639.g1149ac6
+Version: 3.0+1554735444.g63aceaf
Release: 1%{?dist}
Group: System/Filesystems
Summary: Python modules for Ceph iSCSI gateway configuration management
++++++ ceph-iscsi-3.0+1553528639.g1149ac6.tar.gz -> ceph-iscsi-3.0+1554735444.g63aceaf.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ceph-iscsi-3.0+1553528639.g1149ac6/ceph-iscsi.spec new/ceph-iscsi-3.0+1554735444.g63aceaf/ceph-iscsi.spec
--- old/ceph-iscsi-3.0+1553528639.g1149ac6/ceph-iscsi.spec 2019-03-25 16:43:59.851691154 +0100
+++ new/ceph-iscsi-3.0+1554735444.g63aceaf/ceph-iscsi.spec 2019-04-08 16:57:24.610585206 +0200
@@ -20,7 +20,7 @@
Name: ceph-iscsi
-Version: 3.0+1553528639.g1149ac6
+Version: 3.0+1554735444.g63aceaf
Release: 1%{?dist}
Group: System/Filesystems
Summary: Python modules for Ceph iSCSI gateway configuration management
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ceph-iscsi-3.0+1553528639.g1149ac6/ceph_iscsi_config/client.py new/ceph-iscsi-3.0+1554735444.g63aceaf/ceph_iscsi_config/client.py
--- old/ceph-iscsi-3.0+1553528639.g1149ac6/ceph_iscsi_config/client.py 2019-03-25 16:43:59.583689649 +0100
+++ new/ceph-iscsi-3.0+1554735444.g63aceaf/ceph_iscsi_config/client.py 2019-04-08 16:57:24.310583510 +0200
@@ -235,6 +235,34 @@
self.change_count += 1
@staticmethod
+ def get_client_info(target_iqn, client_iqn):
+ result = {
+ "alias": '',
+ "state": '',
+ "ip_address": []
+ }
+ iscsi_fabric = ISCSIFabricModule()
+ target = Target(iscsi_fabric, target_iqn, 'lookup')
+ for tpg in target.tpgs:
+ if tpg.enable:
+ for client in tpg.node_acls:
+ if client.node_wwn != client_iqn:
+ continue
+ session = client.session
+ if session is None:
+ break
+ result['alias'] = session.get('alias')
+ state = session.get('state').upper()
+ result['state'] = state
+ ips = set()
+ if state == 'LOGGED_IN':
+ for conn in session.get('connections'):
+ ips.add(conn.get('address'))
+ result['ip_address'] = list(ips)
+ break
+ return result
+
+ @staticmethod
def define_clients(logger, config, target_iqn):
"""
define the clients (nodeACLs) to the gateway definition
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ceph-iscsi-3.0+1553528639.g1149ac6/ceph_iscsi_config/common.py new/ceph-iscsi-3.0+1554735444.g63aceaf/ceph_iscsi_config/common.py
--- old/ceph-iscsi-3.0+1553528639.g1149ac6/ceph_iscsi_config/common.py 2019-03-25 16:43:59.583689649 +0100
+++ new/ceph-iscsi-3.0+1554735444.g63aceaf/ceph_iscsi_config/common.py 2019-04-08 16:57:24.310583510 +0200
@@ -199,7 +199,7 @@
'clients': self.config['clients'],
'portals': portals,
'groups': self.config['groups'],
- 'controls': self.config['controls'],
+ 'controls': self.config.get('controls', {}),
'ip_list': self.config['gateways']['ip_list']
}
self.add_item('discovery_auth', None, {
@@ -209,7 +209,8 @@
self.add_item("targets", None, {})
self.add_item("targets", iqn, target)
self.update_item("targets", iqn, target)
- self.del_item('controls', None)
+ if 'controls' in self.config:
+ self.del_item('controls', None)
self.del_item('clients', None)
self.del_item('groups', None)
self.update_item("gateways", None, gateways)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ceph-iscsi-3.0+1553528639.g1149ac6/ceph_iscsi_config/target.py new/ceph-iscsi-3.0+1554735444.g63aceaf/ceph_iscsi_config/target.py
--- old/ceph-iscsi-3.0+1553528639.g1149ac6/ceph_iscsi_config/target.py 2019-03-25 16:43:59.583689649 +0100
+++ new/ceph-iscsi-3.0+1554735444.g63aceaf/ceph_iscsi_config/target.py 2019-04-08 16:57:24.310583510 +0200
@@ -709,3 +709,9 @@
config.del_item('gateways', local_gw)
config.commit()
+
+ @staticmethod
+ def get_num_sessions(target_iqn):
+ with open('/sys/kernel/config/target/iscsi/{}/fabric_statistics/iscsi_instance'
+ '/sessions'.format(target_iqn)) as sessions_file:
+ return int(sessions_file.read().rstrip('\n'))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ceph-iscsi-3.0+1553528639.g1149ac6/gwcli/client.py new/ceph-iscsi-3.0+1554735444.g63aceaf/gwcli/client.py
--- old/ceph-iscsi-3.0+1553528639.g1149ac6/gwcli/client.py 2019-03-25 16:43:59.583689649 +0100
+++ new/ceph-iscsi-3.0+1554735444.g63aceaf/gwcli/client.py 2019-04-08 16:57:24.314583532 +0200
@@ -2,11 +2,10 @@
from gwcli.utils import response_message, APIRequest, get_config
-from ceph_iscsi_config.client import CHAP
+from ceph_iscsi_config.client import CHAP, GWClient
import ceph_iscsi_config.settings as settings
from ceph_iscsi_config.utils import human_size
-import rtslib_fb.root as root
from rtslib_fb.utils import normalize_wwn, RTSLibError
# this ignores the warning issued when verify=False is used
@@ -690,22 +689,11 @@
@property
def logged_in(self):
-
- r = root.RTSRoot()
- for sess in r.sessions:
- if sess['parent_nodeacl'].node_wwn == self.client_iqn:
- self.alias = sess.get('alias')
- state = sess.get('state').upper()
- ips = set()
- if state == 'LOGGED_IN':
- for conn in sess.get('connections'):
- ips.add(conn.get('address'))
- self.ip_address = ','.join(list(ips))
- else:
- self.ip_address = ''
-
- return state
- return ''
+ target_iqn = self.parent.parent.name
+ client_info = GWClient.get_client_info(target_iqn, self.client_iqn)
+ self.alias = client_info['alias']
+ self.ip_address = ','.join(client_info['ip_address'])
+ return client_info['state']
class MappedLun(UINode):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ceph-iscsi-3.0+1553528639.g1149ac6/gwcli/utils.py new/ceph-iscsi-3.0+1554735444.g63aceaf/gwcli/utils.py
--- old/ceph-iscsi-3.0+1553528639.g1149ac6/gwcli/utils.py 2019-03-25 16:43:59.587689671 +0100
+++ new/ceph-iscsi-3.0+1554735444.g63aceaf/gwcli/utils.py 2019-04-08 16:57:24.314583532 +0200
@@ -8,8 +8,8 @@
from rtslib_fb.utils import normalize_wwn, RTSLibError
-import rtslib_fb.root as root
+from ceph_iscsi_config.client import GWClient
import ceph_iscsi_config.settings as settings
from ceph_iscsi_config.utils import (resolve_ip_addresses, gen_file_hash,
CephiSCSIError)
@@ -283,14 +283,10 @@
# client to delete must not be logged in - we're just checking locally,
# since *all* nodes are set up the same, and a client login request
# would normally login to each gateway
- lio_root = root.RTSRoot()
- clients_logged_in = [session['parent_nodeacl'].node_wwn
- for session in lio_root.sessions
- if session['state'] == 'LOGGED_IN']
-
- if client_iqn in clients_logged_in:
- return ("Client '{}' is logged in - unable to delete until"
- " it's logged out".format(client_iqn))
+ client_info = GWClient.get_client_info(target_iqn, client_iqn)
+ if client_info['state'] == 'LOGGED_IN':
+ return ("Client '{}' is logged in to {}- unable to delete until"
+ " it's logged out".format(client_iqn, target_iqn))
# at this point, the client looks ok for a DELETE operation
return 'ok'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ceph-iscsi-3.0+1553528639.g1149ac6/rbd-target-api.py new/ceph-iscsi-3.0+1554735444.g63aceaf/rbd-target-api.py
--- old/ceph-iscsi-3.0+1553528639.g1149ac6/rbd-target-api.py 2019-03-25 16:43:59.587689671 +0100
+++ new/ceph-iscsi-3.0+1554735444.g63aceaf/rbd-target-api.py 2019-04-08 16:57:24.314583532 +0200
@@ -1618,6 +1618,76 @@
return jsonify(message='OK'), 200
+@app.route('/api/targetinfo/', methods=['GET'])
+@requires_restricted_auth
+def targetinfo(target_iqn):
+ """
+ Returns the total number of active sessions for
+ **RESTRICTED**
+ Examples:
+ curl --insecure --user admin:admin -X GET
+ http://192.168.122.69:5000/api/targetinfo/iqn.2003-01.com.redhat.iscsi-gw:is...
+ """
+ if target_iqn not in config.config['targets']:
+ return jsonify(message="Target {} does not exist".format(target_iqn)), 400
+ target_config = config.config['targets'][target_iqn]
+ gateways = target_config['portals']
+ num_sessions = 0
+ for gateway in gateways.keys():
+ resp_text, resp_code = call_api([gateway], '_targetinfo', target_iqn, http_method='get')
+ if resp_code != 200:
+ return jsonify(message="{}".format(resp_text)), resp_code
+ gateway_response = json.loads(resp_text)
+ num_sessions += gateway_response['num_sessions']
+ return jsonify({
+ "num_sessions": num_sessions
+ }), 200
+
+
+@app.route('/api/_targetinfo/', methods=['GET'])
+@requires_restricted_auth
+def _targetinfo(target_iqn):
+ """
+ Returns the number of active sessions for on local gateway
+ **RESTRICTED**
+ Examples:
+ curl --insecure --user admin:admin -X GET
+ http://192.168.122.69:5000/api/_targetinfo/iqn.2003-01.com.redhat.iscsi-gw:i...
+ """
+ if target_iqn not in config.config['targets']:
+ return jsonify(message="Target {} does not exist".format(target_iqn)), 400
+ target_config = config.config['targets'][target_iqn]
+ local_gw = this_host()
+ if local_gw not in target_config['portals']:
+ return jsonify(message="{} is not a portal of target {}".format(local_gw, target_iqn)), 400
+ num_sessions = GWTarget.get_num_sessions(target_iqn)
+ return jsonify({
+ "num_sessions": num_sessions
+ }), 200
+
+
+@app.route('/api/gatewayinfo', methods=['GET'])
+@requires_restricted_auth
+def gatewayinfo():
+ """
+ Returns the number of active sessions on local gateway
+ **RESTRICTED**
+ Examples:
+ curl --insecure --user admin:admin -X GET
+ http://192.168.122.69:5000/api/gatewayinfo
+ """
+ local_gw = this_host()
+ if local_gw not in config.config['gateways']:
+ return jsonify(message="Gateway {} does not exist in configuration".format(local_gw)), 400
+ num_sessions = 0
+ for target_iqn, target in config.config['targets'].items():
+ if local_gw in target['portals']:
+ num_sessions += GWTarget.get_num_sessions(target_iqn)
+ return jsonify({
+ "num_sessions": num_sessions
+ }), 200
+
+
@app.route('/api/clients/', methods=['GET'])
@requires_restricted_auth
def get_clients(target_iqn=None):
@@ -2059,6 +2129,73 @@
return jsonify(message="Client does not exist!"), 404
+@app.route('/api/clientinfo//', methods=['GET'])
+@requires_restricted_auth
+def clientinfo(target_iqn, client_iqn):
+ """
+ Returns client alias, ip_address and state for each connected portal
+ **RESTRICTED**
+ Examples:
+ curl --insecure --user admin:admin -X GET
+ http://192.168.122.69:5000/api/clientinfo/
+ iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw/iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw-client
+ """
+ if target_iqn not in config.config['targets']:
+ return jsonify(message="Target {} does not exist".format(target_iqn)), 400
+ target_config = config.config['targets'][target_iqn]
+ if client_iqn not in target_config['clients']:
+ return jsonify(message="Client {} does not exist".format(client_iqn)), 400
+ gateways = target_config['portals']
+ response = {
+ "alias": '',
+ "state": {},
+ "ip_address": []
+ }
+ for gateway in gateways.keys():
+ resp_text, resp_code = call_api([gateway],
+ '_clientinfo',
+ '{}/{}'.format(target_iqn, client_iqn),
+ http_method='get')
+ if resp_code != 200:
+ return jsonify(message="{}".format(resp_text)), resp_code
+ gateway_response = json.loads(resp_text)
+ alias = gateway_response['alias']
+ if alias:
+ response['alias'] = gateway_response['alias']
+ state = gateway_response['state']
+ if state:
+ if state not in response['state']:
+ response['state'][state] = []
+ response['state'][state].append(gateway)
+ response['ip_address'].extend(gateway_response['ip_address'])
+ response['ip_address'] = list(set(response['ip_address']))
+ return jsonify(response), 200
+
+
+@app.route('/api/_clientinfo//', methods=['GET'])
+@requires_restricted_auth
+def _clientinfo(target_iqn, client_iqn):
+ """
+ Returns client alias, ip_address and state for local gateway
+ **RESTRICTED**
+ Examples:
+ curl --insecure --user admin:admin -X GET
+ http://192.168.122.69:5000/api/_clientinfo/
+ iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw/iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw-client
+ """
+ if target_iqn not in config.config['targets']:
+ return jsonify(message="Target {} does not exist".format(target_iqn)), 400
+ target_config = config.config['targets'][target_iqn]
+ if client_iqn not in target_config['clients']:
+ return jsonify(message="Client {} does not exist".format(client_iqn)), 400
+ local_gw = this_host()
+ if local_gw not in target_config['portals']:
+ return jsonify(message="{} is not a portal of target {}".format(local_gw, target_iqn)), 400
+
+ logged_in = GWClient.get_client_info(target_iqn, client_iqn)
+ return jsonify(logged_in), 200
+
+
@app.route('/api/hostgroups/', methods=['GET'])
@requires_restricted_auth
def hostgroups(target_iqn=None):
@@ -2444,7 +2581,7 @@
return fail_msg, api.response.status_code
- return "successful", 200
+ return api.response.text if http_method == 'get' else 'successful', 200
def pre_reqs_errors():