Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
M
mininet
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Deploy
Releases
Package Registry
Model registry
Operate
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Olaf Bergmann
mininet
Commits
85dfac5c
Commit
85dfac5c
authored
11 years ago
by
Bob Lantz
Browse files
Options
Downloads
Patches
Plain Diff
Many more improvements.
parent
860bcc02
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
util/vm/build.py
+153
-75
153 additions, 75 deletions
util/vm/build.py
with
153 additions
and
75 deletions
util/vm/build.py
+
153
−
75
View file @
85dfac5c
...
@@ -46,13 +46,16 @@
...
@@ -46,13 +46,16 @@
import
os
import
os
from
os
import
stat
from
stat
import
ST_MODE
from
os.path
import
exists
,
splitext
from
os.path
import
exists
,
splitext
from
sys
import
exit
,
argv
from
glob
import
glob
from
glob
import
glob
from
urllib
import
urlretrieve
from
urllib
import
urlretrieve
from
subprocess
import
check_output
,
call
,
Popen
,
PIPE
from
subprocess
import
check_output
,
call
,
Popen
,
PIPE
from
tempfile
import
mkdtemp
,
NamedTemporaryFile
from
tempfile
import
mkdtemp
,
NamedTemporaryFile
from
sys
import
exit
from
time
import
time
from
time
import
time
import
argparse
# boot can be slooooow!!!! need to debug/optimize somehow
# boot can be slooooow!!!! need to debug/optimize somehow
TIMEOUT
=
600
TIMEOUT
=
600
...
@@ -60,7 +63,10 @@
...
@@ -60,7 +63,10 @@
VMImageDir
=
os
.
environ
[
'
HOME
'
]
+
'
/vm-images
'
VMImageDir
=
os
.
environ
[
'
HOME
'
]
+
'
/vm-images
'
ImageURLBase
=
{
ImageURLBase
=
{
'
raring-server-amd64
'
:
'
raring32server
'
:
'
http://cloud-images.ubuntu.com/raring/current/
'
'
raring-server-cloudimg-i386
'
,
'
raring64server
'
:
'
http://cloud-images.ubuntu.com/raring/current/
'
'
http://cloud-images.ubuntu.com/raring/current/
'
'
raring-server-cloudimg-amd64
'
'
raring-server-cloudimg-amd64
'
}
}
...
@@ -78,17 +84,17 @@ def srun( cmd, **kwargs ):
...
@@ -78,17 +84,17 @@ def srun( cmd, **kwargs ):
return
run
(
'
sudo
'
+
cmd
,
**
kwargs
)
return
run
(
'
sudo
'
+
cmd
,
**
kwargs
)
# Install necessary packages
def
depend
():
print
'
*
Install
ing
package
dependencies
'
"
Install packagedependencies
"
packages
=
(
)
print
'
* Installing package dependencies
'
run
(
'
sudo apt-get -y update
'
)
packages
=
(
)
run
(
'
sudo apt-get
install -y
'
run
(
'
sudo apt-get
-y update
'
)
'
kvm cloud-utils genisoimage qemu-kvm qemu-utils
'
run
(
'
sudo apt-get install -y
'
'
e2fsprogs
'
'
kvm cloud-utils genisoimage qemu-kvm qemu-utils
'
'
landscape-client
'
'
e2fsprogs
'
'
python-setuptools
'
)
'
landscape-client
'
run
(
'
sudo easy_install pexpect
'
)
'
python-setuptools
'
)
import
pexpect
run
(
'
sudo easy_install
pexpect
'
)
def
popen
(
cmd
):
def
popen
(
cmd
):
...
@@ -127,6 +133,11 @@ def fetchImage( image, path=None ):
...
@@ -127,6 +133,11 @@ def fetchImage( image, path=None ):
floppy
=
path
+
'
-floppy
'
floppy
=
path
+
'
-floppy
'
if
exists
(
disk
)
and
exists
(
kernel
):
if
exists
(
disk
)
and
exists
(
kernel
):
print
'
* Found
'
,
disk
,
'
and
'
,
kernel
print
'
* Found
'
,
disk
,
'
and
'
,
kernel
# Detect race condition with multiple builds
perms
=
stat
(
disk
)[
ST_MODE
]
&
0777
if
perms
!=
0444
:
raise
Exception
(
'
Error - %s is writable
'
%
disk
+
'
; are multiple builds running?
'
)
else
:
else
:
dir
=
os
.
path
.
dirname
(
path
)
dir
=
os
.
path
.
dirname
(
path
)
run
(
'
mkdir -p %s
'
%
dir
)
run
(
'
mkdir -p %s
'
%
dir
)
...
@@ -136,13 +147,10 @@ def fetchImage( image, path=None ):
...
@@ -136,13 +147,10 @@ def fetchImage( image, path=None ):
urlretrieve
(
url
,
tgz
)
urlretrieve
(
url
,
tgz
)
print
'
* Extracting
'
,
tgz
print
'
* Extracting
'
,
tgz
run
(
'
tar -C %s -xzf %s
'
%
(
dir
,
tgz
)
)
run
(
'
tar -C %s -xzf %s
'
%
(
dir
,
tgz
)
)
# Make sure Mininet user is there
# Write-protect disk image so it remains pristine;
os
.
chmod
(
disk
,
0664
)
# We will not use it directly but will use a COW disk
addMininetUser
(
disk
)
print
'
* Write-protecting disk image
'
,
disk
# Write-protect disk image so it remains somewhat pristine;
os
.
chmod
(
disk
,
0444
)
# We will not use it directly but will use a COW disk
print
'
* Write-protecting disk image
'
,
disk
os
.
chmod
(
disk
,
0444
)
return
disk
,
kernel
return
disk
,
kernel
...
@@ -162,14 +170,14 @@ def disableCloud( bind ):
...
@@ -162,14 +170,14 @@ def disableCloud( bind ):
call
(
'
echo manual | sudo tee
'
+
override
,
shell
=
True
)
call
(
'
echo manual | sudo tee
'
+
override
,
shell
=
True
)
def
addMininetUser
(
img
):
def
addMininetUser
(
nbd
):
"
Add mininet user/group to
root
filesystem
image
"
"
Add mininet user/group to filesystem
"
print
'
* Adding mininet user to
'
,
img
print
'
* Adding mininet user to
filesystem on device
'
,
nbd
# 1. We bind-mount / into a temporary directory, and
# 1. We bind-mount / into a temporary directory, and
# then mount the volume's /etc and /home on top of it!
# then mount the volume's /etc and /home on top of it!
mnt
=
mkdtemp
()
mnt
=
mkdtemp
()
bind
=
mkdtemp
()
bind
=
mkdtemp
()
srun
(
'
mount %s %s
'
%
(
img
,
mnt
)
)
srun
(
'
mount %s %s
'
%
(
nbd
,
mnt
)
)
srun
(
'
mount -B /
'
+
bind
)
srun
(
'
mount -B /
'
+
bind
)
srun
(
'
mount -B %s/etc %s/etc
'
%
(
mnt
,
bind
)
)
srun
(
'
mount -B %s/etc %s/etc
'
%
(
mnt
,
bind
)
)
srun
(
'
mount -B %s/home %s/home
'
%
(
mnt
,
bind
)
)
srun
(
'
mount -B %s/home %s/home
'
%
(
mnt
,
bind
)
)
...
@@ -202,66 +210,79 @@ def chroot( cmd ):
...
@@ -202,66 +210,79 @@ def chroot( cmd ):
run
(
'
rmdir
'
+
bind
)
run
(
'
rmdir
'
+
bind
)
run
(
'
rmdir
'
+
mnt
)
run
(
'
rmdir
'
+
mnt
)
# 4. Just to make sure, we check the filesystem
# 4. Just to make sure, we check the filesystem
run
(
'
e2fsck -y
'
+
img
)
srun
(
'
e2fsck -y
'
+
nbd
)
def
connectCOWdevice
(
cow
):
"
Attempt to connect a COW disk and return its nbd device
"
print
'
* Checking for unused /dev/nbdX device
'
,
for
i
in
range
(
0
,
63
):
nbd
=
'
/dev/nbd%d
'
%
i
print
i
,
# Check whether someone's already messing with that device
if
call
(
[
'
pgrep
'
,
'
-f
'
,
nbd
]
)
==
0
:
continue
# Fails without -v for some annoying reason...
print
srun
(
'
qemu-nbd -c %s %s
'
%
(
nbd
,
cow
)
)
return
nbd
raise
Exception
(
"
Error: could not find unused /dev/nbdX device
"
)
def
disconnectCOWdevice
(
nbd
):
srun
(
'
qemu-nbd -d
'
+
nbd
)
def
makeCOWDisk
(
image
):
def
makeCOWDisk
(
image
):
"
Create new COW disk for image
"
"
Create new COW disk for image
"
disk
,
kernel
=
fetchImage
(
image
)
disk
,
kernel
=
fetchImage
(
image
)
cow
=
disk
+
'
.qcow2
'
cow
=
NamedTemporaryFile
(
prefix
=
image
+
'
-
'
,
suffix
=
'
.qcow2
'
,
dir
=
'
.
'
).
name
print
'
* Creating COW disk
'
,
cow
print
'
* Creating COW disk
'
,
cow
remove
(
cow
)
run
(
'
qemu-img create -f qcow2 -b %s %s
'
%
(
disk
,
cow
)
)
run
(
'
qemu-img create -f qcow2 -b %s %s
'
%
(
disk
,
cow
)
)
print
'
* Resizing COW disk and file system
'
print
'
* Resizing COW disk and file system
'
run
(
'
qemu-img resize %s +8G
'
%
cow
)
run
(
'
qemu-img resize %s +8G
'
%
cow
)
srun
(
'
modprobe nbd
'
)
srun
(
'
modprobe nbd max-part=64
'
)
# Ideally we should check if it's being used...
nbd
=
connectCOWdevice
(
cow
)
srun
(
'
qemu-nbd -d /dev/nbd0
'
)
srun
(
'
e2fsck -y
'
+
nbd
)
srun
(
'
qemu-nbd -c /dev/nbd0
'
+
cow
)
srun
(
'
resize2fs
'
+
nbd
)
srun
(
'
e2fsck -fy /dev/nbd0
'
)
addMininetUser
(
nbd
)
srun
(
'
resize2fs /dev/nbd0
'
)
disconnectCOWdevice
(
nbd
)
srun
(
'
qemu-nbd -d /dev/nbd0
'
)
return
cow
,
kernel
return
cow
,
kernel
def
boot
(
cow
,
kernel
):
def
boot
(
cow
,
kernel
,
tap
):
"""
Boot qemu/kvm with a COW disk and local/user data store
"""
Boot qemu/kvm with a COW disk and local/user data store
returns: popen object to qemu process
"""
cow: COW disk path
if
'
amd64
'
in
cow
:
kernel: kernel path
tap: tap device to connect to VM
returns: pexpect object to qemu process
"""
# pexpect might not be installed until after depend() is called
global
pexpect
import
pexpect
if
'
amd64
'
in
kernel
:
kvm
=
'
qemu-system-x86_64
'
kvm
=
'
qemu-system-x86_64
'
elif
'
i386
'
in
cow
:
elif
'
i386
'
in
kernel
:
kvm
=
'
qemu-system-i386
'
kvm
=
'
qemu-system-i386
'
else
:
else
:
print
"
Error: can
'
t discern CPU for image
"
,
cow
print
"
Error: can
'
t discern CPU for image
"
,
cow
exit
exit
(
1
)
# was -nographic
# was -net=%net
# ' -net nic,model=virtio'
# ' -netdev tap,id=mininet0,ifname=%s,script=no ' % tap +
cmd
=
[
'
sudo
'
,
kvm
,
cmd
=
[
'
sudo
'
,
kvm
,
'
-machine
'
,
'
accel=kvm
'
,
'
-machine
accel=kvm
'
,
'
-nographic
'
,
'
-nographic
'
,
'
-m
'
,
'
512
'
,
'
-netdev user,id=mnbuild
'
,
'
-k
'
,
'
en-us
'
,
'
-device virtio-net,netdev=mnbuild
'
,
'
-m 1024
'
,
'
-k en-us
'
,
'
-kernel
'
,
kernel
,
'
-kernel
'
,
kernel
,
'
-drive
'
,
'
file=%s,if=virtio
'
%
cow
,
'
-drive file=%s,if=virtio
'
%
cow
,
'
-append
'
,
'
-append
"
root=/dev/vda init=/sbin/init console=ttyS0
"
'
]
'
"
root=/dev/vda
'
'
init=/sbin/init
'
# ' init=/usr/lib/cloud-init/uncloud-init'
# ' ds=nocloud'
# ' --verbose'
'
console=ttyS0
"'
]
cmd
=
'
'
.
join
(
cmd
)
cmd
=
'
'
.
join
(
cmd
)
print
'
* STARTING VM
'
print
'
* STARTING VM
'
print
cmd
print
cmd
vm
=
pexpect
.
spawn
(
cmd
,
timeout
=
TIMEOUT
)
vm
=
pexpect
.
spawn
(
cmd
,
timeout
=
TIMEOUT
)
logfile
=
NamedTemporaryFile
(
prefix
=
'
mn-build-expect
'
)
return
vm
print
'
* Logging results to
'
,
logfile
.
name
vm
.
logfile_read
=
logfile
return
vm
,
logfile
def
interact
(
vm
):
def
interact
(
vm
):
"
Interact with vm, which is a pexpect object
"
"
Interact with vm, which is a pexpect object
"
...
@@ -288,7 +309,8 @@ def interact( vm ):
...
@@ -288,7 +309,8 @@ def interact( vm ):
print
'
* Running VM install script
'
print
'
* Running VM install script
'
vm
.
sendline
(
'
bash install-mininet-vm.sh
'
)
vm
.
sendline
(
'
bash install-mininet-vm.sh
'
)
print
'
* Waiting for script to complete...
'
print
'
* Waiting for script to complete...
'
vm
.
expect
(
'
Done preparing Mininet
'
)
# Gigantic timeout for now ;-(
vm
.
expect
(
'
Done preparing Mininet
'
,
timeout
=
3600
)
print
'
* Completed successfully
'
print
'
* Completed successfully
'
vm
.
expect
(
prompt
)
vm
.
expect
(
prompt
)
print
'
* Testing Mininet
'
print
'
* Testing Mininet
'
...
@@ -312,18 +334,74 @@ def interact( vm ):
...
@@ -312,18 +334,74 @@ def interact( vm ):
vm
.
read
()
vm
.
read
()
print
'
* Interaction complete
'
print
'
* Interaction complete
'
image
=
'
raring-server-amd64
'
start
=
time
()
cow
,
kernel
=
makeCOWDisk
(
image
)
print
'
* VM image for
'
,
image
,
'
created as
'
,
cow
vm
,
logfile
=
boot
(
cow
,
kernel
)
interact
(
vm
)
logfile
.
close
()
end
=
time
()
elapsed
=
end
-
start
print
'
* Results logged to
'
,
logfile
.
name
print
'
* Completed in %.2f seconds
'
%
elapsed
print
'
* DONE!!!!! :D
'
def
cleanup
():
"
Clean up leftover qemu-nbd processes and other junk
"
call
(
'
sudo pkill -9 qemu-nbd
'
,
shell
=
True
)
def
convert
(
cow
,
basename
):
"""
Convert a qcow2 disk to a vmdk and put it a new directory
basename: base name for output vmdk file
"""
dir
=
mkdtemp
(
prefix
=
basename
,
dir
=
'
.
'
)
vmdk
=
'
%s/%s.vmdk
'
%
(
dir
,
basename
)
print
'
* Converting qcow2 to vmdk
'
run
(
'
qemu-img convert -f qcow2 -O vmdk %s %s
'
%
(
cow
,
vmdk
)
)
return
vmdk
def
build
(
flavor
=
'
raring-server-amd64
'
):
"
Build a Mininet VM
"
start
=
time
()
cow
,
kernel
=
makeCOWDisk
(
flavor
)
print
'
* VM image for
'
,
flavor
,
'
created as
'
,
cow
with
NamedTemporaryFile
(
prefix
=
'
mn-build-%s-
'
%
flavor
,
suffix
=
'
.log
'
,
dir
=
'
.
'
)
as
logfile
:
print
'
* Logging results to
'
,
logfile
.
name
vm
=
boot
(
cow
,
kernel
,
logfile
)
vm
.
logfile_read
=
logfile
interact
(
vm
)
# cow is a temporary file and will go away when we quit!
# We convert it to a .vmdk which can be used in most VMMs
vmdk
=
convert
(
cow
,
basename
=
flavor
)
print
'
* Converted VM image stored as
'
,
vmdk
end
=
time
()
elapsed
=
end
-
start
print
'
* Results logged to
'
,
logfile
.
name
print
'
* Completed in %.2f seconds
'
%
elapsed
print
'
* %s VM build DONE!!!!! :D
'
%
flavor
print
def
parseArgs
():
"
Parse command line arguments and run
"
parser
=
argparse
.
ArgumentParser
(
description
=
'
Mininet VM build script
'
)
parser
.
add_argument
(
'
--depend
'
,
action
=
'
store_true
'
,
help
=
'
Install dependencies for this script
'
)
parser
.
add_argument
(
'
--list
'
,
action
=
'
store_true
'
,
help
=
'
list valid build flavors
'
)
parser
.
add_argument
(
'
--clean
'
,
action
=
'
store_true
'
,
help
=
'
clean up leftover build junk (e.g. qemu-nbd)
'
)
parser
.
add_argument
(
'
flavor
'
,
nargs
=
'
*
'
,
help
=
'
VM flavor to build (e.g. raring32server)
'
)
args
=
parser
.
parse_args
(
argv
)
if
args
.
depend
:
depend
()
if
args
.
list
:
print
'
valid build flavors:
'
,
'
'
.
join
(
ImageURLBase
)
if
args
.
clean
:
cleanup
()
flavors
=
args
.
flavor
[
1
:
]
for
flavor
in
flavors
:
if
flavor
not
in
ImageURLBase
:
parser
.
print_help
()
# try:
build
(
flavor
)
# except Exception as e:
# print '* BUILD FAILED with exception: ', e
# exit( 1 )
if
not
(
args
.
depend
or
args
.
list
or
args
.
clean
or
flavors
):
parser
.
print_help
()
if
__name__
==
'
__main__
'
:
parseArgs
()
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment