xref: /spdk/python/spdk/spdkcli/ui_node_iscsi.py (revision 0098e636761237b77c12c30c2408263a5d2260cc)
1from configshell_fb import ExecutionError
2from ..rpc.client import JSONRPCException
3from .ui_node import UINode
4
5
6class UIISCSI(UINode):
7    def __init__(self, parent):
8        UINode.__init__(self, "iscsi", parent)
9        self.refresh()
10
11    def refresh(self):
12        self._children = set([])
13        UIISCSIDevices(self)
14        UIPortalGroups(self)
15        UIInitiatorGroups(self)
16        UIISCSIConnections(self)
17        UIISCSIAuthGroups(self)
18        UIISCSIGlobalParams(self)
19
20
21class UIISCSIGlobalParams(UINode):
22    def __init__(self, parent):
23        UINode.__init__(self, "global_params", parent)
24        self.refresh()
25
26    def refresh(self):
27        self._children = set([])
28        iscsi_global_params = self.get_root().iscsi_get_options()
29        if not iscsi_global_params:
30            return
31        for param, val in iscsi_global_params.items():
32            UIISCSIGlobalParam("%s: %s" % (param, val), self)
33
34    def ui_command_set_auth(self, g=None, d=None, r=None, m=None):
35        """Set CHAP authentication for discovery service.
36
37        Optional arguments:
38            g = chap_group: Authentication group ID for discovery session
39            d = disable_chap: CHAP for discovery session should be disabled
40            r = require_chap: CHAP for discovery session should be required
41            m = mutual_chap: CHAP for discovery session should be mutual
42        """
43        chap_group = self.ui_eval_param(g, "number", None)
44        disable_chap = self.ui_eval_param(d, "bool", None)
45        require_chap = self.ui_eval_param(r, "bool", None)
46        mutual_chap = self.ui_eval_param(m, "bool", None)
47        self.get_root().iscsi_set_discovery_auth(
48            chap_group=chap_group, disable_chap=disable_chap,
49            require_chap=require_chap, mutual_chap=mutual_chap)
50
51
52class UIISCSIGlobalParam(UINode):
53    def __init__(self, param, parent):
54        UINode.__init__(self, param, parent)
55
56
57class UIISCSIDevices(UINode):
58    def __init__(self, parent):
59        UINode.__init__(self, "target_nodes", parent)
60        self.scsi_devices = list()
61        self.refresh()
62
63    def refresh(self):
64        self._children = set([])
65        self.target_nodes = list(self.get_root().iscsi_get_target_nodes())
66        self.scsi_devices = list(self.get_root().scsi_get_devices())
67        for device in self.scsi_devices:
68            for node in self.target_nodes:
69                if hasattr(device, "device_name") and node['name'] \
70                        == device.device_name:
71                    UIISCSIDevice(device, node, self)
72
73    def delete(self, name):
74        self.get_root().iscsi_delete_target_node(target_node_name=name)
75
76    def ui_command_create(self, name, alias_name, bdev_name_id_pairs,
77                          pg_ig_mappings, queue_depth, g=None, d=None, r=None,
78                          m=None, h=None, t=None):
79        """Create target node
80
81        Positional args:
82           name: Target node name (ASCII)
83           alias_name: Target node alias name (ASCII)
84           bdev_name_id_pairs: List of bdev_name_id_pairs
85           pg_ig_mappings: List of pg_ig_mappings
86           queue_depth: Desired target queue depth
87        Optional args:
88           g = chap_group: Authentication group ID for this target node
89           d = disable_chap: CHAP authentication should be disabled for this target node
90           r = require_chap: CHAP authentication should be required for this target node
91           m = mutual_chap: CHAP authentication should be mutual/bidirectional
92           h = header_digest: Header Digest should be required for this target node
93           t = data_digest: Data Digest should be required for this target node
94        """
95        luns = []
96        print("bdev_name_id_pairs: %s" % bdev_name_id_pairs)
97        print("pg_ig_mappings: %s" % pg_ig_mappings)
98        for u in bdev_name_id_pairs.strip().split(" "):
99            bdev_name, lun_id = u.split(":")
100            luns.append({"bdev_name": bdev_name, "lun_id": int(lun_id)})
101        pg_ig_maps = []
102        for u in pg_ig_mappings.strip().split(" "):
103            pg, ig = u.split(":")
104            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
105        queue_depth = self.ui_eval_param(queue_depth, "number", None)
106        chap_group = self.ui_eval_param(g, "number", None)
107        disable_chap = self.ui_eval_param(d, "bool", None)
108        require_chap = self.ui_eval_param(r, "bool", None)
109        mutual_chap = self.ui_eval_param(m, "bool", None)
110        header_digest = self.ui_eval_param(h, "bool", None)
111        data_digest = self.ui_eval_param(t, "bool", None)
112        self.get_root().iscsi_create_target_node(
113            name=name, alias_name=alias_name, luns=luns,
114            pg_ig_maps=pg_ig_maps, queue_depth=queue_depth,
115            chap_group=chap_group, disable_chap=disable_chap,
116            require_chap=require_chap, mutual_chap=mutual_chap,
117            header_digest=header_digest, data_digest=data_digest)
118
119    def ui_command_delete(self, name=None):
120        """Delete a target node. If name is not specified delete all target nodes.
121
122        Arguments:
123           name - Target node name.
124        """
125        self.delete(name)
126
127    def ui_command_delete_all(self):
128        """Delete all target nodes"""
129        rpc_messages = ""
130        for device in self.scsi_devices:
131            try:
132                self.delete(device.device_name)
133            except JSONRPCException as e:
134                rpc_messages += e.message
135        if rpc_messages:
136            raise JSONRPCException(rpc_messages)
137
138    def ui_command_add_lun(self, name, bdev_name, lun_id=None):
139        """Add lun to the target node.
140
141        Required args:
142           name: Target node name (ASCII)
143           bdev_name: bdev name
144        Positional args:
145           lun_id: LUN ID (integer >= 0)
146        """
147        if lun_id:
148            lun_id = self.ui_eval_param(lun_id, "number", None)
149        self.get_root().iscsi_target_node_add_lun(
150            name=name, bdev_name=bdev_name, lun_id=lun_id)
151
152    def summary(self):
153        count = 0
154        for device in self.scsi_devices:
155            for node in self.target_nodes:
156                if hasattr(device, "device_name") and node['name'] \
157                        == device.device_name:
158                    count = count + 1
159        return "Target nodes: %d" % count, None
160
161
162class UIISCSIDevice(UINode):
163    def __init__(self, device, target, parent):
164        UINode.__init__(self, device.device_name, parent)
165        self.device = device
166        self.target = target
167        self.refresh()
168
169    def ui_command_set_auth(self, g=None, d=None, r=None, m=None):
170        """Set CHAP authentication for the target node.
171
172        Optionals args:
173           g = chap_group: Authentication group ID for this target node
174           d = disable_chap: CHAP authentication should be disabled for this target node
175           r = require_chap: CHAP authentication should be required for this target node
176           m = mutual_chap: CHAP authentication should be mutual/bidirectional
177        """
178        chap_group = self.ui_eval_param(g, "number", None)
179        disable_chap = self.ui_eval_param(d, "bool", None)
180        require_chap = self.ui_eval_param(r, "bool", None)
181        mutual_chap = self.ui_eval_param(m, "bool", None)
182        self.get_root().iscsi_target_node_set_auth(
183            name=self.device.device_name, chap_group=chap_group,
184            disable_chap=disable_chap,
185            require_chap=require_chap, mutual_chap=mutual_chap)
186
187    def ui_command_iscsi_target_node_add_pg_ig_maps(self, pg_ig_mappings):
188        """Add PG-IG maps to the target node.
189
190        Args:
191           pg_ig_maps: List of pg_ig_mappings, e.g. pg_tag:ig_tag pg_tag2:ig_tag2
192        """
193        pg_ig_maps = []
194        for u in pg_ig_mappings.strip().split(" "):
195            pg, ig = u.split(":")
196            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
197        self.get_root().iscsi_target_node_add_pg_ig_maps(
198            pg_ig_maps=pg_ig_maps, name=self.device.device_name)
199
200    def ui_command_iscsi_target_node_remove_pg_ig_maps(self, pg_ig_mappings):
201        """Remove PG-IG maps from the target node.
202
203        Args:
204           pg_ig_maps: List of pg_ig_mappings, e.g. pg_tag:ig_tag pg_tag2:ig_tag2
205        """
206        pg_ig_maps = []
207        for u in pg_ig_mappings.strip().split(" "):
208            pg, ig = u.split(":")
209            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
210        self.get_root().iscsi_target_node_remove_pg_ig_maps(
211            pg_ig_maps=pg_ig_maps, name=self.device.device_name)
212
213    def refresh(self):
214        self._children = set([])
215        UIISCSILuns(self.target['luns'], self)
216        UIISCSIPgIgMaps(self.target['pg_ig_maps'], self)
217        auths = {"disable_chap": self.target["disable_chap"],
218                 "require_chap": self.target["require_chap"],
219                 "mutual_chap": self.target["mutual_chap"],
220                 "chap_group": self.target["chap_group"],
221                 "data_digest": self.target["data_digest"]}
222        UIISCSIAuth(auths, self)
223
224    def summary(self):
225        return "Id: %s, QueueDepth: %s" % (self.device.id,
226                                           self.target['queue_depth']), None
227
228
229class UIISCSIAuth(UINode):
230    def __init__(self, auths, parent):
231        UINode.__init__(self, "auths", parent)
232        self.auths = auths
233        self.refresh()
234
235    def summary(self):
236        return "disable_chap: %s, require_chap: %s, mutual_chap: %s, chap_group: %s" % (
237            self.auths['disable_chap'], self.auths['require_chap'],
238            self.auths['mutual_chap'], self.auths['chap_group']), None
239
240
241class UIISCSILuns(UINode):
242    def __init__(self, luns, parent):
243        UINode.__init__(self, "luns", parent)
244        self.luns = luns
245        self.refresh()
246
247    def refresh(self):
248        self._children = set([])
249        for lun in self.luns:
250            UIISCSILun(lun, self)
251
252    def summary(self):
253        return "Luns: %d" % len(self.luns), None
254
255
256class UIISCSILun(UINode):
257    def __init__(self, lun, parent):
258        UINode.__init__(self, "lun %s" % lun['lun_id'], parent)
259        self.lun = lun
260        self.refresh()
261
262    def summary(self):
263        return "%s" % self.lun['bdev_name'], None
264
265
266class UIISCSIPgIgMaps(UINode):
267    def __init__(self, pg_ig_maps, parent):
268        UINode.__init__(self, "pg_ig_maps", parent)
269        self.pg_ig_maps = pg_ig_maps
270        self.refresh()
271
272    def refresh(self):
273        self._children = set([])
274        for pg_ig in self.pg_ig_maps:
275            UIISCSIPgIg(pg_ig, self)
276
277    def summary(self):
278        return "Pg_ig_maps: %d" % len(self.pg_ig_maps), None
279
280
281class UIISCSIPgIg(UINode):
282    def __init__(self, pg_ig, parent):
283        UINode.__init__(self, "portal_group%s - initiator_group%s" %
284                        (pg_ig['pg_tag'], pg_ig['ig_tag']), parent)
285        self.pg_ig = pg_ig
286        self.refresh()
287
288
289class UIPortalGroups(UINode):
290    def __init__(self, parent):
291        UINode.__init__(self, "portal_groups", parent)
292        self.refresh()
293
294    def delete(self, tag):
295        self.get_root().iscsi_delete_portal_group(tag=tag)
296
297    def ui_command_create(self, tag, portal_list):
298        """Add a portal group.
299
300        Args:
301           portals: List of portals e.g. ip:port ip2:port2
302           tag: Portal group tag (unique, integer > 0)
303        """
304        portals = []
305        for portal in portal_list.strip().split(" "):
306            host = portal
307            cpumask = None
308            if "@" in portal:
309                host, cpumask = portal.split("@")
310            if ":" not in host:
311                raise ExecutionError("Incorrect format of portal group. Port is missing."
312                                     "Use 'help create' to see the command syntax.")
313            host, port = host.rsplit(":", -1)
314            portals.append({'host': host, 'port': port})
315            if cpumask:
316                print("WARNING: Specifying a CPU mask for portal groups is no longer supported. Ignoring.")
317        tag = self.ui_eval_param(tag, "number", None)
318        self.get_root().construct_portal_group(tag=tag, portals=portals, private=None, wait=None)
319
320    def ui_command_delete(self, tag):
321        """Delete a portal group with given tag (unique, integer > 0))"""
322        tag = self.ui_eval_param(tag, "number", None)
323        self.delete(tag)
324
325    def ui_command_delete_all(self):
326        """Delete all portal groups"""
327        rpc_messages = ""
328        for pg in self.pgs:
329            try:
330                self.delete(pg.tag)
331            except JSONRPCException as e:
332                rpc_messages += e.message
333        if rpc_messages:
334            raise JSONRPCException(rpc_messages)
335
336    def refresh(self):
337        self._children = set([])
338        self.pgs = list(self.get_root().iscsi_get_portal_groups())
339        for pg in self.pgs:
340            try:
341                UIPortalGroup(pg, self)
342            except JSONRPCException as e:
343                self.shell.log.error(e.message)
344
345    def summary(self):
346        return "Portal groups: %d" % len(self.pgs), None
347
348
349class UIPortalGroup(UINode):
350    def __init__(self, pg, parent):
351        UINode.__init__(self, "portal_group%s" % pg.tag, parent)
352        self.pg = pg
353        self.refresh()
354
355    def refresh(self):
356        self._children = set([])
357        for portal in self.pg.portals:
358            UIPortal(portal['host'], portal['port'], self)
359
360    def summary(self):
361        return "Portals: %d" % len(self.pg.portals), None
362
363
364class UIPortal(UINode):
365    def __init__(self, host, port, parent):
366        UINode.__init__(self, "host=%s, port=%s" % (
367            host, port), parent)
368        self.refresh()
369
370
371class UIInitiatorGroups(UINode):
372    def __init__(self, parent):
373        UINode.__init__(self, "initiator_groups", parent)
374        self.refresh()
375
376    def delete(self, tag):
377        self.get_root().iscsi_delete_initiator_group(tag=tag)
378
379    def ui_command_create(self, tag, initiator_list, netmask_list):
380        """Add an initiator group.
381
382        Args:
383           tag: Initiator group tag (unique, integer > 0)
384           initiators: List of initiator hostnames or IP addresses
385                       separated with whitespaces, e.g. 127.0.0.1 192.168.200.100
386           netmasks: List of initiator netmasks separated with whitespaces,
387                     e.g. 255.255.0.0 255.248.0.0
388        """
389        tag = self.ui_eval_param(tag, "number", None)
390        self.get_root().construct_initiator_group(
391            tag=tag, initiators=initiator_list.split(" "),
392            netmasks=netmask_list.split(" "))
393
394    def ui_command_delete(self, tag):
395        """Delete an initiator group.
396
397        Args:
398           tag: Initiator group tag (unique, integer > 0)
399        """
400        tag = self.ui_eval_param(tag, "number", None)
401        self.delete(tag)
402
403    def ui_command_delete_all(self):
404        """Delete all initiator groups"""
405        rpc_messages = ""
406        for ig in self.igs:
407            try:
408                self.delete(ig.tag)
409            except JSONRPCException as e:
410                rpc_messages += e.message
411        if rpc_messages:
412            raise JSONRPCException(rpc_messages)
413
414    def ui_command_add_initiator(self, tag, initiators, netmasks):
415        """Add initiators to an existing initiator group.
416
417        Args:
418           tag: Initiator group tag (unique, integer > 0)
419           initiators: List of initiator hostnames or IP addresses,
420                       e.g. 127.0.0.1 192.168.200.100
421           netmasks: List of initiator netmasks,
422                     e.g. 255.255.0.0 255.248.0.0
423        """
424        tag = self.ui_eval_param(tag, "number", None)
425        self.get_root().iscsi_initiator_group_add_initiators(
426            tag=tag, initiators=initiators.split(" "),
427            netmasks=netmasks.split(" "))
428
429    def ui_command_delete_initiator(self, tag, initiators=None, netmasks=None):
430        """Delete initiators from an existing initiator group.
431
432        Args:
433           tag: Initiator group tag (unique, integer > 0)
434           initiators: List of initiator hostnames or IP addresses, e.g. 127.0.0.1 192.168.200.100
435           netmasks: List of initiator netmasks, e.g. 255.255.0.0 255.248.0.0
436        """
437        tag = self.ui_eval_param(tag, "number", None)
438        if initiators:
439            initiators = initiators.split(" ")
440        if netmasks:
441            netmasks = netmasks.split(" ")
442        self.get_root().iscsi_initiator_group_remove_initiators(
443            tag=tag, initiators=initiators,
444            netmasks=netmasks)
445
446    def refresh(self):
447        self._children = set([])
448        self.igs = list(self.get_root().iscsi_get_initiator_groups())
449        for ig in self.igs:
450            UIInitiatorGroup(ig, self)
451
452    def summary(self):
453        return "Initiator groups: %d" % len(self.igs), None
454
455
456class UIInitiatorGroup(UINode):
457    def __init__(self, ig, parent):
458        UINode.__init__(self, "initiator_group%s" % ig.tag, parent)
459        self.ig = ig
460        self.refresh()
461
462    def refresh(self):
463        self._children = set([])
464        for initiator, netmask in zip(self.ig.initiators, self.ig.netmasks):
465            UIInitiator(initiator, netmask, self)
466
467    def summary(self):
468        return "Initiators: %d" % len(self.ig.initiators), None
469
470
471class UIInitiator(UINode):
472    def __init__(self, initiator, netmask, parent):
473        UINode.__init__(self, "hostname=%s, netmask=%s" % (initiator, netmask), parent)
474        self.refresh()
475
476
477class UIISCSIConnections(UINode):
478    def __init__(self, parent):
479        UINode.__init__(self, "iscsi_connections", parent)
480        self.refresh()
481
482    def refresh(self):
483        self._children = set([])
484        self.iscsicons = list(self.get_root().iscsi_get_connections())
485        for ic in self.iscsicons:
486            UIISCSIConnection(ic, self)
487
488    def summary(self):
489        return "Connections: %d" % len(self.iscsicons), None
490
491
492class UIISCSIConnection(UINode):
493    def __init__(self, ic, parent):
494        UINode.__init__(self, "%s" % ic['id'], parent)
495        self.ic = ic
496        self.refresh()
497
498    def refresh(self):
499        self._children = set([])
500        for key, val in self.ic.items():
501            if key == "id":
502                continue
503            UIISCSIConnectionDetails("%s: %s" % (key, val), self)
504
505
506class UIISCSIConnectionDetails(UINode):
507    def __init__(self, info, parent):
508        UINode.__init__(self, "%s" % info, parent)
509        self.refresh()
510
511
512class UIISCSIAuthGroups(UINode):
513    def __init__(self, parent):
514        UINode.__init__(self, "auth_groups", parent)
515        self.refresh()
516
517    def refresh(self):
518        self._children = set([])
519        self.iscsi_auth_groups = list(self.get_root().iscsi_get_auth_groups())
520        if self.iscsi_auth_groups is None:
521            self.iscsi_auth_groups = []
522        for ag in self.iscsi_auth_groups:
523            UIISCSIAuthGroup(ag, self)
524
525    def delete(self, tag):
526        self.get_root().iscsi_delete_auth_group(tag=tag)
527
528    def delete_secret(self, tag, user):
529        self.get_root().iscsi_auth_group_remove_secret(
530            tag=tag, user=user)
531
532    def ui_command_create(self, tag, secrets=None):
533        """Add authentication group for CHAP authentication.
534
535        Args:
536           tag: Authentication group tag (unique, integer > 0).
537        Optional args:
538           secrets: Array of secrets objects separated by comma sign,
539                    e.g. user:test secret:test muser:mutual_test msecret:mutual_test
540        """
541        tag = self.ui_eval_param(tag, "number", None)
542        if secrets:
543            secrets = [dict(u.split(":") for u in a.split(" "))
544                       for a in secrets.split(",")]
545        self.get_root().iscsi_create_auth_group(tag=tag, secrets=secrets)
546
547    def ui_command_delete(self, tag):
548        """Delete an authentication group.
549
550        Args:
551           tag: Authentication group tag (unique, integer > 0)
552        """
553        tag = self.ui_eval_param(tag, "number", None)
554        self.delete(tag)
555
556    def ui_command_delete_all(self):
557        """Delete all authentication groups."""
558        rpc_messages = ""
559        for iscsi_auth_group in self.iscsi_auth_groups:
560            try:
561                self.delete(iscsi_auth_group['tag'])
562            except JSONRPCException as e:
563                rpc_messages += e.message
564        if rpc_messages:
565            raise JSONRPCException(rpc_messages)
566
567    def ui_command_add_secret(self, tag, user, secret,
568                              muser=None, msecret=None):
569        """Add a secret to an authentication group.
570
571        Args:
572           tag: Authentication group tag (unique, integer > 0)
573           user: User name for one-way CHAP authentication
574           secret: Secret for one-way CHAP authentication
575        Optional args:
576           muser: User name for mutual CHAP authentication
577           msecret: Secret for mutual CHAP authentication
578        """
579        tag = self.ui_eval_param(tag, "number", None)
580        self.get_root().iscsi_auth_group_add_secret(
581            tag=tag, user=user, secret=secret,
582            muser=muser, msecret=msecret)
583
584    def ui_command_delete_secret(self, tag, user):
585        """Delete a secret from an authentication group.
586
587        Args:
588           tag: Authentication group tag (unique, integer > 0)
589           user: User name for one-way CHAP authentication
590        """
591        tag = self.ui_eval_param(tag, "number", None)
592        self.delete_secret(tag, user)
593
594    def ui_command_delete_secret_all(self, tag):
595        """Delete all secrets from an authentication group.
596
597        Args:
598           tag: Authentication group tag (unique, integer > 0)
599        """
600        rpc_messages = ""
601        tag = self.ui_eval_param(tag, "number", None)
602        for ag in self.iscsi_auth_groups:
603            if ag['tag'] == tag:
604                for secret in ag['secrets']:
605                    try:
606                        self.delete_secret(tag, secret['user'])
607                    except JSONRPCException as e:
608                        rpc_messages += e.message
609        if rpc_messages:
610            raise JSONRPCException(rpc_messages)
611
612    def summary(self):
613        return "Groups: %s" % len(self.iscsi_auth_groups), None
614
615
616class UIISCSIAuthGroup(UINode):
617    def __init__(self, ag, parent):
618        UINode.__init__(self, "group" + str(ag['tag']), parent)
619        self.ag = ag
620        self.refresh()
621
622    def refresh(self):
623        self._children = set([])
624        for secret in self.ag['secrets']:
625            UISCSIAuthSecret(secret, self)
626
627    def summary(self):
628        return "Secrets: %s" % len(self.ag['secrets']), None
629
630
631class UISCSIAuthSecret(UINode):
632    def __init__(self, secret, parent):
633        info_list = ["%s=%s" % (key, val)
634                     for key, val in secret.items()]
635        info_list.sort(reverse=True)
636        info = ", ".join(info_list)
637        UINode.__init__(self, info, parent)
638        self.secret = secret
639        self.refresh()
640