@@ -333,6 +333,95 @@ def is_ceph_device(lv: "Volume") -> bool:
333333 else :
334334 return True
335335
336+ class Lvm :
337+ def __init__ (self , name_key : str , tags_key : str , ** kw : Any ) -> None :
338+ self .name : str = kw .get (name_key , '' )
339+ self .binary_change : str = ''
340+ self .path : str = ''
341+ if not self .name :
342+ raise ValueError (f'{ self .__class__ .__name__ } must have a non-empty name' )
343+
344+ self .api_data = kw
345+ self .tags = parse_tags (kw .get (tags_key , '' ))
346+
347+ for k , v in kw .items ():
348+ setattr (self , k , v )
349+
350+ def __str__ (self ) -> str :
351+ return f'<{ self .name } >'
352+
353+ def __repr__ (self ) -> str :
354+ return self .__str__ ()
355+
356+ def _format_tag_args (self , op : str , tags : Dict [str , Any ]) -> List [str ]:
357+ result : List [str ] = []
358+ for k , v in tags .items ():
359+ result .extend ([op , f'{ k } ={ v } ' ])
360+ return result
361+
362+ def clear_tags (self , keys : Optional [List [str ]] = None ) -> None :
363+ """
364+ Removes all or passed tags.
365+ """
366+ if not keys :
367+ keys = list (self .tags .keys ())
368+
369+ del_tags = {k : self .tags [k ] for k in keys if k in self .tags }
370+ if not del_tags :
371+ # nothing to clear
372+ return
373+ del_tag_args = self ._format_tag_args ('--deltag' , del_tags )
374+ # --deltag returns successful even if the to be deleted tag is not set
375+ process .call ([self .binary_change ] + del_tag_args + [self .path ], run_on_host = True )
376+ for k in del_tags .keys ():
377+ del self .tags [k ]
378+
379+ def clear_tag (self , key : str ) -> None :
380+ if self .tags .get (key ):
381+ current_value = self .tags [key ]
382+ tag = "%s=%s" % (key , current_value )
383+ process .call ([self .binary_change , '--deltag' , tag , self .path ], run_on_host = True )
384+ del self .tags [key ]
385+
386+ def set_tag (self , key : str , value : str ) -> None :
387+ """
388+ Set the key/value pair as an LVM tag.
389+ """
390+ # remove it first if it exists
391+ self .clear_tag (key )
392+
393+ process .call (
394+ [
395+ self .binary_change ,
396+ '--addtag' , '%s=%s' % (key , value ), self .path
397+ ],
398+ run_on_host = True
399+ )
400+ self .tags [key ] = value
401+
402+ def set_tags (self , tags : Dict [str , Any ]) -> None :
403+ """
404+ :param tags: A dictionary of tag names and values, like::
405+
406+ {
407+ "ceph.osd_fsid": "aaa-fff-bbbb",
408+ "ceph.osd_id": "0"
409+ }
410+
411+ At the end of all modifications, the tags are refreshed to reflect
412+ LVM's most current view.
413+ """
414+ self .clear_tags (list (tags .keys ()))
415+ add_tag_args = self ._format_tag_args ('--addtag' , tags )
416+ process .call ([self .binary_change ] + add_tag_args + [self .path ], run_on_host = True )
417+ for k , v in tags .items ():
418+ self .tags [k ] = v
419+
420+ def deactivate (self ) -> None :
421+ """
422+ Deactivate the LV by calling lvchange -an
423+ """
424+ process .call ([self .binary_change , '-an' , self .path ], run_on_host = True )
336425
337426####################################
338427#
@@ -342,7 +431,7 @@ def is_ceph_device(lv: "Volume") -> bool:
342431
343432PV_FIELDS = 'pv_name,pv_tags,pv_uuid,vg_name,lv_uuid'
344433
345- class PVolume :
434+ class PVolume ( Lvm ) :
346435 """
347436 Represents a Physical Volume from LVM, with some top-level attributes like
348437 ``pv_name`` and parsed tags as a dictionary of key/value pairs.
@@ -351,18 +440,10 @@ class PVolume:
351440 def __init__ (self , ** kw : Any ) -> None :
352441 self .pv_name : str = ''
353442 self .pv_uuid : str = ''
354- self .lv_uuid : str = ''
355- for k , v in kw .items ():
356- setattr (self , k , v )
443+ super ().__init__ ('pv_name' , 'pv_tags' , ** kw )
357444 self .pv_api = kw
358- self .name = kw ['pv_name' ]
359- self .tags = parse_tags (kw ['pv_tags' ])
360-
361- def __str__ (self ) -> str :
362- return '<%s>' % self .pv_api ['pv_name' ]
363-
364- def __repr__ (self ) -> str :
365- return self .__str__ ()
445+ self .binary_change : str = 'pvchange'
446+ self .path : str = self .pv_name
366447
367448 def set_tags (self , tags : Dict [str , Any ]) -> None :
368449 """
@@ -512,7 +593,7 @@ def get_single_pv(fields: str = PV_FIELDS, filters: Optional[Dict[str, Any]] = N
512593VG_CMD_OPTIONS = ['--noheadings' , '--readonly' , '--units=b' , '--nosuffix' , '--separator=";"' ]
513594
514595
515- class VolumeGroup (object ):
596+ class VolumeGroup (Lvm ):
516597 """
517598 Represents an LVM group, with some top-level attributes like ``vg_name``
518599 """
@@ -523,18 +604,8 @@ def __init__(self, **kw: Any) -> None:
523604 self .vg_free_count : str = ''
524605 self .vg_extent_size : str = ''
525606 self .vg_extent_count : str = ''
526- for k , v in kw .items ():
527- setattr (self , k , v )
528- self .name = kw ['vg_name' ]
529- if not self .name :
530- raise ValueError ('VolumeGroup must have a non-empty name' )
531- self .tags = parse_tags (kw .get ('vg_tags' , '' ))
532-
533- def __str__ (self ) -> str :
534- return '<%s>' % self .name
535-
536- def __repr__ (self ) -> str :
537- return self .__str__ ()
607+ super ().__init__ ('vg_name' , 'vg_tags' , ** kw )
608+ self .vg_api = kw
538609
539610 @property
540611 def free (self ) -> int :
@@ -824,7 +895,7 @@ def get_all_devices_vgs(name_prefix: str = '') -> List[VolumeGroup]:
824895 '--units=b' , '--nosuffix' ]
825896
826897
827- class Volume :
898+ class Volume ( Lvm ) :
828899 """
829900 Represents a Logical Volume from LVM, with some top-level attributes like
830901 ``lv_name`` and parsed tags as a dictionary of key/value pairs.
@@ -837,21 +908,15 @@ def __init__(self, **kw: Any) -> None:
837908 self .vg_name : str = ''
838909 self .lv_size : str = ''
839910 self .lv_tags : Dict [str , Any ] = {}
840- for k , v in kw .items ():
841- setattr (self , k , v )
911+ super ().__init__ ('lv_name' , 'lv_tags' , ** kw )
842912 self .lv_api = kw
843- self .name = kw ['lv_name' ]
844- if not self .name :
845- raise ValueError ('Volume must have a non-empty name' )
846- self .tags = parse_tags (kw ['lv_tags' ])
847913 self .encrypted = self .tags .get ('ceph.encrypted' , '0' ) == '1'
848914 self .used_by_ceph = 'ceph.osd_id' in self .tags
915+ self .binary_change : str = 'lvchange'
916+ self .path : str = self .lv_path
849917
850918 def __str__ (self ) -> str :
851- return '<%s>' % self .lv_api ['lv_path' ]
852-
853- def __repr__ (self ) -> str :
854- return self .__str__ ()
919+ return f'<{ self .api_data .get ("lv_path" , self .name )} >'
855920
856921 def as_dict (self ) -> Dict [Any , Any ]:
857922 obj : Dict [Any , Any ] = {}
@@ -883,80 +948,6 @@ def report(self) -> Dict[str, Any]:
883948 report [type_uuid ] = self .tags ['ceph.{}' .format (type_uuid )]
884949 return report
885950
886- def _format_tag_args (self , op : str , tags : Dict [str , Any ]) -> List [str ]:
887- result : List [str ] = []
888- for k , v in tags .items ():
889- result .extend ([op , f'{ k } ={ v } ' ])
890- return result
891-
892- def clear_tags (self , keys : Optional [List [str ]] = None ) -> None :
893- """
894- Removes all or passed tags from the Logical Volume.
895- """
896- if not keys :
897- keys = list (self .tags .keys ())
898-
899- del_tags = {k : self .tags [k ] for k in keys if k in self .tags }
900- if not del_tags :
901- # nothing to clear
902- return
903- del_tag_args = self ._format_tag_args ('--deltag' , del_tags )
904- # --deltag returns successful even if the to be deleted tag is not set
905- process .call (['lvchange' ] + del_tag_args + [self .lv_path ], run_on_host = True )
906- for k in del_tags .keys ():
907- del self .tags [k ]
908-
909-
910- def set_tags (self , tags : Dict [str , Any ]) -> None :
911- """
912- :param tags: A dictionary of tag names and values, like::
913-
914- {
915- "ceph.osd_fsid": "aaa-fff-bbbb",
916- "ceph.osd_id": "0"
917- }
918-
919- At the end of all modifications, the tags are refreshed to reflect
920- LVM's most current view.
921- """
922- self .clear_tags (list (tags .keys ()))
923- add_tag_args = self ._format_tag_args ('--addtag' , tags )
924- process .call (['lvchange' ] + add_tag_args + [self .lv_path ], run_on_host = True )
925- for k , v in tags .items ():
926- self .tags [k ] = v
927-
928-
929- def clear_tag (self , key : str ) -> None :
930- if self .tags .get (key ):
931- current_value = self .tags [key ]
932- tag = "%s=%s" % (key , current_value )
933- process .call (['lvchange' , '--deltag' , tag , self .lv_path ], run_on_host = True )
934- del self .tags [key ]
935-
936-
937- def set_tag (self , key : str , value : str ) -> None :
938- """
939- Set the key/value pair as an LVM tag.
940- """
941- # remove it first if it exists
942- self .clear_tag (key )
943-
944- process .call (
945- [
946- 'lvchange' ,
947- '--addtag' , '%s=%s' % (key , value ), self .lv_path
948- ],
949- run_on_host = True
950- )
951- self .tags [key ] = value
952-
953- def deactivate (self ) -> None :
954- """
955- Deactivate the LV by calling lvchange -an
956- """
957- process .call (['lvchange' , '-an' , self .lv_path ], run_on_host = True )
958-
959-
960951def create_lv (name_prefix : str ,
961952 uuid : str ,
962953 vg : Optional [VolumeGroup ] = None ,
0 commit comments