Skip to content
Snippets Groups Projects
Commit 3ff27f97 authored by Thibault VINCENT's avatar Thibault VINCENT
Browse files

add: drbd abstraction

parent a235d4dd
No related branches found
No related tags found
No related merge requests found
# -*- coding: utf-8 -*-
import xml.dom.minidom, os
from time import sleep
from errors import DRBDPoolError, DRBDError
from utils import RWLock, Exec, Enum, Net
class DRBDPool(object):
'''
'''
MAX_DEV = 16
_dev = dict([(i, None) for i in range(0, MAX_DEV)])
_dev_mutex = RWLock()
def _release_device(self, minor):
'''
'''
with self._dev_mutex.write:
self._dev[minor] = None
def get_device(self, minor):
'''
'''
with self._dev_mutex.read:
if minor > 0 and minor < self.MAX_DEV:
if self._dev[minor] is not None:
return self._dev[minor]
else:
raise DRBDPoolError('device ID `%i` is not allocated' %
minor)
else:
raise DRBDPoolError('invalid device ID')
def new_device(self):
'''
'''
with self._dev_mutex.write:
for minor, dev in self._dev.iteritems():
if dev is None:
drbd = DRBD(self, minor)
self._dev[minor] = drbd
return drbd
class DRBD(object):
'''
'''
# tools binaries
BIN_PATH = '/sbin'
DRBDADM = os.path.join(BIN_PATH, 'drbdadm')
DRBDMETA = os.path.join(BIN_PATH, 'drbdmeta')
DRBDSETUP = os.path.join(BIN_PATH, 'drbdsetup')
DMSETUP = os.path.join(BIN_PATH, 'dmsetup')
MODPROBE = os.path.join(BIN_PATH, 'modprobe')
# connection states
CSTATES = Enum(
STANDALONE = "StandAlone",
DISCONNECTING = "Disconnecting",
UNCONNECTED = "Unconnected",
TIMEOUT = "Timeout",
BROKEN_PIPE = "BrokenPipe",
NETWORK_FAILURE = "NetworkFailure",
PROTOCOL_ERROR = "ProtocolError",
WF_CONNECTION = "WFConnection",
WF_REPORT_PARAMS = "WFReportParams",
TEAR_DOWN = "TearDown",
CONNECTED = "Connected",
STARTING_SYNC_S = "StartingSyncS",
STARTING_SYNC_T = "StartingSyncT",
WF_BITMAP_S = "WFBitMapS",
WF_BITMAP_T = "WFBitMapT",
WF_SYNC_UUID = "WFSyncUUID",
SYNC_SOURCE = "SyncSource",
SYNC_TARGET = "SyncTarget",
PAUSED_SYNC_S = "PausedSyncS",
PAUSED_SYNC_T = "PausedSyncT",
VERIFY_S = "VerifyS",
VERIFY_T = "VerifyT",
)
# node roles
NROLES = Enum(
PRIMARY = "Primary",
SECONDARY = "Secondary",
UNKNOWN = "Unknown",
)
# disk states
DSTATES = Enum(
DISKLESS = "Diskless",
ATTACHING = "Attaching",
FAILED = "Failed",
NEGOTIATING = "Negotiating",
INCONSISTENT = "Inconsistent",
OUTDATED = "Outdated",
UNKNOWN = "DUnknown",
CONSISTENT = "Consistent",
UP_TO_DATE = "UpToDate",
)
def __init__(self, manager, minor):
'''
'''
self._manager = manager
self._minor = int(minor)
self._path = '/dev/drbd%i' % self._minor
self._meta = None
self._dm_table = None
# load kernel driver, do not check for error
Exec.silent([self.MODPROBE, 'drbd', 'minor_count=100',
'usermode_helper=/bin/true'])
# check that binaries are available
for cmd in [self.DRBDADM, self.DRBDMETA, self.DRBDSETUP]:
if not os.access(cmd, os.F_OK):
raise DRBDError('failed to init DRBD device, support tool `%s`'
'is not available' % cmd)
# check that device is not used
if self.status()['conn'] is not None:
print DRBDError('device minor `%i` is configured, cannot use it' %
self._minor)
def destroy(self):
'''
'''
# disconnect remote node
self.disconnect()
# bring down drbd device
Exec.silent([self.DRBDSETUP,
self._path,
'detach'])
Exec.silent([self.DRBDSETUP,
self._path,
'down'])
# release device from the pool
self._manager._release_device(self._minor)
# suicide
self._path = None
self._minor = None
def get_port(self):
'''
'''
return 7788 + self._minor
def status(self):
'''
'''
# fetch xml status
rc, output = Exec.call([self.DRBDSETUP, self._path, 'status'])
if rc:
raise DRBDError('failed to get device status')
# parse status
try:
xroot = xml.dom.minidom.parseString(output[0])
xres = xroot.firstChild
status = {}
status['conn'] = self.CSTATES.get(xres.getAttribute('cs'))
status['disk'] = self.DSTATES.get(xres.getAttribute('ds1'))
status['rdisk'] = self.DSTATES.get(xres.getAttribute('ds2'))
status['role'] = self.NROLES.get(xres.getAttribute('ro1'))
status['rrole'] = self.NROLES.get(xres.getAttribute('ro2'))
return status
except Exception as err:
return {'conn' : None}
def setup(self, sourcedev, metadev):
'''
'''
if self.status()['conn'] is None:
# wipe and init meta device
rc = Exec.silent([self.DRBDMETA,
'-f',
self._path,
'v08',
metadev,
'0',
'wipe-md'])
if rc != 0:
raise DRBDError('failed to clean meta device, maybe it is in'
' use or does not exist')
rc = Exec.silent([self.DRBDMETA,
'-f',
self._path,
'v08',
metadev,
'0',
'create-md'])
if rc != 0:
raise DRBDError('failed to create meta device, maybe it is in'
' use or does not exist')
else:
self._meta = metadev
# create the drbd disk
rc = Exec.silent([self.DRBDSETUP,
self._path,
'disk',
sourcedev,
metadev,
'0',
'--create-device'])
if rc != 0:
raise DRBDError('failed to setup DRBD disk')
else:
raise DRBDError('cannot setup device, it is already configured')
def connect(self, remote, remoteport, rate=50000):
'''
'''
# connect to remote node
rc = Exec.silent([self.DRBDSETUP,
self._path,
'net',
'0.0.0.0:%i' % (self.get_port()),
'%s:%i' % (remote, remoteport),
'C',
'-m',
'-S',
'10000000'])
if rc != 0:
raise DRBDError('failed to initiate connection to remote node, '
'local port may already be in use')
# FIXME is this wait really needed ?
sleep(3)
# force sync rate
rc = Exec.silent([self.DRBDSETUP,
self._path,
'syncer',
'-r',
str(rate)])
if rc != 0:
raise DRBDError('failed to set sync rate')
def disconnect(self):
'''
'''
# disconnect from remote node
Exec.silent([self.DRBDSETUP,
self._path,
'disconnect'])
def primary(self):
'''
Switch the drbd to primary mode
'''
rc = Exec.silent([self.DRBDSETUP,
self._path,
'primary',
'-f'])
if rc != 0:
raise DRBDError('failed to switch to primary node mode')
def secondary(self):
'''
Switch the drbd to secondary mode
'''
rc = Exec.silent([self.DRBDSETUP,
self._path,
'secondary'])
if rc != 0:
raise DRBDError('failed to switch to secondary node mode')
def wait_connection(self, timeout=300):
'''
Wait for connection with the remote drbd node
'''
rc = Exec.silent([self.DRBDSETUP,
self._path,
'wait-connect',
'-t',
str(timeout),
'-d',
str(timeout),
'-o',
str(timeout)])
if rc != 0 or self.status()['conn'] != self.CSTATES.CONNECTED:
raise DRBDError('no connection after `%i` seconds' % timeout)
def wait_sync(self):
'''
Wait for synchronization of the drbd device
'''
rc = Exec.silent([self.DRBDSETUP,
self._path,
'wait-sync'])
status = self.status()
if (rc != 0 or not status or status['disk'] != self.DSTATES.UP_TO_DATE
or status['rdisk'] != self.DSTATES.UP_TO_DATE):
raise DRBDError('synchronization failed')
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment