xref: /spdk/python/spdk/spdkcli/ui_root.py (revision 1758092c7de201bc2a19abefe6c361e41d31c12d)
1#  SPDX-License-Identifier: BSD-3-Clause
2#  Copyright (C) 2018 Intel Corporation.
3#  All rights reserved.
4
5from .ui_node import UINode, UIBdevs, UILvolStores, UIVhosts
6from .ui_node_nvmf import UINVMf
7from .ui_node_iscsi import UIISCSI
8from .. import rpc
9from functools import wraps
10
11
12class UIRoot(UINode):
13    """
14    Root node for CLI menu tree structure. Refreshes running config on startup.
15    """
16    def __init__(self, client, shell):
17        UINode.__init__(self, "/", shell=shell)
18        self.current_bdevs = []
19        self.current_lvol_stores = []
20        self.current_vhost_ctrls = []
21        self.current_nvmf_transports = []
22        self.current_nvmf_subsystems = []
23        self.current_nvmf_referrals = []
24        self.set_rpc_target(client)
25        self.verbose = False
26        self.is_init = self.check_init()
27        self.methods = []
28
29    def refresh(self):
30        self.methods = self.rpc_get_methods(current=True)
31        if self.is_init is False:
32            methods = "\n".join(self.methods)
33            self.shell.log.warning("SPDK Application is not yet initialized.\n"
34                                   "Please initialize subsystems with framework_start_init command.\n"
35                                   "List of available commands in current state:\n"
36                                   "%s" % methods)
37        else:
38            # Pass because we'd like to build main tree structure for "ls"
39            # even if state is uninitialized
40            pass
41
42        self._children = set([])
43        UIBdevs(self)
44        UILvolStores(self)
45        if self.has_subsystem("vhost_scsi") or self.has_subsystem("vhost_blk"):
46            UIVhosts(self)
47        if self.has_subsystem("nvmf"):
48            UINVMf(self)
49        if self.has_subsystem("iscsi"):
50            UIISCSI(self)
51
52    def set_rpc_target(self, client):
53        self.client = client
54
55    def print_array(self, a):
56        return " ".join(a)
57
58    def verbose(f):
59        # For any configuration calls (create, delete, construct, etc.)
60        # Check if verbose option is to be used and set appropriately.
61        # Do not use for "get_*" methods so that output is not
62        # flooded.
63        def w(self, **kwargs):
64            self.client.log_set_level("INFO" if self.verbose else "ERROR")
65            r = f(self, **kwargs)
66            self.client.log_set_level("ERROR")
67            return r
68        return w
69
70    def is_method_available(f):
71        # Check if method f is available for given spdk target
72        def w(self, **kwargs):
73            if f.__name__ in self.methods:
74                r = f(self, **kwargs)
75                return r
76            # If given method is not available return empty list
77            # similar to real get_* like rpc
78            return []
79        return w
80
81    def ui_command_framework_start_init(self):
82        if rpc.framework_start_init(self.client):
83            self.is_init = True
84            self.refresh()
85
86    def ui_command_load_config(self, filename):
87        with open(filename, "r") as fd:
88            rpc.load_config(self.client, fd)
89
90    def ui_command_load_subsystem_config(self, filename):
91        with open(filename, "r") as fd:
92            rpc.load_subsystem_config(self.client, fd)
93
94    def ui_command_save_config(self, filename, indent=2):
95        with open(filename, "w") as fd:
96            rpc.save_config(self.client, fd, indent)
97
98    def ui_command_save_subsystem_config(self, filename, subsystem, indent=2):
99        with open(filename, "w") as fd:
100            rpc.save_subsystem_config(self.client, fd, indent, subsystem)
101
102    def rpc_get_methods(self, current=False):
103        return rpc.rpc_get_methods(self.client, current=current)
104
105    def check_init(self):
106        return "framework_start_init" not in self.rpc_get_methods(current=True)
107
108    def bdev_get_bdevs(self, bdev_type):
109        if self.is_init:
110            self.current_bdevs = rpc.bdev.bdev_get_bdevs(self.client)
111            # Following replace needs to be done in order for some of the bdev
112            # listings to work: logical volumes, split disk.
113            # For example logical volumes: listing in menu is "Logical_Volume"
114            # (cannot have space), but the product name in SPDK is "Logical Volume"
115            bdev_type = bdev_type.replace("_", " ")
116            for bdev in [x for x in self.current_bdevs if bdev_type in x["product_name"].lower()]:
117                test = Bdev(bdev)
118                yield test
119
120    def bdev_get_iostat(self, **kwargs):
121        return rpc.bdev.bdev_get_iostat(self.client, **kwargs)
122
123    @verbose
124    def bdev_split_create(self, **kwargs):
125        response = rpc.bdev.bdev_split_create(self.client, **kwargs)
126        return self.print_array(response)
127
128    @verbose
129    def bdev_split_delete(self, **kwargs):
130        rpc.bdev.bdev_split_delete(self.client, **kwargs)
131
132    @verbose
133    def create_malloc_bdev(self, **kwargs):
134        response = rpc.bdev.bdev_malloc_create(self.client, **kwargs)
135        return response
136
137    @verbose
138    def bdev_malloc_delete(self, **kwargs):
139        rpc.bdev.bdev_malloc_delete(self.client, **kwargs)
140
141    @verbose
142    def create_iscsi_bdev(self, **kwargs):
143        response = rpc.bdev.bdev_iscsi_create(self.client, **kwargs)
144        return response
145
146    @verbose
147    def bdev_iscsi_delete(self, **kwargs):
148        rpc.bdev.bdev_iscsi_delete(self.client, **kwargs)
149
150    @verbose
151    def bdev_aio_create(self, **kwargs):
152        response = rpc.bdev.bdev_aio_create(self.client, **kwargs)
153        return response
154
155    @verbose
156    def bdev_aio_delete(self, **kwargs):
157        rpc.bdev.bdev_aio_delete(self.client, **kwargs)
158
159    @verbose
160    def create_lvol_bdev(self, **kwargs):
161        response = rpc.lvol.bdev_lvol_create(self.client, **kwargs)
162        return response
163
164    @verbose
165    def bdev_lvol_delete(self, **kwargs):
166        response = rpc.lvol.bdev_lvol_delete(self.client, **kwargs)
167        return response
168
169    @verbose
170    def create_nvme_bdev(self, **kwargs):
171        response = rpc.bdev.bdev_nvme_attach_controller(self.client, **kwargs)
172        return response
173
174    @verbose
175    def bdev_nvme_detach_controller(self, **kwargs):
176        rpc.bdev.bdev_nvme_detach_controller(self.client, **kwargs)
177
178    @verbose
179    def bdev_null_create(self, **kwargs):
180        response = rpc.bdev.bdev_null_create(self.client, **kwargs)
181        return response
182
183    @verbose
184    def bdev_null_delete(self, **kwargs):
185        rpc.bdev.bdev_null_delete(self.client, **kwargs)
186
187    @verbose
188    def create_error_bdev(self, **kwargs):
189        response = rpc.bdev.bdev_error_create(self.client, **kwargs)
190
191    @verbose
192    def bdev_error_delete(self, **kwargs):
193        rpc.bdev.bdev_error_delete(self.client, **kwargs)
194
195    @verbose
196    @is_method_available
197    def bdev_lvol_get_lvstores(self):
198        if self.is_init:
199            self.current_lvol_stores = rpc.lvol.bdev_lvol_get_lvstores(self.client)
200            for lvs in self.current_lvol_stores:
201                yield LvolStore(lvs)
202
203    @verbose
204    def bdev_lvol_create_lvstore(self, **kwargs):
205        response = rpc.lvol.bdev_lvol_create_lvstore(self.client, **kwargs)
206        return response
207
208    @verbose
209    def bdev_lvol_delete_lvstore(self, **kwargs):
210        rpc.lvol.bdev_lvol_delete_lvstore(self.client, **kwargs)
211
212    @verbose
213    def create_rbd_bdev(self, **kwargs):
214        response = rpc.bdev.bdev_rbd_create(self.client, **kwargs)
215        return response
216
217    @verbose
218    def bdev_rbd_delete(self, **kwargs):
219        response = rpc.bdev.bdev_rbd_delete(self.client, **kwargs)
220        return response
221
222    @verbose
223    def create_virtio_dev(self, **kwargs):
224        response = rpc.vhost.bdev_virtio_attach_controller(self.client, **kwargs)
225        return self.print_array(response)
226
227    @verbose
228    def bdev_virtio_detach_controller(self, **kwargs):
229        response = rpc.vhost.bdev_virtio_detach_controller(self.client, **kwargs)
230        return response
231
232    @verbose
233    def bdev_raid_create(self, **kwargs):
234        rpc.bdev.bdev_raid_create(self.client, **kwargs)
235
236    @verbose
237    def bdev_raid_delete(self, **kwargs):
238        rpc.bdev.bdev_raid_delete(self.client, **kwargs)
239
240    @verbose
241    def bdev_uring_create(self, **kwargs):
242        response = rpc.bdev.bdev_uring_create(self.client, **kwargs)
243        return response
244
245    @verbose
246    def bdev_uring_delete(self, **kwargs):
247        rpc.bdev.bdev_uring_delete(self.client, **kwargs)
248
249    @verbose
250    @is_method_available
251    def bdev_virtio_scsi_get_devices(self):
252        if self.is_init:
253            for bdev in rpc.vhost.bdev_virtio_scsi_get_devices(self.client):
254                test = Bdev(bdev)
255                yield test
256
257    def list_vhost_ctrls(self):
258        if self.is_init:
259            self.current_vhost_ctrls = rpc.vhost.vhost_get_controllers(self.client)
260
261    @verbose
262    @is_method_available
263    def vhost_get_controllers(self, ctrlr_type):
264        if self.is_init:
265            self.list_vhost_ctrls()
266            for ctrlr in [x for x in self.current_vhost_ctrls if ctrlr_type in list(x["backend_specific"].keys())]:
267                yield VhostCtrlr(ctrlr)
268
269    @verbose
270    def vhost_delete_controller(self, **kwargs):
271        rpc.vhost.vhost_delete_controller(self.client, **kwargs)
272
273    @verbose
274    def vhost_create_scsi_controller(self, **kwargs):
275        rpc.vhost.vhost_create_scsi_controller(self.client, **kwargs)
276
277    @verbose
278    def vhost_start_scsi_controller(self, **kwargs):
279        rpc.vhost.vhost_start_scsi_controller(self.client, **kwargs)
280
281    @verbose
282    def vhost_create_blk_controller(self, **kwargs):
283        rpc.vhost.vhost_create_blk_controller(self.client, **kwargs)
284
285    @verbose
286    def vhost_scsi_controller_remove_target(self, **kwargs):
287        rpc.vhost.vhost_scsi_controller_remove_target(self.client, **kwargs)
288
289    @verbose
290    def vhost_scsi_controller_add_target(self, **kwargs):
291        rpc.vhost.vhost_scsi_controller_add_target(self.client, **kwargs)
292
293    def vhost_controller_set_coalescing(self, **kwargs):
294        rpc.vhost.vhost_controller_set_coalescing(self.client, **kwargs)
295
296    @verbose
297    def create_nvmf_transport(self, **kwargs):
298        rpc.nvmf.nvmf_create_transport(self.client, **kwargs)
299
300    def list_nvmf_transports(self):
301        if self.is_init:
302            self.current_nvmf_transports = rpc.nvmf.nvmf_get_transports(self.client)
303
304    @verbose
305    @is_method_available
306    def nvmf_get_transports(self):
307        if self.is_init:
308            self.list_nvmf_transports()
309            for transport in self.current_nvmf_transports:
310                yield NvmfTransport(transport)
311
312    def list_nvmf_subsystems(self):
313        if self.is_init:
314            self.current_nvmf_subsystems = rpc.nvmf.nvmf_get_subsystems(self.client)
315
316    @verbose
317    @is_method_available
318    def nvmf_get_subsystems(self):
319        if self.is_init:
320            self.list_nvmf_subsystems()
321            for subsystem in self.current_nvmf_subsystems:
322                yield NvmfSubsystem(subsystem)
323
324    def list_nvmf_referrals(self):
325        if self.is_init:
326            self.current_nvmf_referrals = rpc.nvmf.nvmf_discovery_get_referrals(self.client)
327
328    @verbose
329    @is_method_available
330    def nvmf_discovery_get_referrals(self):
331        if self.is_init:
332            self.list_nvmf_referrals()
333            for referral in self.current_nvmf_referrals:
334                yield NvmfReferral(referral)
335
336    @verbose
337    def nvmf_discovery_add_referral(self, **kwargs):
338        rpc.nvmf.nvmf_discovery_add_referral(self.client, **kwargs)
339
340    @verbose
341    def nvmf_discovery_remove_referral(self, **kwargs):
342        rpc.nvmf.nvmf_discovery_remove_referral(self.client, **kwargs)
343
344    @verbose
345    def create_nvmf_subsystem(self, **kwargs):
346        rpc.nvmf.nvmf_create_subsystem(self.client, **kwargs)
347
348    @verbose
349    def nvmf_delete_subsystem(self, **kwargs):
350        rpc.nvmf.nvmf_delete_subsystem(self.client, **kwargs)
351
352    @verbose
353    def nvmf_subsystem_add_listener(self, **kwargs):
354        rpc.nvmf.nvmf_subsystem_add_listener(self.client, **kwargs)
355
356    @verbose
357    def nvmf_subsystem_remove_listener(self, **kwargs):
358        rpc.nvmf.nvmf_subsystem_remove_listener(self.client, **kwargs)
359
360    @verbose
361    def nvmf_subsystem_add_host(self, **kwargs):
362        rpc.nvmf.nvmf_subsystem_add_host(self.client, **kwargs)
363
364    @verbose
365    def nvmf_subsystem_remove_host(self, **kwargs):
366        rpc.nvmf.nvmf_subsystem_remove_host(self.client, **kwargs)
367
368    @verbose
369    def nvmf_subsystem_allow_any_host(self, **kwargs):
370        rpc.nvmf.nvmf_subsystem_allow_any_host(self.client, **kwargs)
371
372    @verbose
373    def nvmf_subsystem_add_ns(self, **kwargs):
374        rpc.nvmf.nvmf_subsystem_add_ns(self.client, **kwargs)
375
376    @verbose
377    def nvmf_subsystem_remove_ns(self, **kwargs):
378        rpc.nvmf.nvmf_subsystem_remove_ns(self.client, **kwargs)
379
380    @verbose
381    def nvmf_subsystem_allow_any_host(self, **kwargs):
382        rpc.nvmf.nvmf_subsystem_allow_any_host(self.client, **kwargs)
383
384    @verbose
385    @is_method_available
386    def scsi_get_devices(self):
387        if self.is_init:
388            for device in rpc.iscsi.scsi_get_devices(self.client):
389                yield ScsiObj(device)
390
391    @verbose
392    @is_method_available
393    def iscsi_get_target_nodes(self):
394        if self.is_init:
395            for tg in rpc.iscsi.iscsi_get_target_nodes(self.client):
396                yield tg
397
398    @verbose
399    def iscsi_create_target_node(self, **kwargs):
400        rpc.iscsi.iscsi_create_target_node(self.client, **kwargs)
401
402    @verbose
403    def iscsi_delete_target_node(self, **kwargs):
404        rpc.iscsi.iscsi_delete_target_node(self.client, **kwargs)
405
406    @verbose
407    @is_method_available
408    def iscsi_get_portal_groups(self):
409        if self.is_init:
410            for pg in rpc.iscsi.iscsi_get_portal_groups(self.client):
411                yield ScsiObj(pg)
412
413    @verbose
414    @is_method_available
415    def iscsi_get_initiator_groups(self):
416        if self.is_init:
417            for ig in rpc.iscsi.iscsi_get_initiator_groups(self.client):
418                yield ScsiObj(ig)
419
420    @verbose
421    def construct_portal_group(self, **kwargs):
422        rpc.iscsi.iscsi_create_portal_group(self.client, **kwargs)
423
424    @verbose
425    def iscsi_delete_portal_group(self, **kwargs):
426        rpc.iscsi.iscsi_delete_portal_group(self.client, **kwargs)
427
428    @verbose
429    def construct_initiator_group(self, **kwargs):
430        rpc.iscsi.iscsi_create_initiator_group(self.client, **kwargs)
431
432    @verbose
433    def iscsi_delete_initiator_group(self, **kwargs):
434        rpc.iscsi.iscsi_delete_initiator_group(self.client, **kwargs)
435
436    @verbose
437    @is_method_available
438    def iscsi_get_connections(self, **kwargs):
439        if self.is_init:
440            for ic in rpc.iscsi.iscsi_get_connections(self.client, **kwargs):
441                yield ic
442
443    @verbose
444    def iscsi_initiator_group_add_initiators(self, **kwargs):
445        rpc.iscsi.iscsi_initiator_group_add_initiators(self.client, **kwargs)
446
447    @verbose
448    def iscsi_initiator_group_remove_initiators(self, **kwargs):
449        rpc.iscsi.iscsi_initiator_group_remove_initiators(self.client, **kwargs)
450
451    @verbose
452    def iscsi_target_node_add_pg_ig_maps(self, **kwargs):
453        rpc.iscsi.iscsi_target_node_add_pg_ig_maps(self.client, **kwargs)
454
455    @verbose
456    def iscsi_target_node_remove_pg_ig_maps(self, **kwargs):
457        rpc.iscsi.iscsi_target_node_remove_pg_ig_maps(self.client, **kwargs)
458
459    @verbose
460    def iscsi_auth_group_add_secret(self, **kwargs):
461        rpc.iscsi.iscsi_auth_group_add_secret(self.client, **kwargs)
462
463    @verbose
464    def iscsi_auth_group_remove_secret(self, **kwargs):
465        rpc.iscsi.iscsi_auth_group_remove_secret(self.client, **kwargs)
466
467    @verbose
468    @is_method_available
469    def iscsi_get_auth_groups(self, **kwargs):
470        return rpc.iscsi.iscsi_get_auth_groups(self.client, **kwargs)
471
472    @verbose
473    def iscsi_create_auth_group(self, **kwargs):
474        rpc.iscsi.iscsi_create_auth_group(self.client, **kwargs)
475
476    @verbose
477    def iscsi_delete_auth_group(self, **kwargs):
478        rpc.iscsi.iscsi_delete_auth_group(self.client, **kwargs)
479
480    @verbose
481    def iscsi_target_node_set_auth(self, **kwargs):
482        rpc.iscsi.iscsi_target_node_set_auth(self.client, **kwargs)
483
484    @verbose
485    def iscsi_target_node_add_lun(self, **kwargs):
486        rpc.iscsi.iscsi_target_node_add_lun(self.client, **kwargs)
487
488    @verbose
489    def iscsi_set_discovery_auth(self, **kwargs):
490        rpc.iscsi.iscsi_set_discovery_auth(self.client, **kwargs)
491
492    @verbose
493    @is_method_available
494    def iscsi_get_options(self, **kwargs):
495        return rpc.iscsi.iscsi_get_options(self.client, **kwargs)
496
497    def has_subsystem(self, subsystem):
498        for system in rpc.subsystem.framework_get_subsystems(self.client):
499            if subsystem.lower() == system["subsystem"].lower():
500                return True
501        return False
502
503    @verbose
504    def create_compress_bdev(self, **kwargs):
505        response = rpc.bdev.bdev_compress_create(self.client, **kwargs)
506        return response
507
508    @verbose
509    def bdev_compress_delete(self, **kwargs):
510        rpc.bdev.bdev_compress_delete(self.client, **kwargs)
511
512
513class Bdev(object):
514    def __init__(self, bdev_info):
515        """
516        All class attributes are set based on what information is received
517        from bdev_get_bdevs RPC call.
518        # TODO: Document in docstring parameters which describe bdevs.
519        # TODO: Possible improvement: JSON schema might be used here in future
520        """
521        for i in list(bdev_info.keys()):
522            setattr(self, i, bdev_info[i])
523
524
525class LvolStore(object):
526    def __init__(self, lvs_info):
527        """
528        All class attributes are set based on what information is received
529        from bdev_get_bdevs RPC call.
530        # TODO: Document in docstring parameters which describe bdevs.
531        # TODO: Possible improvement: JSON schema might be used here in future
532        """
533        for i in list(lvs_info.keys()):
534            setattr(self, i, lvs_info[i])
535
536
537class VhostCtrlr(object):
538    def __init__(self, ctrlr_info):
539        """
540        All class attributes are set based on what information is received
541        from vhost_get_controllers RPC call.
542        # TODO: Document in docstring parameters which describe bdevs.
543        # TODO: Possible improvement: JSON schema might be used here in future
544        """
545        for i in list(ctrlr_info.keys()):
546            setattr(self, i, ctrlr_info[i])
547
548
549class NvmfTransport(object):
550    def __init__(self, transport_info):
551        """
552        All class attributes are set based on what information is received
553        from get_nvmf_transport RPC call.
554        # TODO: Document in docstring parameters which describe bdevs.
555        # TODO: Possible improvement: JSON schema might be used here in future
556        """
557        for i in transport_info.keys():
558            setattr(self, i, transport_info[i])
559
560
561class NvmfSubsystem(object):
562    def __init__(self, subsystem_info):
563        """
564        All class attributes are set based on what information is received
565        from get_nvmf_subsystem RPC call.
566        # TODO: Document in docstring parameters which describe bdevs.
567        # TODO: Possible improvement: JSON schema might be used here in future
568        """
569        for i in subsystem_info.keys():
570            setattr(self, i, subsystem_info[i])
571
572
573class NvmfReferral(object):
574    def __init__(self, referral_info):
575        """
576        All class attributes are set based on what information is received
577        from get_nvmf_referrals RPC call.
578        """
579        for i in referral_info.keys():
580            setattr(self, i, referral_info[i])
581
582
583class ScsiObj(object):
584    def __init__(self, device_info):
585        """
586        All class attributes are set based on what information is received
587        from iscsi related RPC calls.
588        # TODO: Document in docstring parameters which describe bdevs.
589        # TODO: Possible improvement: JSON schema might be used here in future
590        """
591        for i in device_info.keys():
592            setattr(self, i, device_info[i])
593