xref: /spdk/python/spdk/spdkcli/ui_node.py (revision 1758092c7de201bc2a19abefe6c361e41d31c12d)
1#  SPDX-License-Identifier: BSD-3-Clause
2#  Copyright (C) 2018 Intel Corporation.
3#  All rights reserved.
4
5from configshell_fb import ConfigNode, ExecutionError
6from uuid import UUID
7from ..rpc.client import JSONRPCException
8import json
9
10
11def convert_bytes_to_human(size):
12    if size == 0:
13        return "%3.1f%s" % (size, "bytes")
14    if not size:
15        return ""
16    for x in ["bytes", "K", "M", "G", "T"]:
17        if size < 1024.0:
18            return "%3.1f%s" % (size, x)
19        size /= 1024.0
20
21
22class UINode(ConfigNode):
23    def __init__(self, name, parent=None, shell=None):
24        ConfigNode.__init__(self, name, parent, shell)
25
26    def refresh(self):
27        for child in self.children:
28            child.refresh()
29
30    def refresh_node(self):
31        self.refresh()
32
33    def ui_command_refresh(self):
34        self.refresh()
35
36    def ui_command_ll(self, path=None, depth=None):
37        """
38        Alias for ls.
39        """
40        self.ui_command_ls(path, depth)
41
42    def execute_command(self, command, pparams=[], kparams={}):
43        try:
44            result = ConfigNode.execute_command(self, command,
45                                                pparams, kparams)
46        except Exception as e:
47            raise e
48        else:
49            self.shell.log.debug("Command %s succeeded." % command)
50            return result
51        finally:
52            if self.shell.interactive and\
53                command in ["create", "start", "delete", "delete_all", "add_initiator",
54                            "allow_any_host", "bdev_split_create", "add_lun",
55                            "iscsi_target_node_add_pg_ig_maps", "remove_target", "add_secret",
56                            "bdev_split_delete", "delete_secret_all",
57                            "delete_initiator", "set_auth", "delete_secret",
58                            "iscsi_target_node_remove_pg_ig_maps", "load_config",
59                            "load_subsystem_config"]:
60                self.get_root().refresh()
61                self.refresh_node()
62
63
64class UIBdevs(UINode):
65    def __init__(self, parent):
66        UINode.__init__(self, "bdevs", parent)
67        self.refresh()
68
69    def refresh(self):
70        self._children = set([])
71        UIMallocBdev(self)
72        UIAIOBdev(self)
73        UILvolBdev(self)
74        UINvmeBdev(self)
75        UINullBdev(self)
76        UIErrorBdev(self)
77        UISplitBdev(self)
78        UIRbdBdev(self)
79        UIiSCSIBdev(self)
80        UIVirtioBlkBdev(self)
81        UIVirtioScsiBdev(self)
82        UIRaidBdev(self)
83        UIUringBdev(self)
84        UICompressBdev(self)
85
86
87class UILvolStores(UINode):
88    def __init__(self, parent):
89        UINode.__init__(self, "lvol_stores", parent)
90        self.refresh()
91
92    def refresh(self):
93        self._children = set([])
94        for lvs in self.get_root().bdev_lvol_get_lvstores():
95            UILvsObj(lvs, self)
96
97    def delete(self, name, uuid):
98        if name is None and uuid is None:
99            self.shell.log.error("Please specify one of the identifiers: "
100                                 "lvol store name or UUID")
101        self.get_root().bdev_lvol_delete_lvstore(lvs_name=name, uuid=uuid)
102
103    def ui_command_create(self, name, bdev_name, cluster_size=None):
104        """
105        Creates logical volume store on target bdev.
106
107        Arguments:
108        name - Friendly name to use alongside with UUID identifier.
109        bdev_name - On which bdev to create the lvol store.
110        cluster_size - Cluster size to use when creating lvol store, in bytes. Default: 4194304.
111        """
112
113        cluster_size = self.ui_eval_param(cluster_size, "number", None)
114        self.get_root().bdev_lvol_create_lvstore(lvs_name=name, bdev_name=bdev_name, cluster_sz=cluster_size)
115
116    def ui_command_delete(self, name=None, uuid=None):
117        """
118        Deletes logical volume store from configuration.
119        This will also delete all logical volume bdevs created on this lvol store!
120
121        Arguments:
122        name - Friendly name of the logical volume store to be deleted.
123        uuid - UUID number of the logical volume store to be deleted.
124        """
125        self.delete(name, uuid)
126
127    def ui_command_delete_all(self):
128        rpc_messages = ""
129        for lvs in self._children:
130            try:
131                self.delete(None, lvs.lvs.uuid)
132            except JSONRPCException as e:
133                rpc_messages += e.message
134        if rpc_messages:
135            raise JSONRPCException(rpc_messages)
136
137    def summary(self):
138        return "Lvol stores: %s" % len(self.children), None
139
140
141class UIBdev(UINode):
142    def __init__(self, name, parent):
143        UINode.__init__(self, name, parent)
144        self.refresh()
145
146    def refresh(self):
147        self._children = set([])
148        for bdev in self.get_root().bdev_get_bdevs(self.name):
149            UIBdevObj(bdev, self)
150
151    def ui_command_get_bdev_iostat(self, name=None):
152        ret = self.get_root().bdev_get_iostat(name=name)
153        self.shell.log.info(json.dumps(ret, indent=2))
154
155    def ui_command_delete_all(self):
156        """Delete all bdevs from this tree node."""
157        rpc_messages = ""
158        for bdev in self._children:
159            try:
160                self.delete(bdev.name)
161            except JSONRPCException as e:
162                rpc_messages += e.message
163        if rpc_messages:
164            raise JSONRPCException(rpc_messages)
165
166    def summary(self):
167        return "Bdevs: %d" % len(self.children), None
168
169
170class UIMallocBdev(UIBdev):
171    def __init__(self, parent):
172        UIBdev.__init__(self, "malloc", parent)
173
174    def delete(self, name):
175        self.get_root().bdev_malloc_delete(name=name)
176
177    def ui_command_create(self, size, block_size, name=None, uuid=None):
178        """
179        Construct a Malloc bdev.
180
181        Arguments:
182        size - Size in megabytes.
183        block_size - Integer, block size to use when constructing bdev.
184        name - Optional argument. Custom name to use for bdev. If not provided
185               then name will be "MallocX" where X is next available ID.
186        uuid - Optional parameter. Custom UUID to use. If empty then random
187               will be generated.
188        """
189
190        size = self.ui_eval_param(size, "number", None)
191        block_size = self.ui_eval_param(block_size, "number", None)
192        ret_name = self.get_root().create_malloc_bdev(num_blocks=size * 1024 * 1024 // block_size,
193                                                      block_size=block_size,
194                                                      name=name, uuid=uuid)
195        self.shell.log.info(ret_name)
196
197    def ui_command_delete(self, name):
198        """
199        Deletes malloc bdev from configuration.
200
201        Arguments:
202        name - Is a unique identifier of the malloc bdev to be deleted - UUID number or name alias.
203        """
204        self.delete(name)
205
206
207class UIAIOBdev(UIBdev):
208    def __init__(self, parent):
209        UIBdev.__init__(self, "aio", parent)
210
211    def delete(self, name):
212        self.get_root().bdev_aio_delete(name=name)
213
214    def ui_command_create(self, name, filename, block_size):
215        """
216        Construct an AIO bdev.
217        Backend file must exist before trying to create an AIO bdev.
218
219        Arguments:
220        name - Optional argument. Custom name to use for bdev. If not provided
221               then name will be "MallocX" where X is next available ID.
222        filename - Path to AIO backend.
223        block_size - Integer, block size to use when constructing bdev.
224        """
225
226        block_size = self.ui_eval_param(block_size, "number", None)
227        ret_name = self.get_root().bdev_aio_create(name=name,
228                                                   block_size=int(block_size),
229                                                   filename=filename)
230        self.shell.log.info(ret_name)
231
232    def ui_command_delete(self, name):
233        """
234        Deletes aio bdev from configuration.
235
236        Arguments:
237        name - Is a unique identifier of the aio bdev to be deleted - UUID number or name alias.
238        """
239        self.delete(name)
240
241
242class UILvolBdev(UIBdev):
243    def __init__(self, parent):
244        UIBdev.__init__(self, "logical_volume", parent)
245
246    def delete(self, name):
247        self.get_root().bdev_lvol_delete(name=name)
248
249    def ui_command_create(self, name, size, lvs, thin_provision=None):
250        """
251        Construct a Logical Volume bdev.
252
253        Arguments:
254        name - Friendly name to use for creating logical volume bdev.
255        size - Size in megabytes.
256        lvs - Identifier of logical volume store on which the bdev should be
257              created. Can be either a friendly name or UUID.
258        thin_provision - Whether the bdev should be thick or thin provisioned.
259              Default is False, and created bdevs are thick-provisioned.
260        """
261        uuid = None
262        lvs_name = None
263        try:
264            UUID(lvs)
265            uuid = lvs
266        except ValueError:
267            lvs_name = lvs
268
269        size = self.ui_eval_param(size, "number", None)
270        thin_provision = self.ui_eval_param(thin_provision, "bool", False)
271
272        ret_uuid = self.get_root().create_lvol_bdev(lvol_name=name, size_in_mib=size,
273                                                    lvs_name=lvs_name, uuid=uuid,
274                                                    thin_provision=thin_provision)
275        self.shell.log.info(ret_uuid)
276
277    def ui_command_delete(self, name):
278        """
279        Deletes lvol bdev from configuration.
280
281        Arguments:
282        name - Is a unique identifier of the lvol bdev to be deleted - UUID number or name alias.
283        """
284        self.delete(name)
285
286
287class UINvmeBdev(UIBdev):
288    def __init__(self, parent):
289        UIBdev.__init__(self, "nvme", parent)
290
291    def delete(self, name):
292        self.get_root().bdev_nvme_detach_controller(name=name)
293
294    def ui_command_create(self, name, trtype, traddr,
295                          adrfam=None, trsvcid=None, subnqn=None):
296        if "rdma" in trtype and None in [adrfam, trsvcid, subnqn]:
297            self.shell.log.error("Using RDMA transport type."
298                                 "Please provide arguments for adrfam, trsvcid and subnqn.")
299        ret_name = self.get_root().create_nvme_bdev(name=name, trtype=trtype,
300                                                    traddr=traddr, adrfam=adrfam,
301                                                    trsvcid=trsvcid, subnqn=subnqn)
302        self.shell.log.info(ret_name)
303
304    def ui_command_delete_all(self):
305        rpc_messages = ""
306        ctrlrs = [x.name for x in self._children]
307        ctrlrs = [x.rsplit("n", 1)[0] for x in ctrlrs]
308        ctrlrs = set(ctrlrs)
309        for ctrlr in ctrlrs:
310            try:
311                self.delete(ctrlr)
312            except JSONRPCException as e:
313                rpc_messages += e.messages
314        if rpc_messages:
315            raise JSONRPCException(rpc_messages)
316
317    def ui_command_delete(self, name):
318        """
319        Deletes NVMe controller from configuration.
320
321        Arguments:
322        name - Is a unique identifier of the NVMe controller to be deleted.
323        """
324        self.delete(name)
325
326
327class UINullBdev(UIBdev):
328    def __init__(self, parent):
329        UIBdev.__init__(self, "null", parent)
330
331    def delete(self, name):
332        self.get_root().bdev_null_delete(name=name)
333
334    def ui_command_create(self, name, size, block_size, uuid=None):
335        """
336        Construct a Null bdev.
337
338        Arguments:
339        name - Name to use for bdev.
340        size - Size in megabytes.
341        block_size - Integer, block size to use when constructing bdev.
342        uuid - Optional parameter. Custom UUID to use. If empty then random
343               will be generated.
344        """
345
346        size = self.ui_eval_param(size, "number", None)
347        block_size = self.ui_eval_param(block_size, "number", None)
348        num_blocks = size * 1024 * 1024 // block_size
349        ret_name = self.get_root().bdev_null_create(num_blocks=num_blocks,
350                                                    block_size=block_size,
351                                                    name=name, uuid=uuid)
352        self.shell.log.info(ret_name)
353
354    def ui_command_delete(self, name):
355        """
356        Deletes null bdev from configuration.
357
358        Arguments:
359        name - Is a unique identifier of the null bdev to be deleted - UUID number or name alias.
360        """
361        self.delete(name)
362
363
364class UIErrorBdev(UIBdev):
365    def __init__(self, parent):
366        UIBdev.__init__(self, "error", parent)
367
368    def delete(self, name):
369        self.get_root().bdev_error_delete(name=name)
370
371    def ui_command_create(self, base_name):
372        """
373        Construct a error injection bdev.
374
375        Arguments:
376        base_name - base bdev name on top of which error bdev will be created.
377        """
378
379        self.get_root().create_error_bdev(base_name=base_name)
380
381    def ui_command_delete(self, name):
382        """
383        Deletes error bdev from configuration.
384
385        Arguments:
386        name - Is a unique identifier of the error bdev to be deleted - UUID number or name alias.
387        """
388        self.delete(name)
389
390
391class UISplitBdev(UIBdev):
392    def __init__(self, parent):
393        UIBdev.__init__(self, "split_disk", parent)
394
395    def delete(self, name):
396        pass
397
398    def ui_command_bdev_split_create(self, base_bdev, split_count, split_size_mb=None):
399        """
400        Create split block devices from a base bdev.
401
402        Arguments:
403        base_bdev - Name of bdev to split
404        split_count -  Number of split bdevs to create
405        split_size_mb- Size of each split volume in MiB (optional)
406        """
407
408        split_count = self.ui_eval_param(split_count, "number", None)
409        split_size_mb = self.ui_eval_param(split_size_mb, "number", None)
410
411        ret_name = self.get_root().bdev_split_create(base_bdev=base_bdev,
412                                                     split_count=split_count,
413                                                     split_size_mb=split_size_mb)
414        self.shell.log.info(ret_name)
415
416    def ui_command_bdev_split_delete(self, base_bdev):
417        """Delete split block devices associated with base bdev.
418
419        Args:
420            base_bdev: name of previously split bdev
421        """
422
423        self.get_root().bdev_split_delete(base_bdev=base_bdev)
424
425
426class UIRbdBdev(UIBdev):
427    def __init__(self, parent):
428        UIBdev.__init__(self, "rbd", parent)
429
430    def delete(self, name):
431        self.get_root().bdev_rbd_delete(name=name)
432
433    def ui_command_create(self, pool_name, rbd_name, block_size, name=None):
434        block_size = self.ui_eval_param(block_size, "number", None)
435
436        ret_name = self.get_root().create_rbd_bdev(pool_name=pool_name,
437                                                   rbd_name=rbd_name,
438                                                   block_size=block_size,
439                                                   name=name)
440        self.shell.log.info(ret_name)
441
442    def ui_command_delete(self, name):
443        """
444        Deletes rbd bdev from configuration.
445
446        Arguments:
447        name - Is a unique identifier of the rbd bdev to be deleted - UUID number or name alias.
448        """
449        self.delete(name)
450
451
452class UIiSCSIBdev(UIBdev):
453    def __init__(self, parent):
454        UIBdev.__init__(self, "iscsi", parent)
455
456    def delete(self, name):
457        self.get_root().bdev_iscsi_delete(name=name)
458
459    def ui_command_create(self, name, url, initiator_iqn):
460        """
461        Create iSCSI bdev in configuration by connecting to remote
462        iSCSI target.
463
464        Arguments:
465        name - name to be used as an ID for created iSCSI bdev.
466        url - iscsi url pointing to LUN on remote iSCSI target.
467              Example: iscsi://127.0.0.1:3260/iqn.2018-06.org.spdk/0.
468        initiator_iqn - IQN to use for initiating connection with the target.
469        """
470        ret_name = self.get_root().create_iscsi_bdev(name=name,
471                                                     url=url,
472                                                     initiator_iqn=initiator_iqn)
473        self.shell.log.info(ret_name)
474
475    def ui_command_delete(self, name):
476        """
477        Deletes iSCSI bdev from configuration.
478
479        Arguments:
480        name - name of the iscsi bdev to be deleted.
481        """
482        self.delete(name)
483
484
485class UIVirtioBlkBdev(UIBdev):
486    def __init__(self, parent):
487        UIBdev.__init__(self, "virtioblk_disk", parent)
488
489    def ui_command_create(self, name, trtype, traddr,
490                          vq_count=None, vq_size=None):
491
492        vq_count = self.ui_eval_param(vq_count, "number", None)
493        vq_size = self.ui_eval_param(vq_size, "number", None)
494
495        ret = self.get_root().create_virtio_dev(name=name,
496                                                trtype=trtype,
497                                                traddr=traddr,
498                                                dev_type="blk",
499                                                vq_count=vq_count,
500                                                vq_size=vq_size)
501
502        self.shell.log.info(ret)
503
504    def ui_command_delete(self, name):
505        """
506        Deletes virtio scsi bdev from configuration.
507
508        Arguments:
509        name - Is a unique identifier of the virtio scsi bdev to be deleted - UUID number or name alias.
510        """
511        self.get_root().bdev_virtio_detach_controller(name=name)
512
513
514class UIVirtioScsiBdev(UIBdev):
515    def __init__(self, parent):
516        UIBdev.__init__(self, "virtioscsi_disk", parent)
517
518    def refresh(self):
519        self._children = set([])
520        for bdev in self.get_root().bdev_virtio_scsi_get_devices():
521            UIVirtioScsiBdevObj(bdev, self)
522
523    def ui_command_create(self, name, trtype, traddr,
524                          vq_count=None, vq_size=None):
525
526        vq_count = self.ui_eval_param(vq_count, "number", None)
527        vq_size = self.ui_eval_param(vq_size, "number", None)
528
529        ret = self.get_root().create_virtio_dev(name=name,
530                                                trtype=trtype,
531                                                traddr=traddr,
532                                                dev_type="scsi",
533                                                vq_count=vq_count,
534                                                vq_size=vq_size)
535
536        self.shell.log.info(ret)
537
538    def ui_command_delete(self, name):
539        self.get_root().bdev_virtio_detach_controller(name=name)
540
541
542class UIBdevObj(UINode):
543    def __init__(self, bdev, parent):
544        self.bdev = bdev
545        # Using bdev name also for lvol bdevs, which results in displaying
546        # UUID instead of alias. This is because alias naming convention
547        # (lvol_store_name/lvol_bdev_name) conflicts with configshell paths
548        # ("/" as separator).
549        # Solution: show lvol alias in "summary field" for now.
550        # TODO: Possible next steps:
551        # - Either change default separator in tree for smth else
552        # - or add a UI command which would be able to autocomplete
553        #   "cd" command based on objects alias and match is to the
554        #   "main" bdev name.
555        UINode.__init__(self, self.bdev.name, parent)
556
557    def ui_command_show_details(self):
558        self.shell.log.info(json.dumps(vars(self.bdev), indent=2))
559
560    def summary(self):
561        size = convert_bytes_to_human(self.bdev.block_size * self.bdev.num_blocks)
562        size = "=".join(["Size", size])
563
564        in_use = "Not claimed"
565        if bool(self.bdev.claimed):
566            in_use = "Claimed"
567
568        alias = None
569        if self.bdev.aliases:
570            alias = self.bdev.aliases[0]
571
572        info = ", ".join([_f for _f in [alias, size, in_use] if _f])
573        return info, True
574
575
576class UIVirtioScsiBdevObj(UIBdevObj):
577    def __init__(self, bdev, parent):
578        UIBdevObj.__init__(self, bdev, parent)
579        self.refresh()
580
581    def refresh(self):
582        self._children = set([])
583        for bdev in self.get_root().bdev_get_bdevs("virtio_scsi_disk"):
584            if self.bdev.name in bdev.name:
585                UIBdevObj(bdev, self)
586
587    def summary(self):
588        if "socket" in list(self.bdev.virtio.keys()):
589            info = self.bdev.virtio["socket"]
590        if "pci_address" in list(self.bdev.virtio.keys()):
591            info = self.bdev.virtio["pci_address"]
592        return info, True
593
594
595class UILvsObj(UINode):
596    def __init__(self, lvs, parent):
597        UINode.__init__(self, lvs.name, parent)
598        self.lvs = lvs
599
600    def ui_command_show_details(self):
601        self.shell.log.info(json.dumps(vars(self.lvs), indent=2))
602
603    def summary(self):
604        size = convert_bytes_to_human(self.lvs.total_data_clusters * self.lvs.cluster_size)
605        free = convert_bytes_to_human(self.lvs.free_clusters * self.lvs.cluster_size)
606        if not free:
607            free = "0"
608        size = "=".join(["Size", size])
609        free = "=".join(["Free", free])
610        info = ", ".join([str(size), str(free)])
611        return info, True
612
613
614class UIVhosts(UINode):
615    def __init__(self, parent):
616        UINode.__init__(self, "vhost", parent)
617        self.refresh()
618
619    def refresh(self):
620        self._children = set([])
621        self.get_root().list_vhost_ctrls()
622        UIVhostBlk(self)
623        UIVhostScsi(self)
624
625
626class UIVhost(UINode):
627    def __init__(self, name, parent):
628        UINode.__init__(self, name, parent)
629        self.refresh()
630
631    def ui_command_delete(self, name):
632        """
633        Delete a Vhost controller from configuration.
634
635        Arguments:
636        name - Controller name.
637        """
638        self.get_root().vhost_delete_controller(ctrlr=name)
639
640
641class UIVhostBlk(UIVhost):
642    def __init__(self, parent):
643        UIVhost.__init__(self, "block", parent)
644        self.refresh()
645
646    def refresh(self):
647        self._children = set([])
648        for ctrlr in self.get_root().vhost_get_controllers(ctrlr_type=self.name):
649            UIVhostBlkCtrlObj(ctrlr, self)
650
651    def ui_command_create(self, name, bdev, cpumask=None, readonly=False):
652        """
653        Create a Vhost BLK controller.
654
655        Arguments:
656        name - Controller name.
657        bdev - Which bdev to attach to the controller.
658        cpumask - Optional. Integer to specify mask of CPUs to use.
659                  Default: 1.
660        readonly - Whether controller should be read only or not.
661                   Default: False.
662        """
663        self.get_root().vhost_create_blk_controller(ctrlr=name,
664                                                    dev_name=bdev,
665                                                    cpumask=cpumask,
666                                                    readonly=bool(readonly))
667
668
669class UIVhostScsi(UIVhost):
670    def __init__(self, parent):
671        UIVhost.__init__(self, "scsi", parent)
672        self.refresh()
673
674    def refresh(self):
675        self._children = set([])
676        for ctrlr in self.get_root().vhost_get_controllers(ctrlr_type=self.name):
677            UIVhostScsiCtrlObj(ctrlr, self)
678
679    def ui_command_create(self, name, delay=None, cpumask=None):
680        """
681        Create a Vhost SCSI controller.
682
683        Arguments:
684        name - Controller name.
685        delay - Optional. whether delay starting controller or not.
686                Default: False.
687        cpumask - Optional. Integer to specify mask of CPUs to use.
688                  Default: 1.
689        """
690        delay = self.ui_eval_param(delay, "bool", False)
691        self.get_root().vhost_create_scsi_controller(ctrlr=name,
692                                                     delay=delay,
693                                                     cpumask=cpumask)
694
695
696class UIVhostCtrl(UINode):
697    # Base class for SCSI and BLK controllers, do not instantiate
698    def __init__(self, ctrlr, parent):
699        self.ctrlr = ctrlr
700        UINode.__init__(self, self.ctrlr.ctrlr, parent)
701        self.refresh()
702
703    def ui_command_show_details(self):
704        self.shell.log.info(json.dumps(vars(self.ctrlr), indent=2))
705
706    def ui_command_set_coalescing(self, delay_base_us, iops_threshold):
707        delay_base_us = self.ui_eval_param(delay_base_us, "number", None)
708        iops_threshold = self.ui_eval_param(iops_threshold, "number", None)
709
710        self.get_root().vhost_controller_set_coalescing(ctrlr=self.ctrlr.ctrlr,
711                                                        delay_base_us=delay_base_us,
712                                                        iops_threshold=iops_threshold)
713
714
715class UIVhostScsiCtrlObj(UIVhostCtrl):
716    def refresh(self):
717        self._children = set([])
718        for lun in self.ctrlr.backend_specific["scsi"]:
719            UIVhostTargetObj(lun, self)
720
721    def ui_command_start(self, name):
722        """
723        Start a Vhost SCSI controller.
724
725        Arguments:
726        name - Controller name.
727        """
728        self.get_root().vhost_start_scsi_controller(ctrlr=name)
729
730    def ui_command_remove_target(self, target_num):
731        """
732        Remove target node from SCSI controller.
733
734        Arguments:
735        target_num - Integer identifier of target node to delete.
736        """
737        self.get_root().vhost_scsi_controller_remove_target(ctrlr=self.ctrlr.ctrlr,
738                                                            scsi_target_num=int(target_num))
739        for ctrlr in self.get_root().vhost_get_controllers(ctrlr_type="scsi"):
740            if ctrlr.ctrlr == self.ctrlr.ctrlr:
741                self.ctrlr = ctrlr
742
743    def ui_command_add_lun(self, target_num, bdev_name):
744        """
745        Add LUN to SCSI target node.
746        Currently only one LUN (which is LUN ID 0) per target is supported.
747        Adding LUN to not existing target node will create that node.
748
749        Arguments:
750        target_num - Integer identifier of target node to modify.
751        bdev - Which bdev to add as LUN.
752        """
753        self.get_root().vhost_scsi_controller_add_target(ctrlr=self.ctrlr.ctrlr,
754                                                         scsi_target_num=int(target_num),
755                                                         bdev_name=bdev_name)
756        for ctrlr in self.get_root().vhost_get_controllers(ctrlr_type="scsi"):
757            if ctrlr.ctrlr == self.ctrlr.ctrlr:
758                self.ctrlr = ctrlr
759
760    def summary(self):
761        info = self.ctrlr.socket
762        return info, True
763
764
765class UIVhostBlkCtrlObj(UIVhostCtrl):
766    def refresh(self):
767        self._children = set([])
768        UIVhostLunDevObj(self.ctrlr.backend_specific["block"]["bdev"], self)
769
770    def summary(self):
771        ro = None
772        if self.ctrlr.backend_specific["block"]["readonly"]:
773            ro = "Readonly"
774        info = ", ".join([_f for _f in [self.ctrlr.socket, ro] if _f])
775        return info, True
776
777
778class UIVhostTargetObj(UINode):
779    def __init__(self, target, parent):
780        self.target = target
781        # Next line: configshell does not allow paths with spaces.
782        UINode.__init__(self, target["target_name"].replace(" ", "_"), parent)
783        self.refresh()
784
785    def refresh(self):
786        self._children = set([])
787        for target in self.target["luns"]:
788            UIVhostLunDevObj(target["bdev_name"], self)
789
790    def ui_command_show_details(self):
791        self.shell.log.info(json.dumps(self.target, indent=2))
792
793    def summary(self):
794        luns = "LUNs: %s" % len(self.target["luns"])
795        id = "TargetID: %s" % self.target["scsi_dev_num"]
796        info = ",".join([luns, id])
797        return info, True
798
799
800class UIVhostLunDevObj(UINode):
801    def __init__(self, name, parent):
802        UINode.__init__(self, name, parent)
803
804
805class UIRaidBdev(UIBdev):
806    def __init__(self, parent):
807        UIBdev.__init__(self, "raid_volume", parent)
808
809    def delete(self, name):
810        self.get_root().bdev_raid_delete(name=name)
811
812    def ui_command_create(self, name, raid_level, base_bdevs, strip_size_kb):
813        """
814        Creates a raid bdev of the provided base_bdevs
815
816        Arguments:
817        name - raid bdev name
818        raid_level - raid level, supported values 0
819        base_bdevs - base bdevs name, whitespace separated list in quotes
820        strip_size_kb - strip size of raid bdev in KB, supported values like 8, 16, 32, 64, 128, 256, etc
821        """
822        base_bdevs_array = []
823        for u in base_bdevs.strip().split(" "):
824            base_bdevs_array.append(u)
825
826        strip_size_kb = self.ui_eval_param(strip_size_kb, "number", None)
827
828        ret_name = self.get_root().bdev_raid_create(name=name,
829                                                    raid_level=raid_level,
830                                                    base_bdevs=base_bdevs_array,
831                                                    strip_size_kb=strip_size_kb)
832        self.shell.log.info(ret_name)
833
834    def ui_command_delete(self, name):
835        """
836        Deletes this raid bdev object
837
838        Arguments:
839        name - raid bdev name
840        """
841        self.delete(name)
842
843
844class UIUringBdev(UIBdev):
845    def __init__(self, parent):
846        UIBdev.__init__(self, "uring", parent)
847
848    def delete(self, name):
849        self.get_root().bdev_uring_delete(name=name)
850
851    def ui_command_create(self, filename, name, block_size, uuid=None):
852        """
853        Construct a uring bdev.
854
855        Arguments:
856        filename - Path to device or file.
857        name - Name to use for bdev.
858        block_size - Integer, block size to use when constructing bdev.
859        uuid: UUID of block device (optional)
860        """
861
862        block_size = self.ui_eval_param(block_size, "number", None)
863        ret_name = self.get_root().bdev_uring_create(filename=filename,
864                                                     name=name,
865                                                     block_size=int(block_size),
866                                                     uuid=uuid)
867        self.shell.log.info(ret_name)
868
869    def ui_command_delete(self, name):
870        """
871        Deletes a uring bdev.
872
873        Arguments:
874        name - uring bdev name
875        """
876        self.delete(name)
877
878
879class UICompressBdev(UIBdev):
880    def __init__(self, parent):
881        UIBdev.__init__(self, "compress", parent)
882
883    def delete(self, name):
884        self.get_root().bdev_compress_delete(name=name)
885
886    def ui_command_create(self, base_bdev_name, pm_path, lb_size=None, comp_algo=None, comp_level=None):
887        """
888        Construct a compress bdev.
889
890        Arguments:
891        base_bdev_name - Name of the base bdev.
892        pm_path - Path to persistent memory.
893        lb_size - Optional argument. Integer, compressed vol logical block size (optional, if used must be 512 or 4096).
894        comp_algo - Optional argument. Compression algorithm, (deflate, lz4). Default is deflate.
895        comp_level - Optional argument. Integer, compression algorithm level. if algo == deflate, level ranges from 0 to 3.
896                     if algo == lz4, level ranges from 1 to 65537
897        """
898
899        lb_size = self.ui_eval_param(lb_size, "number", None)
900        comp_level = self.ui_eval_param(comp_level, "number", None)
901        ret_name = self.get_root().create_compress_bdev(base_bdev_name=base_bdev_name,
902                                                        pm_path=pm_path,
903                                                        lb_size=lb_size,
904                                                        comp_algo=comp_algo,
905                                                        comp_level=comp_level)
906
907        self.shell.log.info(ret_name)
908
909    def ui_command_delete(self, name):
910        """
911        Deletes compress bdev from configuration.
912
913        Arguments:
914        name - Is a unique identifier of the compress bdev to be deleted - UUID number or name alias.
915        """
916        self.delete(name)
917