-
Notifications
You must be signed in to change notification settings - Fork 18
/
packet-block-storage-attach
executable file
·427 lines (370 loc) · 12.5 KB
/
packet-block-storage-attach
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
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
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
#!/bin/sh
# Packet.net block storage auto-attach - dlaube
set -e
[ -n "$DEBUG" ] && set -x
usage(){
echo "usage: $0 [-vhmnc] [volume_name]"
echo " -v Make verbose"
echo " -h Display help"
echo " -n Do not try to attach any volumes; just ensure that iscsid and multipath are set up correctly"
echo " -c Do not start or restart multipathd or iscsid; just set up the correct configuration and ensure dependencies are installed"
echo " -m Set multipath feature no_path_retry, ex: -m [<fail (default) | queue>]"
echo " If block storage is unreachable, the option "fail" results in FS read-only and "queue" will keep IO in memory buffer until reachable"
echo " volume_name Specify the volume name to attach, eg: volume-f9a8a263"
exit 1
}
hasSystemctl=0
set +e
systemctl 2>/dev/null | grep -q '^\s*\-\.mount'
[ $? -eq 0 ] && hasSystemctl=1
set -e
restart_all() {
local configonly="$1"
if [ $configonly -ne 1 ]; then
[ $_V -eq 1 ] && echo "restarting iscsid and resetting multipath"
restart_iscsid
sleep 2
multipath
fi
}
restart_iscsid() {
if [ "$hasSystemctl" -eq 1 ]; then
[ $_V -eq 1 ] && echo "Restarting iscsid with systemctl"
systemctl restart iscsid &>/dev/null
if systemctl -q is-active open-iscsi; then
systemctl restart open-iscsi &>/dev/null
fi
# for alpine
elif [ -f /etc/init.d/iscsid -a ! -h /etc/init.d/iscsid ]; then
[ $_V -eq 1 ] && echo "Restarting iscsid with sysv init"
/etc/init.d/iscsid restart &>/dev/null
fi
if [ -f /etc/init.d/open-iscsi -a ! -h /etc/init.d/open-iscsi ]; then
[ $_V -eq 1 ] && echo "Restarting iscsid with sysv init"
/etc/init.d/open-iscsi restart &>/dev/null
fi
}
attach_volumes() {
local volumecnt="$1"
local targetidx="$2"
local metadatapath="$3"
if [ "$targetidx" ] ; then
attach_volume "$targetidx" "$metadatapath"
else
idx=0
while [ $idx -lt $volumecnt ]; do
attach_volume "$idx" "$metadatapath"
idx=$(( $idx + 1 ))
done
fi
}
attach_volume() {
local volume="$1"
local metadatapath="$2"
if [ -z "$volume" ]; then
echo "Error: no volume index passed to attach_volume () function"
exit 1
fi
[ $_V -eq 1 ] && echo "attaching volume index \"$volume\"."
iqn=`jq -r '.volumes['$volume'].iqn ' $metadatapath `
portals=`jq -r '.volumes['$volume'].ips[] ' $metadatapath `
volname=`jq -r '.volumes['$volume'].name ' $metadatapath `
for portal in ${portals}; do
echo "portal: $portal iqn: $iqn"
# Discover
if iscsiadm --mode discovery --type sendtargets --portal $portal --discover &>/dev/null ; then
[ $_V -eq 1 ] && echo "Discovery success on $portal"
else
echo "Error: We couldn't discover targets on $portal"
fi
# Login and attach
if iscsiadm --mode node --targetname $iqn --portal $portal --login &>/dev/null ; then
[ $_V -eq 1 ] && echo "Logged in iqn $iqn"
sleep 3
bdname=`ls -l /dev/disk/by-path/ | grep "$iqn" | grep $portal | awk {'print $11'} | sed 's/..\/..\///'`
wwid=`/lib/udev/scsi_id -g -u -d /dev/$bdname`
[ $_V -eq 1 ] && echo "Block device $bdname aka $volname is mapped to $iqn with WWID $wwid"
[ $_V -eq 1 ] && echo "updating /etc/multipath/bindings with $volname $wwid"
if `grep -q "^$volname" /etc/multipath/bindings`; then
sed -i "s/^$volname .*/$volname $wwid/" /etc/multipath/bindings
else
echo "$volname $wwid" >> /etc/multipath/bindings
fi
multipath $volname
else
echo "Error: We couldn't log in iqn $iqn"
fi
done
}
# install the right package for the given package manager
# arg order: apt (debian/ubuntu), yum (centos/rhel), apk (alpine)
install_package() {
local ranonce="$1"
local apt="$2"
local yum="$3"
local apk="$4"
local install_command=""
local update_command=""
local pkgs=""
local dep=""
if type apt &>/dev/null; then
install_command="apt install -y"
update_command="apt-get update -y"
pkgs="$apt"
elif type yum &>/dev/null; then
install_command="yum install -y"
update_command="yum makecache fast"
pkgs="$yum"
elif type apk &>/dev/null; then
install_command="apk add"
update_command="apk update"
pkgs="$apk"
else
echo "Unknown packaging system. Exiting." >&2
exit 1
fi
# if necessary, update package system cache
if [ -z "$ranonce" ]; then
$update_command
fi
for dep in $pkgs; do
$install_command $dep
done
}
ensure_deps() {
# Check for deps - check each one independently, because each has its own unique package name
# track if we have run update or not
local ranonce=""
# jq
if [ ! `which jq` ]; then
echo "JQ was not found. Installing..."
# jq requires epel first
install_package "$ranonce" jq "epel-release jq" jq
ranonce="true"
fi
# iscsi requirements
if [ ! `which iscsiadm` ]; then
echo "iscsi was not found. Installing..."
install_package "$ranonce" open-iscsi iscsi-initiator-utils open-iscsi
ranonce="true"
fi
# multipath
if [ ! `which multipath` ]; then
echo "multipath was not found. Installing..."
install_package "$ranonce" multipath-tools device-mapper-multipath multipath-tools
ranonce="true"
fi
}
enable_multipath() {
# Check for multipath support
set +e
lsmod | grep -q dm_multipath
local found=$?
set -e
if [ $found -eq 0 ]; then
[ $_V -eq 1 ] && echo "Multipath module is loaded."
return 0
else
[ $_V -eq 1 ] && echo "Multipath module not yet loaded. Restarting service."
# Run multipath
if [ "$hasSystemctl" -eq 1 ]; then
[ $_V -eq 1 ] && echo "Restarting multipath with systemctl"
modprobe dm_multipath
modprobe dm_round_robin
systemctl restart multipathd
systemctl enable multipathd
elif [ -f /etc/init.d/multipath-tools -a ! -h /etc/init.d/multipath-tools ]; then
[ $_V -eq 1 ] && echo "Restarting multipath with sysv init"
/etc/init.d/multipath-tools restart &>/dev/null
# for alpine linux
elif [ -f /etc/init.d/multipathd -a ! -h /etc/init.d/multipathtd ]; then
[ $_V -eq 1 ] && echo "Restarting multipath with sysv init"
/etc/init.d/multipathd restart &>/dev/null
fi
# Restart multipath with sysv init to fix ubuntu/deb
if [ -f /etc/init.d/multipath-tools -a ! -h /etc/init.d/multipath-tools ]; then
[ $_V -eq 1 ] && echo "Restarting multipath with sysv init"
/etc/init.d/multipath-tools restart &>/dev/null
fi
fi
true
}
create_multipath_config() {
local mpnpropt="$1"
# create the multipath config
cat <<- EOF_mpconf > /etc/multipath.conf
defaults {
polling_interval 3
fast_io_fail_tmo 5
path_selector "round-robin 0"
rr_min_io 100
rr_weight priorities
failback immediate
no_path_retry $mpnpropt
user_friendly_names yes
}
blacklist {
devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
devnode "^hd[a-z][[0-9]*]"
devnode "^vd[a-z]"
devnode "^cciss!c[0-9]d[0-9]*[p[0-9]*]"
device {
vendor "Micron"
product ".*"
}
device {
vendor "Intel"
product ".*"
}
device {
vendor "DELL"
product ".*"
}
}
devices {
device {
vendor "DATERA"
product "IBLOCK"
path_grouping_policy group_by_prio
path_checker tur
#checker_timer 5
#prio_callout "/sbin/mpath_prio_alua /dev/%n"
hardware_handler "1 alua"
}
}
EOF_mpconf
}
validate_iscsi_config() {
# iscsid config check - fix for Ubuntu/Debian since they default to manual
iscsiconf="/etc/iscsi/iscsid.conf"
if `grep -q 'node.startup = manual' "$iscsiconf"` || \
`grep -q 'node.session.timeo.replacement_timeout = 120' "$iscsiconf"` || \
`grep -q 'node.session.timeo.replacement_timeout = 15' "$iscsiconf"` || \
`grep -q 'node.conn\[0\].timeo.noop_out_interval = 5' "$iscsiconf"` || \
`grep -q 'node.conn\[0\].timeo.noop_out_timeout = 5' "$iscsiconf"`; then
echo "updating iscsid.conf"
sed -i.bak 's/node.startup = manual/node.startup = automatic/g' /etc/iscsi/iscsid.conf
# Adjust timeout settings for decreased queue time during failover
# Adjust for Ubuntu/Debian specific defaults
sed -i.bak 's/node.session.timeo.replacement_timeout = 120/node.session.timeo.replacement_timeout = 5/g' $iscsiconf
# Adjust for RHEL/CentOS specific defaults
sed -i.bak 's/node.session.timeo.replacement_timeout = 15/node.session.timeo.replacement_timeout = 5/g' $iscsiconf
sed -i.bak 's/node.conn\[0\].timeo.noop_out_interval = 5/node.conn\[0\].timeo.noop_out_interval = 3/g' $iscsiconf
sed -i.bak 's/node.conn\[0\].timeo.noop_out_timeout = 5/node.conn\[0\].timeo.noop_out_timeout = 3/g' $iscsiconf
fi
}
update_initiator() {
# Initiator check
local initiator="$1"
if [ ! `grep $initiator\$ /etc/iscsi/initiatorname.iscsi` ]; then
[ $_V -eq 1 ] && echo "Initiator name mismatch! Updating from metadata..."
echo "InitiatorName=$initiator" > /etc/iscsi/initiatorname.iscsi
fi
}
get_target_idx() {
local target="$1"
local volumecnt="$2"
local metadatapath="$3"
local targetidx=
if [ $volumecnt -lt "1" ]; then
echo "Error: No volume(s) associated with this server. Have you attached a volume to this server via the Packet.net API/Portal?" >&2
exit 1
fi
if [ "$target" ] ; then
idx=0
while [ $idx -lt $volumecnt ]; do
volname=`jq -r '.volumes['$idx'].name ' $metadatapath `
if [ "$volname" = "$target" ] ; then
targetidx=$idx
idx=$(( $idx + 1 ))
continue
fi
idx=$(( $idx + 1 ))
done
if [ ! "$targetidx" ] ; then
echo "target not found, is it connected to this host? : $target" >&2
exit 1
fi
fi
echo $targetidx
}
clean_mpath_bindings() {
# cleanup mpath entries in bindings file
# remove old ones if the file already exists
[ -f /etc/multipath/bindings ] && sed -i "/^mpath.*/d" /etc/multipath/bindings
# add all of the existing known ones
if `ls /dev/mapper/mpath* >/dev/null 2>/dev/null`; then
[ $_V -eq 1 ] && echo "mpath volume entries found, cleaning up"
for i in `ls /dev/mapper/mpath* | cut -d / -f4`; do
multipath -f $i
done
multipath
fi
}
report_status() {
# Check for block device(s)
local volumecnt="$1"
local metadatapath="$2"
volume=0
while [ $volume -lt $volumecnt ]; do
volname=`jq -r '.volumes['$volume'].name ' $metadatapath`
thiswwid=$(grep $volname /etc/multipath/bindings | awk {'print $2'})
if [ -b /dev/mapper/$volname ]; then
echo "Block device /dev/mapper/$volname is available for use"
elif [ -b /dev/mapper/$thiswwid ]; then
echo "Found $volname as WWID $thiswwid. Reloading devmap..."
multipath -r
if [ -b /dev/mapper/$volname ]; then
echo "Block device /dev/mapper/$volname is available for use"
fi
else
echo "Error: Block device /dev/mapper/$volname is NOT available for use"
fi
volume=$(($volume + 1))
done
}
# Get cli options after setting defaults
_V=0
mpnpropt="fail" # multipath no_path_retry default
noattach=0
configonly=0
while getopts "m:vhnc" OPTION
do
case $OPTION in
v) _V=1;;
h) usage;;
m) mpnpropt="$OPTARG";;
n) noattach=1;;
c) configonly=1;;
*) exit 1;;
esac
done
shift $(($OPTIND - 1))
if [ $1 ] ; then
target=$1
fi
[ $_V -eq 1 ] && echo "Multipath no_path_retry feature option set as '$mpnpropt'"
ensure_deps
[ $configonly -ne 1 ] && enable_multipath
export LOCALMD=/tmp/metadata.tmp
curl -sSL https://metadata.packet.net/metadata > $LOCALMD
volumecnt=`jq '.volumes | length' $LOCALMD`
initiator=`jq -r '.iqn' $LOCALMD`
targetidx=
# Check for volumes - but only if noattach was not set
if [ $noattach -ne 1 ]; then
targetidx=$(get_target_idx "$target" "$volumecnt" "$LOCALMD")
[ "$targetidx" ] && echo "target has been set to: $target , index $targetidx"
fi
create_multipath_config "$mpnpropt"
update_initiator "$initiator"
restart_all "$configonly"
validate_iscsi_config
restart_all "$configonly"
# attach volumes - but only if noattach was not set
[ $noattach -ne 1 ] && attach_volumes "$volumecnt" "$targetidx" "$LOCALMD"
clean_mpath_bindings
# output the paths when in verbose mode
[ $_V -eq 1 ] && multipath -ll
[ $noattach -ne 1 ] && report_status "$volumecnt" "$LOCALMD"
exit 0