aboutsummaryrefslogtreecommitdiff
path: root/kvm-creator
blob: 69a56d5ab9127af7dcfd9f913d9ee8fac8680179 (plain)
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
#!/bin/bash
#set -x

# Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
# Date: 2009-10-08
# License: GPL v3+

CMD="$1"
shift

NAME="$1"
# FIXME: make this default to the only vg (if only one vg exists), or to vg_$(hostname0) or $(hostname), if those VGs exist
VG="${2:-vg_$(hostname)0}"
SIZE="${3:-3G}"
RAM="${4:-512}"
DISK="/dev/mapper/${VG}-${NAME}"

# for managing udev (we want to make sure that logical volumes get
# created with ownership by the controlling user:
UDEV_RULES_FILE="/etc/udev/rules.d/92-kvm_creator-%s.rules"
# Why choose 92?  /usr/share/doc/udev/README.Debian.gz says after 91
# default permissions and ownership are set.

[ "$CMD" == "create" ] && mkdir -p /etc/sv/kvm

ls /etc/sv/kvm/* &> /dev/null
if [ "$?" -eq 0 ]; then
  TAP="${5:-tap$(( $( cat /etc/sv/kvm/*/env/TAP | sed 's/^tap//' | sort -n  | tail -n 1 ) + 1 ))}"
  MAC="${6:-$(printf "%s:%02x" "$(cat /etc/sv/kvm/*/env/MAC | sort | head -n1 | cut -f 1-5 -d: )" "$(( 0x$( cat /etc/sv/kvm/*/env/MAC | cut -f 6 -d:  | sort -n  | tail -n 1 ) + 1 ))" )}"
else
  TAP=tap0
  # use a locally-administered MAC address:
  MAC=02:00:00:00:00:01
fi

usage() {

	die "USAGE: kvm-creator create|destroy|demo guestname [volumegroup [disksize [ram [tap [mac] ] ] ] ]"

}

die() {

  echo "$1"
  exit_code=1
  [ -n "$2" ] && exit_code="$2"
  exit $exit_code

}

destroy() {

    update-service --remove "/etc/sv/kvm/$NAME"
    rm -rf "/etc/sv/kvm/$NAME"
    deluser --remove-home "$NAME"
    lvremove "$VG/$NAME"
    rm -f "$(udevrulename "$NAME")"

}

validate() {

  errors=""

  # Make sure none of the pieces already exist.
  [ -z "$NAME" ] && errors=$(printf "%s\n%s" "$errors" "Please pass the name of the virtual server to create") || :
  [ -z "$VG" ] && errors=$(printf "%s\n%s" "$errors"  "Please pass the name of the volume group to use") || :
  vgs --noheadings --unbuffered -o name | tr -d ' ' | grep -q -F -x "$VG" || errors=$(printf "%s\n%s" "$errors"  "Please pass the name of the volume group to use") || :
  getent passwd "$NAME" > /dev/null && errors=$(printf "%s\n%s" "$errors" "The username '$NAME' already exists.") || :
  getent group "$NAME" > /dev/null && errors=$(printf "%s\n%s" "The group '$NAME' already exists.") || :
  [ -d /home/"$NAME" ] && errors=$(printf "%s\n%s" "The directory '/home/$NAME' already exists.") || :
  [ -d /etc/sv/kvm/"$NAME" ] && errors=$(printf "%s\n%s" "The directory '/etc/sv/kvm/$NAME' already exists.") || :
  [ -e $DISK ] && errors=$(printf "%s\n%s" "The logical volume $NAME already exists.") || :
  [ -e "$(udevrulename "$NAME")" ] && errors=$(printf "%s\n%s" "The udev rules file '$(udevrulename "$NAME")' already exists.") || :

  [ -z "$errors" ] || die "$errors"

}

udevrule() {
    VOLUME_GROUP="$1"
    LOGICAL_VOLUME="$2"
    GROUP="$3"

    # this appears to be the way that a udev rule to control the LVM device gets created:
    printf 'ACTION=="change", SUBSYSTEM=="block", ATTR{dm/name}=="%s-%s", GROUP="%s"\n' "$VOLUME_GROUP" "$LOGICAL_VOLUME" "$GROUP" 

    # unfortunately, kernel 2.6.26-2-amd64 (from debian) does not
    # appear to export /sys/block/dm-%d/dm/name ; nether does
    # 2.6.28-16-generic (ubuntu);  2.6.30-2-686 *does* export that
    # label, so the above line works on squeeze at the moment.

    # not sure what to do about this for lenny systems.  See:
    # http://bugs.debian.org/450793
}

udevrulename() {
    printf "$UDEV_RULES_FILE" "$1"
}

create() {

    set -e
    validate
    adduser "$NAME" --disabled-password --gecos "$NAME KVM user,,,"
    USERHOMEDIR=$(getent passwd "$NAME" | cut -f6 -d:)
    mkdir -p "$USERHOMEDIR/"{.ssh,vms,"vms/$NAME"}
    chown "$NAME:$NAME" "$USERHOMEDIR/"{.ssh,vms,"vms/$NAME"}
    if [ -f /root/.ssh/authorized_keys ]; then
        # is this really the right thing to do?
        cp /root/.ssh/authorized_keys "$USERHOMEDIR/.ssh/"
    fi
    USERGID="$(getent passwd "$NAME")"
    USERGID="$(cut -f4 -d: <<<$USERGID)"
    USERGROUP="$(getent group "$USERGID")"
    USERGROUP="${USERGROUP%%:*}"
 
    udevrule "$VG" "$NAME" "$USERGROUP" > $(udevrulename "$NAME")

    lvcreate --name "$NAME" --size "$SIZE" $VG
    mkdir "/etc/sv/kvm/$NAME"{,/log,/env}
    cat > "/etc/sv/kvm/$NAME/log/run" <<EOF
#!/bin/sh

exec 2>&1
exec chpst -e ../env /usr/local/sbin/kvm-manager log
EOF
    cat > "/etc/sv/kvm/$NAME/run" <<EOF
#!/bin/sh

exec 2>&1
exec chpst -e ./env /usr/local/sbin/kvm-manager up
EOF
    cat > "/etc/sv/kvm/$NAME/finish" <<EOF
#!/bin/sh

exec 2>&1
exec chpst -e ./env /usr/local/sbin/kvm-manager down
EOF
    chmod a+x "/etc/sv/kvm/$NAME/"{run,finish,log/run}
    echo "$NAME" > "/etc/sv/kvm/$NAME/env/OWNER"
    echo "$NAME" > "/etc/sv/kvm/$NAME/env/VMNAME"
    echo "$TAP" > "/etc/sv/kvm/$NAME/env/TAP"
    echo "$RAM" > "/etc/sv/kvm/$NAME/env/RAM"
    echo "$MAC" > "/etc/sv/kvm/$NAME/env/MAC"
    echo "$DISK" > "/etc/sv/kvm/$NAME/env/HDA"

}

demo() {

    validate
    for foo in NAME VG TAP RAM MAC SIZE DISK ; do
      echo "$foo : ${!foo}"
    done

}

[ "$CMD" != "create" ] && [ "$CMD" != "destroy" ] && [ "$CMD" != "demo" ] && usage

"$CMD"