→ Applies to: SynetoOS 4.x
Step 1. Connect to SynetoOS appliance via SSH as admin
ssh admin@<your_ip_address_or_hostname>
Step 2. Remove bash_profile
rm /var/storage/admin/.bash_profile
Step 3. Create file
touch /tmp/trans
Step 4. Give permissions to file
chmod +x trans
Step 5. Add this script to the file just created at step 3
#!/usr/bin/env python3
import sys
from hashlib import md5
from socket import gethostname
from argparse import ArgumentParser
from collections import defaultdict
from json import loads, dumps
from subprocess import Popen, PIPE
from logging import basicConfig, debug, error, DEBUG # , info, warning
basicConfig(filename='/var/log/trans.log', level=DEBUG)
parser = ArgumentParser()
parser.add_argument("--list", help="List replicas", action="store_true")
parser.add_argument("--serial", help="This host Serial Number", action="store_true")
parser.add_argument("--to-local", type=str, nargs='+',
help="Transform a replica into a local dataset", action="store")
parser.add_argument("--to-replica", type=str, nargs='+',
help="Transform a local dataset into a replica", action="store")
if len(sys.argv) == 1:
parser.print_help(sys.stderr)
sys.exit(1)
args = parser.parse_args()
def _run(cmd):
result = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=True)
stdout, stderr = result.communicate()
if len(stderr.decode()) != 0:
error('{0} failed with {1}'.format(cmd, stderr))
sys.exit(0)
else:
return stdout.decode()
def ser2id(s, newline=True):
if newline:
s = '{}\n'.format(s)
return md5(s.encode()).hexdigest()[:8]
def _replicas():
"""Return a dict with metadata of replica datasets"""
datasets_cmd = 'zfs list -Hro name'
dataset_list = [i for i in _run(datasets_cmd).split('\n') if i]
replicas = defaultdict(dict)
for dataset in dataset_list:
metadata_cmd = 'zfs-meta get {}'.format(dataset)
metadata = loads(_run(metadata_cmd))
if metadata:
if 'createdOn' in metadata.keys():
serial_number = '{}\n'.format(metadata['createdOn']['serialNumber'])
replicas[metadata['name']] = metadata
if 'trans' not in metadata.keys():
replicas[metadata['name']]['trans'] = {
'zfspath': dataset,
'machineid': ser2id(serial_number),
'createdOn': metadata['createdOn']
}
return replicas
def to_local(dataset_name):
metadata = _replicas()[dataset_name]
if len(metadata['hosts']) != 0:
print('{} is mounted. Unmount and try again.\n'.format(dataset_name))
sys.exit(0)
metadata['createdOn'] = {
'domain': metadata['createdOn']['domain'],
'poolName': metadata['createdOn']['poolName'],
'hostname': gethostname(),
'serialNumber': LOCAL
}
zfspath = metadata['trans']['zfspath']
if zfspath.split('/').count('backups') > 1:
pool = zfspath.split('/')[-3]
else:
pool = zfspath.split('/')[0]
metadata_set_cmd = "echo '{0}' | zfs-meta set {1}".format(dumps(metadata), zfspath)
out = _run(metadata_set_cmd)
debug('writing metadata on {}: {}'.format(dataset_name, out))
rename_cmd = 'sudo zfs rename -f {0} {1}/datastores/{2}'.format(zfspath, pool, dataset_name)
out = _run(rename_cmd)
debug('renaming dataset {} like so: {}'.format(dataset_name, rename_cmd))
def to_replica(dataset_name):
metadata = _replicas()[dataset_name]
# check if the dataset is mounted
if len(metadata['hosts']) != 0:
print('{} is mounted. Unmount and try again.\n'.format(dataset_name))
sys.exit(0)
if 'trans' in metadata.keys():
zfspath = metadata['trans']['zfspath']
pool = zfspath.split('/')[0]
metadata['createdOn'] = metadata['trans']['createdOn']
else:
zfspath = None # build the zfspath
rename_cmd = 'sudo zfs rename {0}/datasores/{1} {2}'.format(pool, dataset_name, zfspath)
out = _run(rename_cmd)
debug('renaming dataset {} like so: {}'.format(dataset_name, rename_cmd))
metadata_set_cmd = "echo '{0}' | zfs-meta set {1}".format(dumps(metadata), zfspath)
out = _run(metadata_set_cmd)
debug('writing metadata on {}: {}'.format(dataset_name, out))
if __name__ == "__main__":
datasets = _replicas()
LOCAL = _run('smbios -t 1|grep Serial').split(':')[1].strip()
if args.serial:
print(LOCAL)
if args.list:
for k, v in datasets.items():
name = k
sn = v['createdOn']['serialNumber']
hostname = v['createdOn']['hostname']
mounted = len(v['hosts']) != 0
if sn == LOCAL:
kind = 'Local dataset'
else:
kind = 'Replica'
text = '{}: {} Mounted: {} From: {} ({})'.format(kind, name, mounted, hostname, sn)
print(text)
if args.to_local:
for ds in args.to_local:
# TODO: check the kind of dataset and prevent transforming a replica into a replica
if ds in datasets.keys() and datasets[ds]['createdOn']['serialNumber'] != LOCAL:
print('convert replica into local dataset: ', ds)
to_local(ds)
else:
debug('{} is not a valid dataset. Ignoring.'.format(ds))
if args.to_replica:
for ds in args.to_replica:
if ds in datasets.keys() and datasets[ds]['createdOn']['serialNumber'] == LOCAL:
print('convert local dataset to replica: ', ds)
to_replica(ds)
else:
debug('{} is not a valid dataset. Ignoring.'.format(ds))
Step 6. List replicas
./tmp/trans --list
EXAMPLE OUTPUT
# ./trans --list Replica: ad From: hyper-a-demo2 (VMware-56 4d c8 3f f2 83 ee e2-5e 88 eb 98 e8 c9 bb c5) Replica: victim From: hyper-a-demo2 (VMware-56 4d c8 3f f2 83 ee e2-5e 88 eb 98 e8 c9 bb c5)
Step 7. Run the script (replace <datastore_name> with the correct information)
./trans --to-local <datastore_name>