import os import os.path import subprocess import uuid import socket import xattr import shutil class GlusterManager: ''' This class manage cluster filesystem by Glusterfs ''' def __init__(self): self.varpath = '/var/lib/glusterd/' self.peerspath = self.varpath + 'peers' self.volspath = self.varpath + 'vols' self.mount_lock = '/run/n4d-glusterfs.lock' self.mount_boot_path = '/var/lib/n4d-glusterfs/volumes' #def __init__ def startup(self,options): pass #def startup def test(self): pass #def test def backup(self): pass #def backup def path_is_clean(self,path): if not os.path.exists(path): return {'status':False,'msg':'This path not exists.'} # check not have .glusterfs folder if ( os.path.exists(path + "/.glusterfs" )): return {'status':False,'msg':'.glusterfs folder exists. You may clean this directory'} # check directory is void if ( len(os.listdir(path)) > 0 ): return {'status':False,'msg':'This folder not is void.'} # check not have xattr list_xattr = [x.encode('utf-8') for x in xattr.listxattr(path)] if ("trusted.glusterfs.volume-id" in list_xattr or "trusted.gfid" in list_xattr): return {'status':False,'msg':'There are extended atributes. You may clean'} return {'status':True,'msg':'This path is ok'} #def path_is_clean def clean_path(self,path): if ( os.path.exists(path + "/.glusterfs")): shutil.rmtree(path + "/.glusterfs") list_xattr = [x.encode('utf-8') for x in xattr.listxattr(path)] if ('trusted.glusterfs.volume-id' in list_xattr): xattr.removexattr(path,'trusted.glusterfs.volume-id') if ( 'trusted.gfid' in list_xattr): xattr.removexattr(path,'trusted.gfid') return {'status': True, 'msg':'This folder is ok to add into cluster'} #def clean_path def create_vol(self, vol_name, my_name, brick, transport='tcp'): ''' p = 0 - Creation of volume $vol_name has been successful. Please start the volume to access data. erro 255 - Brick: $my_name:/$brick already in use - Volume $vol_name already exists - Host $my_name not a friend ''' if not self.__gluster_is_started__(): return {'status':False,'msg':'Glusterfs is down.'} action = ['gluster','volume','create',vol_name,'transport',transport,my_name+":/"+brick] p = subprocess.Popen(action,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout, stderr = p.communicate() return_code = p.poll() if return_code == 0 : return {'status': True, 'msg':'Volume ' + vol_name + ' is created. Now you must start it'} else: if "or a prefix of it is already part of a volume" in stderr : return {'status': False, 'msg' : stderr.strip() + ". You must launch clean_path n4d method for /"+brick +", because this directory has been used in the past and may be dirty" } return {'status': False, 'msg' : stderr.strip() } #def create_vol def add_brick(self,vol_name,server_name,brick): if not self.__gluster_is_started__(): return {'status':False,'msg':'Glusterfs is down.'} if not os.path.exists(self.volspath +'/'+vol_name): return {'status':False,'msg':'This volume not exists.'} try: info_vol = open(self.volspath +'/'+vol_name+'/info','r').readlines() except: return {'status':False,'msg':'There is error in internal configuration for this volume.'} list_bricks = [] for x in info_vol: try: key,value = x.split('=') except: continue if key == 'replica_count': replica = str(int(value) + 1) continue if key.startswith('brick-'): list_bricks.append(value.strip()) if server_name+":-"+brick.replace('/','-') in list_bricks: return {'status':True,'msg':'This brick has been added in the past.'} action = ['gluster','volume','add-brick',vol_name,'replica',replica,server_name+":/"+brick] p = subprocess.Popen(action,stdout=subprocess.PIPE,stderr=subprocess.PIPE) out,err = p.communicate() return_code = p.poll() if return_code != 0: return {'status': False, 'msg':err} action = ['gluster','volume','heal',vol_name,'full'] p = subprocess.call(action) return {'status': True, 'msg':'Brick ' + server_name +':/' + brick +' is join to '+ vol_name} #def add_brick def remove_brick(self,vol_name,server_name,brick): if not self.__gluster_is_started__(): return {'status':False,'msg':'Glusterfs is down.'} if not os.path.exists(self.volspath +'/'+vol_name): return {'status':False,'msg':'This volume not exists.'} try: info_vol = open(self.volspath +'/'+vol_name+'/info','r').readlines() except: return {'status':False,'msg':'There is error in internal configuration for this volume.'} list_bricks = [] for x in info_vol: try: key,value = x.split('=') except: continue if key == 'replica_count': replica = str(int(value) - 1) continue if key.startswith('brick-'): list_bricks.append(value.strip()) if not server_name+":-"+brick.replace('/','-') in list_bricks: return {'status':True,'msg':'This brick has been removed in the past or never exists.'} action = ['gluster','volume','remove-brick',vol_name,'replica',replica,server_name+":/"+brick] p = subprocess.Popen(action,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE) out,err = p.communicate(input='y\n') return_code = p.poll() if return_code != 0: return {'status': False, 'msg':err} return {'status': True, 'msg':'Brick ' + server_name +':/' + brick +' is removed from '+ vol_name} #def remove_brick def delete_vol(self,vol_name): if not self.__gluster_is_started__(): return {'status':False,'msg':'Glusterfs is down.'} if not os.path.exists(self.volspath +'/'+vol_name): return {'status':False,'msg':'This volume not exists.'} action = ['gluster','volume','delete',vol_name] p = subprocess.Popen(action,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE) out,err = p.communicate(input='y\n') return_code = p.poll() if return_code != 0: return {'status': False, 'msg':err} return {'status': True, 'msg':'Volume ' + vol_name +' is deleted'} #def delete_vol def start_vol(self,vol_name): if not self.__gluster_is_started__(): return {'status':False,'msg':'Glusterfs is down.'} if vol_name not in os.listdir(self.volspath): return {'status':False,'msg':'Not exists this volume.'} action = ['gluster','volume','start',vol_name] p = subprocess.Popen(action,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout, stderr = p.communicate() return_code = p.poll() if return_code == 0: return {'status': True, 'msg':'volume ' + vol_name + ' has been started'} else: if "already started" in stderr: return {'status': True, 'msg':'volume ' + vol_name + ' has been started in the past'} return {'status': False, 'msg': stderr.strip()} #def start_vol def stop_vol(self,vol_name): if not self.__gluster_is_started__(): return {'status':False,'msg':'Glusterfs is down.'} if vol_name not in os.listdir(self.volspath): return {'status':False,'msg':'Not exists this volume.'} action = ['gluster','volume','stop',vol_name] p = subprocess.Popen(action,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE) stdout, stderr = p.communicate(input='y\n') return_code = p.poll() if return_code == 0: return {'status': True, 'msg':'volume ' + vol_name + ' has been stoped'} else: if "is not in the started state" in stderr: return {'status': True, 'msg':stderr.strip()} return {'status': False, 'msg': stderr.strip()} #def stop_vol def add_peer(self,peer): if self.__i_am_he__(peer): return {'status':'True','msg':'I am this peer'} if self.__gluster_is_started__(): p = subprocess.call(['gluster','peer','probe',peer]) if ( p != 0 ) : return {'status':False,'msg':'peer '+ peer + ' is unknown'} else: return {'status':True,'msg':'Peer is added'} else: try: socket.gethostbyname(peer) except: return {'status':False,'msg':'peer '+ peer + ' is unknown'} id = self.__peer_added__(peer) if id != None: return {'status':True,'msg':'Peer has been added in the past. Glusterfs service is down. You may start'} id = str(uuid.uuid4()) f = open(self.peerspath+'/'+id,'w') f.write('uuid='+id+'\n') f.write('state=3\n') f.write('hostname1='+peer+'\n') f.close() return {'status':True,'msg':'Peer is added, but Glusterfs service is down. You may start'} #def add_peer def del_peer(self,peer,force=False): if self.__i_am_he__(peer): return {'status':'False','msg':'I am this peer'} if self.__gluster_is_started__(): action = ['gluster','peer','detach'] if force: action.append('force') action.append(peer) p = subprocess.call(action) if ( p != 0 and p != 1) : return {'status':False,'msg':'peer '+ peer + ' is unknown'} elif (p != 0): return {'status': True, 'msg': "This peer isn't cluster"} else: return {'status':True,'msg':'Peer is detached'} else: id = self.__peer_added__(peer) if id == None: return {'status':True,'msg':"This peer isn't cluster"} os.remove(self.peerspath+'/'+id) return {'status':True,'msg':'Peer is removed, but Glusterfs service is down. You may start'} #def del_peer def mount_on_boot(self,tag,source,dest,options=""): if os.path.exists(self.mount_lock): return {'status':False,'msg':'mount_on_boot method is locked because other request is being processing at the moment '} f = open(self.mount_lock,'w') f.close() f = open(self.mount_boot_path,'a') f.write(source + " " + dest + " " + options + "#TAG "+ tag+"-"+ dest + "\n") f.close() os.remove(self.mount_lock) return {'status':True,'msg':'Added correctly'} #def mount_on_boot def mount_now(self): os.remove('/run/n4d-glusterfs.lock') os.system("service mounting-glusterfs restart") return {'status':True,'msg':'Mounting'} #def mount_now def clean_mount_on_boot(self): os.remove(self.mount_boot_path) #def clean_mount_on_boot ''' Internal Methods ''' def __gluster_is_started__(self): if os.path.exists('/run/glusterd.pid'): return True else: return False #def __gluster_is_started__ def __i_am_he__(self,peer): fqdn = socket.getfqdn() if peer == fqdn : return True else: return False #def __i_am_he__ def __peer_added__(self,peer): needle = "hostname1="+str(peer) for p in os.listdir(self.peerspath): # x are all fields striped from file in list listfield = [field.strip() for field in open(self.peerspath+"/"+p).readlines()] if needle in listfield: return p return None