-
Notifications
You must be signed in to change notification settings - Fork 1
/
xlxc-create.rb
226 lines (184 loc) · 5.47 KB
/
xlxc-create.rb
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
#
# xlxc-create: create Linux XIA containers
#
# Author: Cody Doucette <doucette@bu.edu>
#
# This Ruby script creates and initializes a Linux XIA container.
#
require 'fileutils'
require 'optparse'
require 'rubygems'
require 'netaddr'
require 'ipaddr'
require './xlxc'
require './xlxc-bridge'
# Directories that need to be directly copied.
LOCAL_ETC = "./etc"
# Directories that are initially empty, but need to be created.
INITIALLY_EMPTY_DIRECTORIES = [
"/proc",
"/sys",
"/dev/pts",
"/dev/shm",
"/home/ubuntu",
"/root",
"/var/run"
]
# Character special files that need to be created.
DEV_RANDOM = "/dev/random" # for HID principal in XIA
DEV_URANDOM = "/dev/urandom" # for HID principal in XIA
# Directories that hold XIA-related data.
XIA = "/etc/xia"
XIA_HIDS = File.join(XIA, "hid/prv")
USAGE = "\nUsage: ruby xlxc-create.rb [options]\n\n"
# Parse the command and organize the options.
#
def parse_opts()
options = {}
optparse = OptionParser.new do |opts|
opts.banner = USAGE
options[:bridge] = nil
opts.on('-b', '--bridge ARG', 'Bridge name') do |bridge|
options[:bridge] = bridge
end
options[:name] = nil
opts.on('-n', '--name ARG', 'Container name') do |name|
options[:name] = name
end
options[:script] = false
opts.on('-s', '--script', 'Create a script for this container') do
options[:script] = true
end
end
optparse.parse!
return options
end
# Perform error checks on the parameters of the script and options
#
def check_for_errors(options)
# Check that user is root.
if Process.uid != 0
puts("xlxc-create.rb must be run as root.")
exit
end
# Check that user is running XIA kernel.
# TODO Find a better way to check this.
if !`uname -r`.include?("xia")
puts("Must be running Linux XIA to create XIA containers.")
exit
end
# Check that there are no conflicts with the container name.
name = options[:name]
if name == nil
puts("Specify name for container using -n or --name.")
exit
end
container = File.join(XLXC::LXC, name)
if options[:reset] && !File.exist?(container)
puts("Container #{container} does not exist.")
exit
end
if !options[:reset] && File.exist?(container)
puts("Container #{container} already exists in #{XLXC::LXC}.")
exit
end
# Check that the bridge exists.
bridge = options[:bridge]
if bridge == nil
puts("Specify name for bridge using -b or --bridge.")
exit
end
if !File.exist?(File.join(XLXC_BRIDGE::BRIDGES, bridge))
puts("Bridge #{bridge} does not exist.")
exit
end
end
# Create container filesystem by bind mounting from host.
#
def create_fs(rootfs)
FileUtils.mkdir_p(rootfs)
# Bind mount (read-only) directories from host.
for dir in XLXC::BIND_MOUNTED_DIRECTORIES
XLXC.bind_mount(dir, File.join(rootfs, dir), true, true)
end
# Copy local etc to containers.
`cp -R #{LOCAL_ETC} #{rootfs}`
# Create necessary directories that are initially empty.
for dir in INITIALLY_EMPTY_DIRECTORIES
FileUtils.mkdir_p(File.join(rootfs, dir))
end
# Create dev directory and necessary files (pts, random, urandom).
`mknod #{File.join(rootfs, DEV_RANDOM)} c 1 8`
`mknod #{File.join(rootfs, DEV_URANDOM)} c 1 9`
# Remove root password.
`chroot #{rootfs} passwd -d root`
end
# Creates and installs a script into each container.
#
def create_script(container_name)
script = File.join(XLXC::LXC, container_name, "rootfs", "run.sh")
open(script, 'w') { |f|
f.puts("# Add HID for this container.")
if !File.file?(File.join(XIA_HIDS, container_name))
f.puts("sudo xip hid new #{container_name}")
end
f.puts("sudo xip hid add #{container_name}")
f.puts("# Keep container running.")
f.puts("cat")
}
`chmod +x #{script}`
end
# Copy a default LXC configuration file and add configuration
# information for it that is specific to this container, such
# as a network interface, hardware address, and bind mounts.
#
def config_container(name, bridge)
container = File.join(XLXC::LXC, name)
rootfs = File.join(container, "rootfs")
config = File.join(container, "config")
fstab = File.join(container, "fstab")
bridge_file = File.join(container, "bridge")
# Set up container config file.
open(config, 'w') { |f|
f.puts(XLXC::LXC_CONFIG_TEMPLATE)
f.puts("lxc.network.link=#{bridge}\n" \
"lxc.network.veth.pair=#{name}veth\n" \
"lxc.rootfs=#{rootfs}\n" \
"lxc.utsname=#{name}\n" \
"lxc.mount=#{fstab}")
}
# Set up container fstab file.
open(fstab, 'w') { |f|
f.puts(XLXC::FSTAB_TEMPLATE)
}
# Set up container hosts files.
open(File.join(rootfs, XLXC::HOSTS_FILE), 'w') { |f|
f.puts(sprintf(XLXC::HOSTS_TEMPLATE, name))
}
open(File.join(rootfs, XLXC::HOSTNAME_FILE), 'w') { |f|
f.puts(name)
}
open(bridge_file, 'w') { |f|
f.puts(bridge)
}
end
# Setup a container with the given options.
#
def setup_container(options)
name = options[:name]
bridge = options[:bridge]
`cp -R #{XIA} #{LOCAL_ETC}`
# Create filesystem for container.
create_fs(File.join(XLXC::LXC, name, "rootfs"))
# Configure the container (network, fstab, hostname).
config_container(name, bridge)
if options[:script]
create_script(name)
end
`rm -rf #{File.join(LOCAL_ETC, "xia")}`
end
if __FILE__ == $PROGRAM_NAME
options = parse_opts()
check_for_errors(options)
setup_container(options)
end