Skip to content

Commit a53b888

Browse files
author
Claudio Fahey
committed
initial commit
1 parent 7b976ea commit a53b888

File tree

3 files changed

+332
-0
lines changed

3 files changed

+332
-0
lines changed

configure-ssh.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#!/usr/bin/env python
2+
# Written by [email protected]
3+
4+
"""%prog [options] host ...
5+
6+
%prog will configure password-less SSH to remote hosts.
7+
8+
Examples:
9+
%prog -u root host1
10+
This is the simplest usage. It will prompt for the password for root@host1.
11+
When it is done, "ssh root@host1" should run without asking for a password.
12+
13+
%prog -u root host1 host2
14+
Same as above but it will repeat for host1 and host2.
15+
16+
%prog -u root -p mysecretpassword host1 host2
17+
To avoid being prompted for a password, specify it with the -p option.
18+
You must have sshpass installed for this to work.
19+
Understand the risks of using sshpass before using this in a secure setting.
20+
21+
%prog -u user1 -U centos host1
22+
When configuring password-less SSH, use the account centos@host1 to establish the
23+
initial SSH connection. This will configure the ~/.ssh directory for user1@host1 to
24+
allow password-less SSH to this account.
25+
26+
%prog -u user1 -U centos -s host1
27+
Same as above but centos@host1 will use sudo to access user1@host1's home directory.
28+
This is useful for cloud images where password-less SSH already exists for centos@host1 but
29+
not other accounts.
30+
31+
%prog --help
32+
View all options."""
33+
34+
import os
35+
import optparse
36+
import tempfile
37+
import shutil
38+
import os.path
39+
40+
def configure_ssh(host, user, password, configure_as_user, sudo, public_key_path, configure_as_identity, identity):
41+
"""host can be IP, fqdn, or relative host name"""
42+
43+
# Use a dictionary to make it easy to build the various command strings.
44+
p = {
45+
'password': password,
46+
'user': user,
47+
'host': host,
48+
'configure_as_user': configure_as_user,
49+
'public_key_path': public_key_path,
50+
'configure_as_identity': configure_as_identity,
51+
'identity': identity,
52+
}
53+
54+
if p['password']:
55+
p['sshpass'] = 'sshpass -p "%(password)s" ' % p
56+
else:
57+
p['sshpass'] = ''
58+
59+
if not p['configure_as_user']:
60+
p['configure_as_user'] = p['user']
61+
62+
if sudo:
63+
p['sudo'] = 'sudo '
64+
p['ssh_opt_tt'] = '-tt '
65+
else:
66+
p['sudo'] = ''
67+
p['ssh_opt_tt'] = ''
68+
69+
# Note that when ssh -tt is used, read from stdin blocks. Therefore, use head -1 to prevent blocking.
70+
p['remote_cmd'] = ((
71+
'%(sudo)smkdir -p ~%(user)s/.ssh ' +
72+
'; %(sudo)stouch ~%(user)s/.ssh/authorized_keys ' +
73+
'; %(sudo)schmod 700 ~%(user)s/.ssh ' +
74+
'; %(sudo)schown -R %(user)s ~%(user)s/.ssh ' +
75+
'; %(sudo)schown -R :%(user)s ~%(user)s/.ssh ' +
76+
'; %(sudo)schmod 600 ~%(user)s/.ssh/authorized_keys ' +
77+
'; head -1 - | %(sudo)stee -a ~%(user)s/.ssh/authorized_keys')
78+
% p)
79+
80+
p['ssh_cmd'] = '"%(remote_cmd)s"' % p
81+
82+
if p['configure_as_identity']:
83+
p['ssh_opt_i'] = '-i %(configure_as_identity)s ' % p
84+
else:
85+
p['ssh_opt_i'] = ''
86+
87+
if p['identity']:
88+
p['ssh_opt_i_test'] = '-i %(identity)s ' % p
89+
else:
90+
p['ssh_opt_i_test'] = ''
91+
92+
# Remove host from known_hosts file to avoid problems with IP address reuse
93+
cmd = 'ssh-keygen -R %(host)s' % p
94+
print('# %s' % cmd)
95+
os.system(cmd)
96+
97+
# Configure password-less SSH
98+
cmd = ((
99+
'cat %(public_key_path)s ' +
100+
'| %(sshpass)sssh -o StrictHostKeyChecking=no %(ssh_opt_tt)s%(ssh_opt_i)s%(configure_as_user)s@%(host)s %(ssh_cmd)s')
101+
% p)
102+
print('# %s' % cmd)
103+
os.system(cmd)
104+
105+
# Test password-less SSH
106+
cmd = 'ssh %(ssh_opt_i_test)s%(user)s@%(host)s "echo -n success: ; hostname"' % p
107+
print('# %s' % cmd)
108+
os.system(cmd)
109+
110+
def main():
111+
parser = optparse.OptionParser(usage=__doc__)
112+
parser.add_option('-u', '--user', action='append', dest='users',
113+
help='target user on remote host')
114+
parser.add_option('-U', '--configure-as-user', action='store', dest='configure_as_user',
115+
help='configure using this user account on remote host')
116+
parser.add_option('-k', '--public-key', action='store', dest='public_key', default='~/.ssh/id_rsa.pub',
117+
help='id_rsa.pub key file to authorize')
118+
parser.add_option('-i', '--identity', action='store', dest='identity', default='~/.ssh/id_rsa',
119+
help='id_rsa private key file for local user that should be allowed to SSH into the remote host')
120+
parser.add_option('-I', '--configure-as-identity', action='store', dest='configure_as_identity', default='~/.ssh/id_rsa',
121+
help='configure using id_rsa private key file')
122+
parser.add_option('-n', '--host', action='append', dest='hosts',
123+
help='name of remote host (-n is optional)')
124+
parser.add_option('-p', '--password', action='store', dest='password',
125+
help='password of user on remote host (requires sshpass)')
126+
parser.add_option('-s', '--sudo', action='store_true', dest='sudo',
127+
help='use sudo on remote host')
128+
options, args = parser.parse_args()
129+
hosts = args
130+
if options.hosts:
131+
hosts += options.hosts
132+
if not options.users or not hosts:
133+
parser.error('Missing required parameters')
134+
135+
public_key_path = os.path.expanduser(options.public_key)
136+
if not os.path.isfile(public_key_path):
137+
parser.error('The public key file %s is missing. Use the command "ssh-keygen -t rsa -b 4096" to create a key pair.' % public_key_path)
138+
139+
for user in options.users:
140+
for host in hosts:
141+
configure_ssh(host, user, options.password, options.configure_as_user, options.sudo, public_key_path, options.configure_as_identity, options.identity)
142+
143+
if __name__ == '__main__':
144+
main()

mount-nfs.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env python
2+
# Written by [email protected]
3+
4+
"""%prog [options]
5+
6+
%prog will permanently mount an NFS export.
7+
8+
Examples:
9+
%prog -m /mnt/home -p nfs-server.example.com:/home
10+
This will create the mount point directory, modify /etc/fstab,
11+
and mount the NFS export.
12+
13+
%prog --help
14+
View all options."""
15+
16+
import optparse
17+
import os
18+
19+
def create_mount(mount_point, nfs_path):
20+
assert os.system('umount %s ; mkdir -p %s' % (mount_point, mount_point)) == 0
21+
assert os.system('grep -v %s /etc/fstab > /tmp/fstab ; cp /tmp/fstab /etc/fstab' % mount_point) == 0
22+
assert os.system('echo %s\t%s\tnfs\tnolock,nfsvers=3,tcp,rw,hard,intr,timeo=600,retrans=2,rsize=524288,wsize=524288 >> /etc/fstab' % (nfs_path, mount_point)) == 0
23+
assert os.system('mount -a ; ls -lh %s' % mount_point) == 0
24+
25+
def main():
26+
parser = optparse.OptionParser(usage=__doc__)
27+
parser.add_option('-m', '--mount-point', action='store', dest='mount_point',
28+
help='mount point (e.g. /mnt/home)')
29+
parser.add_option('-p', '--nfs-path', action='store', dest='nfs_path',
30+
help='NFS path (e.g. nfs-server.example.com:/home)')
31+
options, args = parser.parse_args()
32+
if not options.mount_point or not options.nfs_path:
33+
parser.error('Missing required parameters')
34+
35+
create_mount(options.mount_point, options.nfs_path)
36+
37+
if __name__ == '__main__':
38+
main()

prepare-data-disks.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#!/usr/bin/python -u
2+
# Written by [email protected]
3+
4+
"""%prog [options] /dev/disk ...
5+
6+
%prog will partition, format, and mount each of the specified disks.
7+
8+
Examples:
9+
%prog /dev/sdb
10+
This will create a single partition on /dev/sdb, format it using ext4,
11+
and then mount it as /grid/0.
12+
13+
%prog /dev/sdb /dev/sdc
14+
Same as above but will repeat with /dev/sdc and mount it is /grid/1.
15+
16+
%prog -a -m sdb-c
17+
Automatically discover the disks that match the pattern /dev/sd[b-z],
18+
then partition, format, and mount them.
19+
20+
%prog -a -m sdb-c -t
21+
Automatically discover the disks that match the pattern /dev/sd[b-z].
22+
Prints the list of disks but does not take any action to partition,
23+
format, or mount the disks. Use the -t option to ensure the list of
24+
disks is correct before preparing them.
25+
26+
%prog --help
27+
View all options.
28+
29+
WARNING! THIS WILL ERASE THE CONTENTS OF THESE DISKS!"""
30+
31+
import os
32+
import glob
33+
import platform
34+
import multiprocessing
35+
import subprocess
36+
import optparse
37+
from time import sleep
38+
39+
def get_disks_to_prepare(discover_method):
40+
disks = []
41+
if discover_method == 'ecs':
42+
disk_list_commands = [
43+
'parted -m -s -l | grep /dev/.*:4001GB | cut -d : -f 1 | sort',
44+
'parted -m -s -l | grep /dev/.*:6001GB | cut -d : -f 1 | sort',
45+
'parted -m -s -l | grep /dev/.*:8002GB | cut -d : -f 1 | sort',
46+
]
47+
disks = [subprocess.check_output(cmd, shell=True) for cmd in disk_list_commands]
48+
disks = ''.join(disks)
49+
disks = disks.rstrip('\n').split('\n')
50+
disks = [d for d in disks if d != '']
51+
elif discover_method == 'sdb-z':
52+
disks = sorted(glob.glob('/dev/sd[b-z]'))
53+
elif discover_method == 'vdb-z':
54+
disks = sorted(glob.glob('/dev/vd[b-z]'))
55+
return disks
56+
57+
def umount_disk(disk_info):
58+
disk = disk_info['disk']
59+
disk_number = disk_info['disk_number']
60+
print('%s: Umounting disk %d, %s' % (platform.node(), disk_number, disk))
61+
os.system('umount %s1' % disk)
62+
63+
def partition_disk(disk_info):
64+
disk = disk_info['disk']
65+
disk_number = disk_info['disk_number']
66+
print('%s: Partitioning disk %d, %s' % (platform.node(), disk_number, disk))
67+
if True:
68+
assert os.system('parted -s %s mklabel GPT' % disk) == 0
69+
os.system('parted -s %s rm 1' % disk)
70+
os.system('parted -s %s rm 2' % disk)
71+
# Must sleep to avoid errors updating OS partition table.
72+
sleep(0.2)
73+
assert os.system('parted -s -a cylinder %s -- mkpart primary ext4 1 -1' % disk) == 0
74+
75+
def format_disk(disk_info):
76+
disk = disk_info['disk']
77+
disk_number = disk_info['disk_number']
78+
mount = disk_info['mount']
79+
print('%s: Formatting disk %d, %s, %s' % (platform.node(), disk_number, disk, mount))
80+
if True:
81+
assert os.system('mkfs.ext4 -q -T largefile -m 0 %s1' % disk) == 0
82+
83+
def mount_disk(disk_info):
84+
disk = disk_info['disk']
85+
disk_number = disk_info['disk_number']
86+
mount = disk_info['mount']
87+
print('%s: Mounting disk %d, %s, %s' % (platform.node(), disk_number, disk, mount))
88+
assert os.system('mkdir -p /grid/%d' % disk_number) == 0
89+
assert os.system('echo %s1\t%s\text4\tdefaults,noatime\t0\t0 >> /etc/fstab' % (disk, mount)) == 0
90+
assert os.system('mount %s' % mount) == 0
91+
92+
def main():
93+
parser = optparse.OptionParser(usage=__doc__)
94+
parser.add_option('-a', '--auto', action='store_true', dest='auto',
95+
help='automatically discover disks to prepare')
96+
parser.add_option('-m', '--discover-method', action='store', dest='discover_method', default='sdb-z',
97+
help='method to discover disks to prepare')
98+
parser.add_option('-x', '--exclude', action='append', dest='exclude',
99+
help='disk to always exclude (will not be prepared)')
100+
parser.add_option('-t', '--test', action='store_true', dest='test',
101+
help='show disks that will be prepared but do not prepare them')
102+
parser.add_option('-n', '--disk-count', action='store', dest='disk_count', type='int',
103+
help='ensure there are exactly this many disks to prepare')
104+
options, args = parser.parse_args()
105+
disks = args
106+
107+
# Get list of disk partitions to format and mount.
108+
if options.auto:
109+
disks += get_disks_to_prepare(discover_method=options.discover_method)
110+
111+
if options.exclude:
112+
disks = [d for d in disks if d not in options.exclude]
113+
114+
disk_info = [{
115+
'disk_number': disk_number, # First disk will be 0
116+
'disk': disk,
117+
'mount': '/grid/%d' % disk_number
118+
} for disk_number, disk in enumerate(disks)]
119+
120+
print('The following %d disks will be erased:' % len(disks))
121+
print(' ' + '\n '.join(disks))
122+
123+
if options.disk_count:
124+
assert len(disks) == options.disk_count
125+
126+
if options.test:
127+
return 0
128+
129+
assert os.system('mkdir -p /grid') == 0
130+
131+
# Remove /grid mounts from fstab.
132+
assert os.system('egrep -v "/grid/" /etc/fstab > /tmp/fstab ; cp /tmp/fstab /etc/fstab') == 0
133+
134+
map(umount_disk, disk_info)
135+
map(partition_disk, disk_info)
136+
map(umount_disk, disk_info)
137+
138+
# Format all disks in parallel.
139+
pool = multiprocessing.Pool(len(disks))
140+
pool.map(format_disk, disk_info)
141+
print('Format done.')
142+
143+
map(mount_disk, disk_info)
144+
145+
assert os.system('mount | grep /grid/') == 0
146+
147+
print('%s: prepare_data_disks complete.' % platform.node())
148+
149+
if __name__ == '__main__':
150+
main()

0 commit comments

Comments
 (0)