Newer
Older
#!/usr/bin/env python
#
# This is a work in progress, adding functionality as I need it.
#
# Chad Kerner - chad.kerner@gmail.com
#
from __future__ import print_function
from subprocess import Popen, PIPE
import sys
import shlex
def run_cmd( cmdstr=None ):
"""
Wrapper around subprocess module calls.
"""
if not cmdstr:
return None
cmd = shlex.split(cmdstr)
subp = Popen(cmd, stdout=PIPE, stderr=PIPE)
(outdata, errdata) = subp.communicate()
if subp.returncode != 0:

Chad Kerner
committed
msg = "Error\n Command: {0}\n Message: {1}".format(cmdstr,errdata)

Chad Kerner
committed
sys.exit( subp.returncode )
"""
The mmlsfileset command returns encoded strings for special characters.
This will replace those encoded strings and return a true string.
"""
tempstring = mystring.replace('%2F', '/')
mystring = tempstring
tempstring = mystring.replace('%3A', ':')
return tempstring
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
class Cluster:
"""
This class will collect the information about the cluster.
"""
def __init__( self ):
self.get_cluster_info()
self.get_nsd_info()
def get_nsd_info( self ):
"""
This routing parses the mmlsnsd command.
"""
self.nsd_info = {}
fsdevs = {}
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmlsnsd")
for line in cmd_out.splitlines():
line.rstrip()
# Ignore blank lines
if not line:
continue
# Ignore dashed lines
if '----------' in line:
continue
# Ignore header lines
if 'File system' in line:
continue
if 'free disk' in line:
nsd_name = line.split()[2]
fsname = 'free'
servers = (line.split()[3]).split(',')
else:
nsd_name = line.split()[1]
fsname = line.split()[0]
servers = (line.split()[2]).split(',')
fsdevs[fsname] = 1
self.nsd_info[nsd_name] = {}
self.nsd_info[nsd_name]['usage'] = fsname
self.nsd_info[nsd_name]['servers'] = servers
self.gpfsdevs = fsdevs.keys()
def get_cluster_info( self ):
"""
This routine parses the mmlscluster command.
"""
self.cluster_info = {}
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmlscluster")
found_nodes = 0
for line in cmd_out.splitlines():
line.rstrip()
# Ignore blank lines
if not line:
continue
# Ignore dashed lines
if '----------' in line:
continue
if '==========' in line:
continue
if found_nodes >= 1:
nodeid = line.split()[0]
daemonname = line.split()[1]
ipaddr = line.split()[2]
adminname = line.split()[3]
self.cluster_info['nodes'][nodeid] = {}
self.cluster_info['nodes'][nodeid]['daemon_name'] = daemonname
self.cluster_info['nodes'][nodeid]['ip'] = ipaddr
self.cluster_info['nodes'][nodeid]['admin_name'] = adminname
if 'Node Daemon' in line:
found_nodes = found_nodes + 1
self.cluster_info['nodes'] = {}
if 'GPFS cluster name' in line:
self.cluster_info['name'] = line.split()[3]
if 'GPFS cluster id' in line:
self.cluster_info['id'] = line.split()[3]
if 'GPFS UID domain' in line:
self.cluster_info['uid'] = line.split()[3]
if 'Remote shell command' in line:
self.cluster_info['rsh'] = line.split()[3]
if 'Remote file copy command' in line:
self.cluster_info['rcp'] = line.split()[4]
if 'Primary server' in line:
self.cluster_info['primary'] = line.split()[2]
if 'Secondary server' in line:
self.cluster_info['secondary'] = line.split()[2]
def dump( self ):
if debug == 1:
print("Cluster Information")
for key in self.cluster_info.keys():
print("{0} -> {1}".format(key, self.cluster_info[key]))
if debug == 2:
print("\nNSD Information")
for key in self.nsd_info.keys():
print("{0} -> FS: {1} Servers: {2}".format(key, self.nsd_info[key]['usage'], self.nsd_info[key]['servers']))
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def remove_special_characters( mystring ):
tempstring = mystring.replace('%', '')
mystring = tempstring
tempstring = mystring.replace('(', '')
mystring = tempstring
tempstring = mystring.replace(')', '')
return tempstring
class StoragePool:
def __init__( self, gpfsdev ):
self.gpfsdev = gpfsdev
self.pools = {}
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmlspool {}".format( self.gpfsdev))
for line in cmd_out.splitlines()[2:]:
line.rstrip()
# Ignore blank lines
if not line:
continue
newline = remove_special_characters( line )
vals = newline.split()
poolname = vals[0]
self.pools[poolname] = {}
self.pools[poolname]['id'] = vals[1]
self.pools[poolname]['blksize'] = vals[2]
self.pools[poolname]['blkmod'] = vals[3]
self.pools[poolname]['data'] = vals[4]
self.pools[poolname]['metadata'] = vals[5]
self.pools[poolname]['datasize'] = vals[6]
self.pools[poolname]['datafree'] = vals[7]
self.pools[poolname]['datapctfree'] = vals[8]
self.pools[poolname]['metasize'] = vals[9]
self.pools[poolname]['metafree'] = vals[10]
self.pools[poolname]['metapctfree'] = vals[11]
self.pool_list = self.pools.keys()
def dump( self ):
print("{}".format(self.pools))
def __getitem__( self, key ):
return self.pools[key]
class Snapshots:
def __init__( self, gpfsdev, fileset='' ):
self.gpfsdev = gpfsdev
self.fileset = fileset
self.snap_name_separator = '_'
self.snapshots = {}
if self.fileset == '':
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmlssnapshot {0} -Y".format( self.gpfsdev ))
else:
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmlssnapshot {0} -j {1} -Y".format( self.gpfsdev, self.fileset ))
# Process the HEADER line
if 'No snapshots in file system' in cmd_out.splitlines()[0]:
self.snap_count = 0
return
keys = cmd_out.splitlines()[0].split(':')[6:]
for line in cmd_out.splitlines()[1:]:
line.rstrip()
# Ignore blank lines
if not line:
continue
vals = line.split(':')[6:]
sname = vals[1]
self.snapshots[sname] = {}
for idx in range(len(vals)-1):
self.snapshots[sname][keys[idx]] = replace_encoded_strings( vals[idx] )
snaplist = self.snapshots.keys()
self.snaplist = sorted( snaplist )
self.snap_count = len( snaplist )
"""
Given an integer of how many snapshots you want to keep, this will return a list of snapshot
names that are to be purged. It does not purge them, only a list of what needs to be purged
based on how many you want to keep.
"""
if self.snap_count <= max_to_keep:
self.dellist = list(self.snaplist)[ : -( max_to_keep ) ]
return self.dellist
def delsnap( self, snap_name ):
"""
Given a specific snapshot name, this routine will execute mmdelsnapshot and return you the output
from the command. The object already knows if it is a filesystem or a fileset snapshot, so you just
need to specify the snapshot name.
"""
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmdelsnapshot {} {}".format(self.gpfsdev, snap_name))
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmdelsnapshot {} {} -j {}".format(self.gpfsdev, snap_name, self.fileset))
return cmd_out
def snap( self ):
"""
This code will create a snapshot of the specified filesystem or fileset.
NOTE: You can NOT mix filesystem and fileset snapshots on the same GPFS device.
Filesystem snapshots are named: CCYYMMDD==HHMM for easy sorting / processing.
Filesystem snapshots are named: <Fileset>==CCYYMMDD==HHMM for easy processing again.
"""
if self.fileset == '':
snapname = time.strftime("%Y%m%d") + self.snap_name_separator + time.strftime("%H%M")
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmcrsnapshot {0} {1}".format( self.gpfsdev, snapname ))
else:
snapname = self.fileset + self.snap_name_separator + time.strftime("%Y%m%d") + self.snap_name_separator + time.strftime("%H%M")
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmcrsnapshot {0} {1} -j {2}".format( self.gpfsdev, snapname, self.fileset ))
class Filesystem:
"""
This class will collect the information about the specified GPFS device.
"""
filesystem_defaults = { 'automaticMountOption': 'yes',
'defaultMetadataReplicas': '1',
'maxMetadataReplicas': '2',
'defaultDataReplicas': '1',
'maxDataReplicas': '2',
'blockAllocationType': '',
'fileLockingSemantics': '',
}
def __init__( self, gpfsdev ):
if not gpfsdev:
raise ValueError('NoDevice')
else:
self.gpfsdev = gpfsdev
self.get_filesystem_information()
self.get_fileset_information()
def print_keys( self ):
keys = self.filesys.keys()
return keys
def get_pool_information( self ):
self.pools = StoragePool( self.gpfsdev )
def get_filesystem_information( self ):
self.filesys = {}
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmlsfs {0} -Y".format(self.gpfsdev))
for line in cmd_out.splitlines():
line.rstrip()
# Ignore blank lines
if not line:
continue
# Ignore HEADER line
if 'HEADER' in line:
continue
key = line.split(':')[7]
value = line.split(':')[8]
self.filesys[key] = replace_encoded_strings( value )
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
def fileset_list( self ):
"""
Return all of the fileset names in the file system.
"""
return self.filesets.keys()
def get_fileset_information( self ):
self.filesets = {}
cmd_out = run_cmd("/usr/lpp/mmfs/bin/mmlsfileset {0} -Y".format(self.gpfsdev))
# Process the HEADER line
keys = cmd_out.splitlines()[0].split(':')[7:]
for line in cmd_out.splitlines()[1:]:
line.rstrip()
# Ignore blank lines
if not line:
continue
vals = line.split(':')[7:]
fname = vals[0]
self.filesets[fname] = {}
for idx in range(len(vals)-1):
self.filesets[fname][keys[idx]] = replace_encoded_strings( vals[idx] )
@classmethod
def Create( self, gpfsdev, fsname ):
"""
This function will create a new filesystem.
Input 1: A dictionary containing the nsd's and their parameters.
mydisks['nsd1']['usage']='dataAndMetadata'
mydisks['nsd1']['failuregroup']='-1'
mydisks['nsd1']['pool']='system'
Input 2: A dictionary containing parameters for the file system.
"""
print("Creating {0} on {1}".format(fsname, gpfsdev))
self = Filesystem( gpfsdev )
return self
def __getitem__( self, key ):
return self.filesys[key]
if __name__ == '__main__':
#
# This is just where I do my testing of stuff.
#
snap = Snapshots( 'condo', 'root' )
sys.exit(0)
myFS = Filesystem( 'condo' )
fslist = myFS.fileset_list()
print("{}".format(myFS.filesets))
newfs = Filesystem.Create( 'fs0', 'chad' )
Clstr = Cluster()
#Clstr.dump()
print(Clstr.gpfsdevs)
myFs = Filesystem( 'wvu' )
FSa = Filesystem( 'des003' )
print(myFs['disks'])
print(FSa['disks'])
try:
F = Filesystem('')
except:
print("No Filesystem device specified.")
else:
print(F[disks])