1616"""
1717
1818# isort: STDLIB
19+ import glob
20+ import os
1921from abc import ABC , abstractmethod
22+ from collections import defaultdict
23+ from pathlib import Path
2024from typing import Any , Callable , Dict , List , Optional
2125
2226# isort: THIRDPARTY
2327from dateutil import parser as date_parser
2428from dbus import ObjectPath , String
2529from justbytes import Range
2630
31+ from .._constants import FilesystemId , IdType
2732from ._connection import get_object
2833from ._constants import TOP_OBJECT
2934from ._formatting import (
3540from ._utils import SizeTriple
3641
3742
43+ def _read_filesystem_symlinks (
44+ * , pool_name : Optional [str ] = None , fs_name : Optional [str ] = None
45+ ) -> defaultdict [str , set [str ]]:
46+ """
47+ Return a dict of pool names to filesystem names based on reading the
48+ directory of filesystem links at /dev/stratis.
49+
50+ Restrict to just one pool if pool_name is specified.
51+ """
52+ pools_and_fss = defaultdict (set )
53+ for path in glob .glob (
54+ os .path .join (
55+ "/" ,
56+ * (
57+ ["dev" , "stratis" ]
58+ + (["*" ] if pool_name is None else [pool_name ])
59+ + (["*" ] if fs_name is None else [fs_name ])
60+ ),
61+ )
62+ ):
63+ p_path = Path (path )
64+ pools_and_fss [p_path .parent .name ].add (p_path .name )
65+ return pools_and_fss
66+
67+
3868def list_filesystems (
39- uuid_formatter : Callable , * , pool_name = None , fs_id = None
69+ uuid_formatter : Callable ,
70+ * ,
71+ pool_name : Optional [str ] = None ,
72+ fs_id : Optional [FilesystemId ] = None ,
73+ use_dev_dir : bool = False ,
4074): # pylint: disable=too-many-locals
4175 """
4276 List the specified information about filesystems.
@@ -46,14 +80,26 @@ def list_filesystems(
4680 # pylint: disable=import-outside-toplevel
4781 from ._data import MOFilesystem , MOPool , ObjectManager , filesystems , pools
4882
83+ pools_and_fss = (
84+ _read_filesystem_symlinks (
85+ pool_name = pool_name ,
86+ fs_name = (
87+ fs_id .id_value
88+ if fs_id is not None and fs_id .id_type is IdType .UUID
89+ else None
90+ ),
91+ )
92+ if use_dev_dir
93+ else None
94+ )
95+
4996 proxy = get_object (TOP_OBJECT )
5097 managed_objects = ObjectManager .Methods .GetManagedObjects (proxy , {})
5198
5299 if pool_name is None :
53100 props = None
54101 pool_object_path = None
55102 fs_props = None
56- requires_unique = False
57103 else :
58104 props = {"Name" : pool_name }
59105 pool_object_path = next (
@@ -62,7 +108,6 @@ def list_filesystems(
62108 fs_props = {"Pool" : pool_object_path } | (
63109 {} if fs_id is None else fs_id .managed_objects_key ()
64110 )
65- requires_unique = fs_id is not None
66111
67112 pool_object_path_to_pool_name = dict (
68113 (path , MOPool (info ).Name ())
@@ -72,49 +117,46 @@ def list_filesystems(
72117 filesystems_with_props = [
73118 MOFilesystem (info )
74119 for objpath , info in filesystems (props = fs_props )
75- .require_unique_match (requires_unique )
120+ .require_unique_match (
121+ (not use_dev_dir ) and pool_name is not None and fs_id is not None
122+ )
76123 .search (managed_objects )
77124 ]
78125
79- if fs_id is None :
80- klass = Table (
81- uuid_formatter ,
82- filesystems_with_props ,
83- pool_object_path_to_pool_name ,
84- )
85- else :
86- klass = Detail (
87- uuid_formatter ,
88- filesystems_with_props ,
89- pool_object_path_to_pool_name ,
90- )
91-
92- klass .display ()
126+ klass = (Table if fs_id is None else Detail )(uuid_formatter )
127+ klass .display (
128+ filesystems_with_props ,
129+ pool_object_path_to_pool_name ,
130+ system_pools_and_fss = pools_and_fss ,
131+ pool_name = pool_name ,
132+ fs_id = fs_id ,
133+ )
93134
94135
95136class ListFilesystem (ABC ): # pylint: disable=too-few-public-methods
96137 """
97138 Handle listing a filesystem or filesystems.
98139 """
99140
100- def __init__ (
101- self ,
102- uuid_formatter : Callable ,
103- filesystems_with_props : List [Any ],
104- pool_object_path_to_pool_name : Dict [ObjectPath , String ],
105- ):
141+ def __init__ (self , uuid_formatter : Callable ):
106142 """
107143 Initialize a List object.
108144 :param uuid_formatter: function to format a UUID str or UUID
109145 :param uuid_formatter: str or UUID -> str
110146 :param bool stopped: whether to list stopped pools
111147 """
112148 self .uuid_formatter = uuid_formatter
113- self .filesystems_with_props = filesystems_with_props
114- self .pool_object_path_to_pool_name = pool_object_path_to_pool_name
115149
116150 @abstractmethod
117- def display (self ):
151+ def display ( # pylint: disable=too-many-arguments
152+ self ,
153+ filesystems_with_props : List [Any ],
154+ pool_object_path_to_pool_name : Dict [ObjectPath , String ],
155+ * ,
156+ system_pools_and_fss : Optional [defaultdict [str , set (str )]] = None ,
157+ pool_name : Optional [str ] = None ,
158+ fs_id : Optional [FilesystemId ] = None ,
159+ ):
118160 """
119161 List filesystems.
120162 """
@@ -125,7 +167,15 @@ class Table(ListFilesystem): # pylint: disable=too-few-public-methods
125167 List filesystems using table format.
126168 """
127169
128- def display (self ):
170+ def display ( # pylint: disable=too-many-arguments
171+ self ,
172+ filesystems_with_props : List [Any ],
173+ pool_object_path_to_pool_name : Dict [ObjectPath , String ],
174+ * ,
175+ system_pools_and_fss : Optional [defaultdict [str , set (str )]] = None ,
176+ pool_name : Optional [str ] = None ,
177+ fs_id : Optional [FilesystemId ] = None ,
178+ ):
129179 """
130180 List the filesystems.
131181 """
@@ -152,20 +202,52 @@ def filesystem_size_quartet(
152202 )
153203 return f'{ triple_str } / { "None" if limit is None else limit } '
154204
155- tables = [
156- (
157- self .pool_object_path_to_pool_name [mofilesystem .Pool ()],
158- mofilesystem .Name (),
159- filesystem_size_quartet (
160- Range (mofilesystem .Size ()),
161- get_property (mofilesystem .Used (), Range , None ),
162- get_property (mofilesystem .SizeLimit (), Range , None ),
163- ),
164- mofilesystem .Devnode (),
165- self .uuid_formatter (mofilesystem .Uuid ()),
166- )
167- for mofilesystem in self .filesystems_with_props
168- ]
205+ if system_pools_and_fss is not None :
206+ tables = []
207+ for mofilesystem in filesystems_with_props :
208+ pool_name = pool_object_path_to_pool_name [mofilesystem .Pool ()]
209+ fs_name = mofilesystem .Name ()
210+ if fs_name in system_pools_and_fss .get (pool_name , []):
211+ tables .append (
212+ (
213+ pool_name ,
214+ fs_name ,
215+ filesystem_size_quartet (
216+ Range (mofilesystem .Size ()),
217+ get_property (mofilesystem .Used (), Range , None ),
218+ get_property (mofilesystem .SizeLimit (), Range , None ),
219+ ),
220+ mofilesystem .Devnode (),
221+ self .uuid_formatter (mofilesystem .Uuid ()),
222+ )
223+ )
224+ system_pools_and_fss [pool_name ].remove (fs_name )
225+ for pool , fss in system_pools_and_fss :
226+ for fs in fss :
227+ tables .append (
228+ (
229+ pool ,
230+ fs ,
231+ "<UNAVAILABLE> / <UNAVAILABLE> / <UNAVAILABLE> / <UNAVAILABLE>" ,
232+ os .path .join ("/" , "dev" , "stratis" , pool , fs ),
233+ "<UNAVAILABLE>" ,
234+ )
235+ )
236+ else :
237+ tables = [
238+ (
239+ pool_object_path_to_pool_name [mofilesystem .Pool ()],
240+ mofilesystem .Name (),
241+ filesystem_size_quartet (
242+ Range (mofilesystem .Size ()),
243+ get_property (mofilesystem .Used (), Range , None ),
244+ get_property (mofilesystem .SizeLimit (), Range , None ),
245+ ),
246+ mofilesystem .Devnode (),
247+ self .uuid_formatter (mofilesystem .Uuid ()),
248+ )
249+ for mofilesystem in filesystems_with_props
250+ ]
169251
170252 print_table (
171253 [
@@ -185,31 +267,55 @@ class Detail(ListFilesystem): # pylint: disable=too-few-public-methods
185267 Do a detailed listing of filesystems.
186268 """
187269
188- def display (self ):
270+ def display ( # pylint: disable=too-many-arguments, too-many-locals
271+ self ,
272+ filesystems_with_props : List [Any ],
273+ pool_object_path_to_pool_name : Dict [ObjectPath , String ],
274+ * ,
275+ system_pools_and_fss : Optional [defaultdict [str , set (str )]] = None ,
276+ pool_name : Optional [str ] = None ,
277+ fs_id : Optional [FilesystemId ] = None ,
278+ ):
189279 """
190280 List the filesystems.
191281 """
192- assert len (self .filesystems_with_props ) == 1
282+ if system_pools_and_fss is None :
283+ assert len (filesystems_with_props ) == 1
193284
194- fs = self . filesystems_with_props [0 ]
285+ fs = filesystems_with_props [0 ]
195286
196- size_triple = SizeTriple (Range (fs .Size ()), get_property (fs .Used (), Range , None ))
197- limit = get_property (fs .SizeLimit (), Range , None )
198- created = (
199- date_parser .isoparse (fs .Created ()).astimezone ().strftime ("%b %d %Y %H:%M" )
200- )
287+ size_triple = SizeTriple (
288+ Range (fs .Size ()), get_property (fs .Used (), Range , None )
289+ )
290+
291+ limit = get_property (fs .SizeLimit (), Range , None )
292+ limit_str = "None" if limit is None else limit
293+
294+ created = (
295+ date_parser .isoparse (fs .Created ())
296+ .astimezone ()
297+ .strftime ("%b %d %Y %H:%M" )
298+ )
299+
300+ origin = get_property (fs .Origin (), self .uuid_formatter , None )
301+ origin_str = "None" if origin is None else origin
201302
202- origin = get_property (fs .Origin (), self .uuid_formatter , None )
303+ uuid = self .uuid_formatter (fs .Uuid ())
304+ name = fs .Name ()
305+ pool = pool_object_path_to_pool_name [fs .Pool ()]
306+ devnode = fs .Devnode ()
307+ else :
308+ pass
203309
204- print (f"UUID: { self . uuid_formatter ( fs . Uuid ()) } " )
205- print (f"Name: { fs . Name () } " )
206- print (f"Pool: { self . pool_object_path_to_pool_name [ fs . Pool ()] } " )
310+ print (f"UUID: { uuid } " )
311+ print (f"Name: { name } " )
312+ print (f"Pool: { pool } " )
207313 print ()
208- print (f"Device: { fs . Devnode () } " )
314+ print (f"Device: { devnode } " )
209315 print ()
210316 print (f"Created: { created } " )
211317 print ()
212- print (f' Snapshot origin: { "None" if origin is None else origin } ' )
318+ print (f" Snapshot origin: { origin_str } " )
213319 if origin is not None :
214320 scheduled = "Yes" if fs .MergeScheduled () else "No"
215321 print (f" Revert scheduled: { scheduled } " )
@@ -225,4 +331,4 @@ def display(self):
225331 f"{ TABLE_FAILURE_STRING if size_triple .free () is None else size_triple .free ()} "
226332 )
227333 print ()
228- print (f" Size Limit: { 'None' if limit is None else limit } " )
334+ print (f" Size Limit: { limit_str } " )
0 commit comments