~ubuntu-on-ec2/vmbuilder/jenkins_kvm-kvp-telemetry-xenial

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
#!/bin/bash
usage() {
cat << EOF
This program is a KVM wrapper for performing tasks inside a KVM Environment.
Its primary goal is to help developers do dangerous tasks that their IS/IT
deparment won't allow them to do on an existing machine.
    --id <ARG>           The ID you want to use to identify the KVM image
                         this is used to name the image
    --disk-gb <ARG>      Disk size you want to resize the image too
                         Default it to _add_ 30GB
    --smp <ARG>          KVM SMP options, defaults to:
                         ${smp_opt}
    --mem <ARG>          How much RAM do you want to use
    --user-data <ARG>    Cloud-Init user-data file
    --cloud-config <ARG> Cloud-Init cloud-config file
    --img-url <ARG>      Location of the image file.
    --raw-disk <ARG>     Name of RAW disk to create and attach.
    --raw-size <ARG>     Size of RAW disk in GB.
    --extra-disk <ARG>   Add an extra disk, starting with /dev/vdd
    --cloud-init-file <ARG> Additional file for the cloud-init data
EOF
exit 1
}

short_opts="h"
long_opts="id:,ssh_port,disk-gb:,mem:,bzr-automated-ec2-builds:,cloud-config:,user-data:,kernel-url:,img-url:,raw-disk:,raw-size:,smp:,extra-disk:,cloud-init-file:,help"
getopt_out=$(getopt --name "${0##*/}" \
    --options "${short_opts}" --long "${long_opts}" -- "$@") &&
    eval set -- "${getopt_out}" ||
    usage

builder_id=$(uuidgen)
uuid=${builder_id}
bname="server"
size_gb=15
mem=512
smp_opt="4"
ud=""
cloud_config=""
img_loc="${BUILDER_CLOUD_IMAGE:-http://cloud-images.ubuntu.com/releases/precise/release/ubuntu-12.04-server-cloudimg-amd64-disk1.img}"
KVM_PID=""

while [ $# -ne 0 ]; do
    cur=${1}; next=${2};
    case "$cur" in
    --id)                       id="$2"; shift;;
    --disk-gb)                  size_gb="$2"; shift;;
    --mem)                      mem="$2"; shift;;
    --cloud-config)             ccloud="$2"; shift;;
    --user-data)                ud="$2"; shift;;
    --img-url)                  img_loc="$2"; shift;;
    --raw-disk)                 raw_disk="$2"; shift;;
    --raw-size)                 raw_size="$2"; shift;;
    --smp)                      smp_opts="$2"; shift;;
    --extra-disk)               [ -z "${extra_disk}" ] && extra_disk=$2 || extra_disk="${extra_disk} $2"; shift;;
    --cloud-init-file)          [ -z "${cloud_init_files}" ] && cloud_init_files=$2 || cloud_init_files="${cloud_init_files} $2"; shift;;
    -h|--help)                  usage; exit 0;;
    --) shift; break;;
  esac
  shift;
done

work_d="$(mktemp -d /tmp/kvm-builder.XXXX)"
kvm_pidfile="$(mktemp --tmpdir=${work_d})"

error() { echo "$@" 1>&2; }
cleanup() {
        [ -n "${KVM_PID}" ] && kill -9 ${KVM_PID};
        [ -n "${TAIL_PID}" ] && kill -9 ${TAIL_PID};
        rm -rf "${work_d}";
}
fail() { error "$@"; cleanup;  exit 1; }
debug() { error "$(date -R):" "$@"; }
sysfail() { fail "Failure in commands detected; purging "; }

# Make sure that we kill everything
trap sysfail SIGINT SIGTERM

[ -z "${ud}" ] && fail "Must define user-data script via --user-data"
[ -z "${ccloud}" ] && fail "Must define cloud-config script via --cloud-config"

debug "Creating Cloud-Init configuration..."
write_mime_args=(
    -o "${work_d}/user-data.txt"
    "${ccloud}"
    "${ud}")
write_mime_args+=(${cloud_init_files[@]})
write_mime_location="$(which write-mime-multipart)"
if which python3 > /dev/null; then
    "${write_mime_location}" ${write_mime_args[@]} || fail "Unable to create user-data"
else
    python "${write_mime_location}" ${write_mime_args[@]} || fail "Unable to create user-data"
fi

echo "instance-id: $(uuidgen)" > "${work_d}/meta-data"
echo "local-hostname: builder" >> "${work_d}/meta-data"

debug "Creating Seed for Cloud-Init..."
"${0%/*}/make-seed.sh" "${work_d}/seed.img" "${work_d}/user-data.txt" "${work_d}/meta-data" ||
    fail "Failed to create Configruation ISO"

# Place the image in place
debug "Build image location is ${img_loc}"
if [[ "${img_loc}" =~ "http" ]]; then
    debug "Fetching cloud image from ${img_loc}"
    curl -s -o "${work_d}/img-${builder_id}" "${img_loc}" ||
        fail "Unable to fetch pristine image from '${img_loc}'"
else
    cp "${img_loc}" "${work_d}/img-${builder_id}" ||
        fail "Unable to copy '${img_loc}'"
fi

debug "Adding ${size_gb}G to image size"
qemu-img resize "${work_d}/img-${builder_id}" +"${size_gb}G" ||
    fail "Unable to resize image to ${size_gb}G"

if [ -n "${raw_disk}" -a ! -e "${raw_disk}" ]; then
    if [ -n "${raw_size}" ]; then
        dd if=/dev/zero of=${raw_disk} bs=1k count=1 seek=$((${raw_size} * 1024000)) &&
            debug "Create new raw disk" ||
            fail "Unable to create raw disk"
    else
        fail "Undefined raw disk size"
    fi
else
    debug "Using existing raw disk."
fi


debug "________________________________________________"
debug "Launching instance..."
kvm_cmd=(
   ${QEMU_COMMAND:-kvm}
   -name ${uuid}
   -drive file=${work_d}/img-${builder_id},if=virtio,bus=0,cache=unsafe,unit=0
   -drive file=${raw_disk},if=virtio,format=raw,bus=0,unit=1
   -drive file=${work_d}/seed.img,if=virtio,media=cdrom,bus=0,cache=unsafe,unit=2
   -net nic,model=virtio
   -net user
   -no-reboot
   -display none
   -daemonize
   -serial file:${work_d}/console.log
   -pidfile ${kvm_pidfile}
   )
kvm_cmd+=(${QEMU_ARGS[@]})

# Arch independant stuff
if [[ "$(uname -p)" =~ "ppc64" ]]; then
    # Use more memory for building on PPC64
    kvm_cmd+=(-m 4G)
else
    kvm_cmd+=(-smp ${smp_opt} -m ${mem})
fi

# Allow for kernel and append
if [ -n "${QEMU_KERNEL}" ]; then
     root="/dev/vda1"
     if [[ "$(uname -p)" =~ "ppc64" ]]; then
         root="/dev/vda"
     fi
     kvm_cmd+=(-kernel ${QEMU_KERNEL}
               -append "earlyprintk root=${root} console=hvc0"
              )
fi

unit_c=3
for disk in ${extra_disk}
do
    if [[ $(file ${disk}) =~ (disk|qcow|QCOW|vmdk|VMDK|vdi|VDI) ]]; then
        debug "Adding extra disk $disk to KVM configuration"
        kvm_cmd+=(-drive file=${extra_disk},if=virtio,bus=1,unit=${unit_c})
    else
        debug "Adding extra disk as a raw formated disk"
        kvm_cmd+=(-drive file=${extra_disk},if=virtio,format=raw,bus=1,unit=${unit_c})
    fi
    unit_c=$((unit_c+1))
done

debug "KVM command is: ${kvm_cmd[@]}"
"${kvm_cmd[@]}" ||
    fail "Failed to launch KVM image\n${kvm_out}"

read KVM_PID < ${kvm_pidfile}
debug "KVM PID is: ${KVM_PID}"

tail -f "${work_d}/console.log" &
TAIL_PID=$!

# Wait on the pid until the max timeout
count=0
max_count=${MAX_CYCLES:-720}
while $(ps ${KVM_PID} > /dev/null 2>&1)
do
    sleep 10
    count=$((count + 1))
    if [ "${count}" -gt "${max_count}" ]; then
        kill -15 ${KVM_PID}
        debug "Build timed out...killing PID ${KVM_PID}"
    fi
done

debug "________________________________________________"
debug "KVM PID has ended. Work is done"
kill -15 ${TAIL_PID}

unset KVM_PID
unset TAIL_PID

[ -n "${raw_disk}" ] &&
    debug "Extracting raw tarball" &&
    { tar xvvf "${raw_disk}" || /bin/true; }

[ ! -e success ] &&
    fail "Tarball contents reported failure"

cp "${work_d}/console.log" .

# Wait for Cloud-Init to finish any work
debug "Cleaning up..."
cleanup
exit 0