vagrant-lxc 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +62 -40
- data/README.md +42 -28
- data/lib/vagrant-lxc/action.rb +9 -23
- data/lib/vagrant-lxc/action/create.rb +14 -5
- data/lib/vagrant-lxc/action/gc_private_network_bridges.rb +46 -0
- data/lib/vagrant-lxc/action/private_networks.rb +43 -0
- data/lib/vagrant-lxc/action/warn_networks.rb +3 -3
- data/lib/vagrant-lxc/command/sudoers.rb +4 -3
- data/lib/vagrant-lxc/driver.rb +63 -1
- data/lib/vagrant-lxc/driver/cli.rb +1 -1
- data/lib/vagrant-lxc/errors.rb +4 -0
- data/lib/vagrant-lxc/plugin.rb +7 -14
- data/lib/vagrant-lxc/provider.rb +9 -1
- data/lib/vagrant-lxc/version.rb +1 -1
- data/locales/en.yml +10 -3
- data/scripts/pipework +298 -0
- data/spec/unit/driver/cli_spec.rb +3 -1
- data/spec/unit/driver_spec.rb +85 -23
- data/templates/sudoers.rb.erb +27 -6
- metadata +5 -11
- data/lib/vagrant-backports/README.md +0 -12
- data/lib/vagrant-backports/action/handle_box.rb +0 -1
- data/lib/vagrant-backports/action/is_state.rb +0 -34
- data/lib/vagrant-backports/action/message.rb +0 -20
- data/lib/vagrant-backports/action/wait_for_communicator.rb +0 -42
- data/lib/vagrant-backports/ui.rb +0 -12
- data/lib/vagrant-backports/utils.rb +0 -27
- data/lib/vagrant-lxc/action/message.rb +0 -0
- data/lib/vagrant-lxc/backports/action/share_folders.rb +0 -67
@@ -16,11 +16,7 @@ module Vagrant
|
|
16
16
|
when String
|
17
17
|
# Nothing to do here, move along...
|
18
18
|
else
|
19
|
-
container_name =
|
20
|
-
container_name.gsub!(/[^-a-z0-9_]/i, "")
|
21
|
-
# milliseconds + random number suffix to allow for simultaneous
|
22
|
-
# `vagrant up` of the same box in different dirs
|
23
|
-
container_name << "_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}"
|
19
|
+
container_name = generate_container_name(env)
|
24
20
|
end
|
25
21
|
|
26
22
|
env[:machine].provider.driver.create(
|
@@ -36,6 +32,19 @@ module Vagrant
|
|
36
32
|
|
37
33
|
@app.call env
|
38
34
|
end
|
35
|
+
|
36
|
+
def generate_container_name(env)
|
37
|
+
container_name = "#{env[:root_path].basename}_#{env[:machine].name}"
|
38
|
+
container_name.gsub!(/[^-a-z0-9_]/i, "")
|
39
|
+
|
40
|
+
# milliseconds + random number suffix to allow for simultaneous
|
41
|
+
# `vagrant up` of the same box in different dirs
|
42
|
+
container_name << "_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}"
|
43
|
+
|
44
|
+
# Trim container name to 64 chars, keeping "randomness"
|
45
|
+
trim_point = container_name.size > 64 ? -64 : -(container_name.size)
|
46
|
+
container_name[trim_point..-1]
|
47
|
+
end
|
39
48
|
end
|
40
49
|
end
|
41
50
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module LXC
|
3
|
+
module Action
|
4
|
+
class GcPrivateNetworkBridges
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
was_running = env[:machine].provider.state.id == :running
|
11
|
+
|
12
|
+
# Continue execution, we need the container to be stopped
|
13
|
+
@app.call(env)
|
14
|
+
|
15
|
+
was_running = was_running && env[:machine].provider.state.id != :running
|
16
|
+
|
17
|
+
if was_running && private_network_configured?(env[:machine].config)
|
18
|
+
private_network_configured?(env[:machine].config)
|
19
|
+
remove_bridges_that_are_not_in_use(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def private_network_configured?(config)
|
24
|
+
config.vm.networks.find do |type, _|
|
25
|
+
type.to_sym == :private_network
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_bridges_that_are_not_in_use(env)
|
30
|
+
env[:machine].config.vm.networks.find do |type, config|
|
31
|
+
next if type.to_sym != :private_network
|
32
|
+
|
33
|
+
bridge = config.fetch(:lxc__bridge_name)
|
34
|
+
driver = env[:machine].provider.driver
|
35
|
+
|
36
|
+
if ! driver.bridge_is_in_use?(bridge)
|
37
|
+
env[:ui].info I18n.t("vagrant_lxc.messages.remove_bridge", name: bridge)
|
38
|
+
# TODO: Output that bridge is being removed
|
39
|
+
driver.remove_bridge(bridge)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module LXC
|
3
|
+
module Action
|
4
|
+
class PrivateNetworks
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
@app.call(env)
|
11
|
+
|
12
|
+
if private_network_configured?(env[:machine].config)
|
13
|
+
env[:ui].output(I18n.t("vagrant_lxc.messages.setup_private_network"))
|
14
|
+
configure_private_networks(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def private_network_configured?(config)
|
19
|
+
config.vm.networks.find do |type, _|
|
20
|
+
type.to_sym == :private_network
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def configure_private_networks(env)
|
25
|
+
env[:machine].config.vm.networks.find do |type, config|
|
26
|
+
next if type.to_sym != :private_network
|
27
|
+
|
28
|
+
container_name = env[:machine].provider.driver.container_name
|
29
|
+
ip = config[:ip]
|
30
|
+
bridge_ip = config.fetch(:lxc__bridge_ip) { build_bridge_ip(ip) }
|
31
|
+
bridge = config.fetch(:lxc__bridge_name)
|
32
|
+
|
33
|
+
env[:machine].provider.driver.configure_private_network(bridge, bridge_ip, container_name, ip)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_bridge_ip(ip)
|
38
|
+
ip.sub(/^(\d+\.\d+\.\d+)\.\d+/, '\1.254')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -7,16 +7,16 @@ module Vagrant
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(env)
|
10
|
-
if
|
10
|
+
if public_network_configured?(env[:machine].config)
|
11
11
|
env[:ui].warn(I18n.t("vagrant_lxc.messages.warn_networks"))
|
12
12
|
end
|
13
13
|
|
14
14
|
@app.call(env)
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
17
|
+
def public_network_configured?(config)
|
18
18
|
config.vm.networks.find do |type, _|
|
19
|
-
|
19
|
+
type.to_sym == :public_network
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -46,8 +46,9 @@ module Vagrant
|
|
46
46
|
wrapper = Tempfile.new('lxc-wrapper').tap do |file|
|
47
47
|
template = Vagrant::Util::TemplateRenderer.new(
|
48
48
|
'sudoers.rb',
|
49
|
-
:template_root
|
50
|
-
:cmd_paths
|
49
|
+
:template_root => Vagrant::LXC.source_root.join('templates').to_s,
|
50
|
+
:cmd_paths => build_cmd_paths_hash,
|
51
|
+
:pipework_regex => "#{ENV['HOME']}/\.vagrant\.d/gems/gems/vagrant-lxc.+/scripts/pipework"
|
51
52
|
)
|
52
53
|
file.puts template.render
|
53
54
|
end
|
@@ -78,7 +79,7 @@ module Vagrant
|
|
78
79
|
|
79
80
|
def build_cmd_paths_hash
|
80
81
|
{}.tap do |hash|
|
81
|
-
%w( which cat mkdir cp chown chmod rm tar chown ).each do |cmd|
|
82
|
+
%w( which cat mkdir cp chown chmod rm tar chown ip ifconfig brctl ).each do |cmd|
|
82
83
|
hash[cmd] = `which #{cmd}`.strip
|
83
84
|
end
|
84
85
|
hash['lxc_bin'] = Pathname(`which lxc-create`.strip).parent.to_s
|
data/lib/vagrant-lxc/driver.rb
CHANGED
@@ -6,6 +6,8 @@ require "vagrant-lxc/driver/cli"
|
|
6
6
|
|
7
7
|
require "etc"
|
8
8
|
|
9
|
+
require "tempfile"
|
10
|
+
|
9
11
|
module Vagrant
|
10
12
|
module LXC
|
11
13
|
class Driver
|
@@ -45,7 +47,21 @@ module Vagrant
|
|
45
47
|
end
|
46
48
|
|
47
49
|
def rootfs_path
|
48
|
-
|
50
|
+
config_entry = config_string.match(/^lxc\.rootfs\s+=\s+(.+)$/)[1]
|
51
|
+
case config_entry
|
52
|
+
when /^overlayfs:/
|
53
|
+
# Split on colon (:), ignoring any colon escaped by an escape character ( \ )
|
54
|
+
# Pays attention to when the escape character is itself escaped.
|
55
|
+
fs_type, master_path, overlay_path = config_entry.split(/(?<!\\)(?:\\\\)*:/)
|
56
|
+
if overlay_path
|
57
|
+
Pathname.new(overlay_path)
|
58
|
+
else
|
59
|
+
# Malformed: fall back to prior behaviour
|
60
|
+
Pathname.new(config_entry)
|
61
|
+
end
|
62
|
+
else
|
63
|
+
Pathname.new(config_entry)
|
64
|
+
end
|
49
65
|
end
|
50
66
|
|
51
67
|
def mac_address
|
@@ -124,6 +140,52 @@ module Vagrant
|
|
124
140
|
@cli.attach(*command)
|
125
141
|
end
|
126
142
|
|
143
|
+
def configure_private_network(bridge_name, bridge_ip, container_name, ip)
|
144
|
+
@logger.info "Configuring network interface for #{container_name} using #{ip} and bridge #{bridge_name}"
|
145
|
+
cmd = [
|
146
|
+
Vagrant::LXC.source_root.join('scripts/pipework').to_s,
|
147
|
+
bridge_name,
|
148
|
+
container_name,
|
149
|
+
"#{ip}/24"
|
150
|
+
]
|
151
|
+
@sudo_wrapper.run(*cmd)
|
152
|
+
|
153
|
+
if ! bridge_has_an_ip?(bridge_name)
|
154
|
+
@logger.info "Adding #{bridge_ip} to the bridge #{bridge_name}"
|
155
|
+
cmd = [
|
156
|
+
'ip',
|
157
|
+
'addr',
|
158
|
+
'add',
|
159
|
+
"#{bridge_ip}/24",
|
160
|
+
'dev',
|
161
|
+
bridge_name
|
162
|
+
]
|
163
|
+
@sudo_wrapper.run(*cmd)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def bridge_has_an_ip?(bridge_name)
|
168
|
+
@logger.info "Checking whether the bridge #{bridge_name} has an IP"
|
169
|
+
`ip -4 addr show scope global #{bridge_name}` =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
|
170
|
+
end
|
171
|
+
|
172
|
+
def bridge_is_in_use?(bridge_name)
|
173
|
+
# REFACTOR: This method is **VERY** hacky
|
174
|
+
@logger.info "Checking if bridge #{bridge_name} is in use"
|
175
|
+
brctl_output = `brctl show #{bridge_name} 2>/dev/null | tail -n +2 | grep -q veth`
|
176
|
+
$?.to_i == 0
|
177
|
+
end
|
178
|
+
|
179
|
+
def remove_bridge(bridge_name)
|
180
|
+
@logger.info "Checking whether bridge #{bridge_name} exists"
|
181
|
+
brctl_output = `ifconfig -a | grep -q #{bridge_name}`
|
182
|
+
return if $?.to_i != 0
|
183
|
+
|
184
|
+
@logger.info "Removing bridge #{bridge_name}"
|
185
|
+
@sudo_wrapper.run('ifconfig', bridge_name, 'down')
|
186
|
+
@sudo_wrapper.run('brctl', 'delbr', bridge_name)
|
187
|
+
end
|
188
|
+
|
127
189
|
def version
|
128
190
|
@version ||= @cli.version
|
129
191
|
end
|
data/lib/vagrant-lxc/errors.rb
CHANGED
@@ -18,6 +18,10 @@ module Vagrant
|
|
18
18
|
class NamespacesNotSupported < Vagrant::Errors::VagrantError
|
19
19
|
end
|
20
20
|
|
21
|
+
class LxcLinuxRequired < Vagrant::Errors::VagrantError
|
22
|
+
error_key(:lxc_linux_required)
|
23
|
+
end
|
24
|
+
|
21
25
|
class LxcNotInstalled < Vagrant::Errors::VagrantError
|
22
26
|
error_key(:lxc_not_installed)
|
23
27
|
end
|
data/lib/vagrant-lxc/plugin.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'vagrant'
|
2
|
-
require 'vagrant-backports/utils'
|
3
2
|
|
4
3
|
module Vagrant
|
5
4
|
module LXC
|
@@ -10,9 +9,7 @@ module Vagrant
|
|
10
9
|
LXC-based virtual machines.
|
11
10
|
EOF
|
12
11
|
|
13
|
-
|
14
|
-
extra << {parallel: true} if Vagrant::Backports.vagrant_1_2_or_later?
|
15
|
-
provider(:lxc, *extra) do
|
12
|
+
provider(:lxc, parallel: true, priority: 7) do
|
16
13
|
require File.expand_path("../provider", __FILE__)
|
17
14
|
|
18
15
|
I18n.load_path << File.expand_path(File.dirname(__FILE__) + '/../../locales/en.yml')
|
@@ -31,18 +28,14 @@ module Vagrant
|
|
31
28
|
Config
|
32
29
|
end
|
33
30
|
|
34
|
-
|
35
|
-
synced_folder
|
36
|
-
|
37
|
-
SyncedFolder
|
38
|
-
end
|
31
|
+
synced_folder(:lxc) do
|
32
|
+
require File.expand_path("../synced_folder", __FILE__)
|
33
|
+
SyncedFolder
|
39
34
|
end
|
40
35
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
Provider::Cap::PublicAddress
|
45
|
-
end
|
36
|
+
provider_capability("lxc", "public_address") do
|
37
|
+
require_relative "provider/cap/public_address"
|
38
|
+
Provider::Cap::PublicAddress
|
46
39
|
end
|
47
40
|
end
|
48
41
|
end
|
data/lib/vagrant-lxc/provider.rb
CHANGED
@@ -9,6 +9,14 @@ module Vagrant
|
|
9
9
|
class Provider < Vagrant.plugin("2", :provider)
|
10
10
|
attr_reader :driver
|
11
11
|
|
12
|
+
def self.usable?(raise_error=false)
|
13
|
+
if !Vagrant::Util::Platform.linux?
|
14
|
+
raise Errors::LxcLinuxRequired
|
15
|
+
end
|
16
|
+
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
12
20
|
def initialize(machine)
|
13
21
|
@logger = Log4r::Logger.new("vagrant::provider::lxc")
|
14
22
|
@machine = machine
|
@@ -28,7 +36,7 @@ module Vagrant
|
|
28
36
|
|
29
37
|
def ensure_lxc_installed!
|
30
38
|
begin
|
31
|
-
sudo_wrapper.run("which", "lxc-create")
|
39
|
+
sudo_wrapper.run("/usr/bin/which", "lxc-create")
|
32
40
|
rescue Vagrant::LXC::Errors::ExecuteError
|
33
41
|
raise Errors::LxcNotInstalled
|
34
42
|
end
|
data/lib/vagrant-lxc/version.rb
CHANGED
data/locales/en.yml
CHANGED
@@ -13,15 +13,18 @@ en:
|
|
13
13
|
force_shutdown: |-
|
14
14
|
Forcing shutdown of container...
|
15
15
|
warn_networks: |-
|
16
|
-
Warning! The LXC provider doesn't support
|
17
|
-
|
18
|
-
They will be silently ignored.
|
16
|
+
Warning! The LXC provider doesn't support public networks, the settings
|
17
|
+
will be silently ignored.
|
19
18
|
warn_group: |-
|
20
19
|
Warning! The LXC provider doesn't support the :group parameter for synced
|
21
20
|
folders. It will be silently ignored.
|
22
21
|
warn_owner: |-
|
23
22
|
Warning! The LXC provider doesn't support the :owner parameter for synced
|
24
23
|
folders. It will be silently ignored.
|
24
|
+
setup_private_network: |-
|
25
|
+
Setting up private networks...
|
26
|
+
remove_bridge: |-
|
27
|
+
Removing bridge '%{name}'...
|
25
28
|
|
26
29
|
vagrant:
|
27
30
|
commands:
|
@@ -56,6 +59,10 @@ en:
|
|
56
59
|
|
57
60
|
Looked up under: %{paths}
|
58
61
|
|
62
|
+
lxc_linux_required: |-
|
63
|
+
The LXC provider only works on Linux. Please try to use
|
64
|
+
another provider.
|
65
|
+
|
59
66
|
lxc_not_installed: |-
|
60
67
|
The `lxc` package does not seem to be installed or is not accessible on the PATH.
|
61
68
|
|
data/scripts/pipework
ADDED
@@ -0,0 +1,298 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Borrowed from https://github.com/jpetazzo/pipework
|
4
|
+
|
5
|
+
set -e
|
6
|
+
|
7
|
+
case "$1" in
|
8
|
+
--wait)
|
9
|
+
WAIT=1
|
10
|
+
;;
|
11
|
+
esac
|
12
|
+
|
13
|
+
IFNAME=$1
|
14
|
+
|
15
|
+
# default value set further down if not set here
|
16
|
+
CONTAINER_IFNAME=
|
17
|
+
if [ "$2" = "-i" ]; then
|
18
|
+
CONTAINER_IFNAME=$3
|
19
|
+
shift 2
|
20
|
+
fi
|
21
|
+
|
22
|
+
GUESTNAME=$2
|
23
|
+
IPADDR=$3
|
24
|
+
MACADDR=$4
|
25
|
+
|
26
|
+
if echo $MACADDR | grep -q @
|
27
|
+
then
|
28
|
+
VLAN=$(echo $MACADDR | cut -d@ -f2)
|
29
|
+
MACADDR=$(echo $MACADDR | cut -d@ -f1)
|
30
|
+
else
|
31
|
+
VLAN=
|
32
|
+
fi
|
33
|
+
|
34
|
+
[ "$IPADDR" ] || [ "$WAIT" ] || {
|
35
|
+
echo "Syntax:"
|
36
|
+
echo "pipework <hostinterface> [-i containerinterface] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]"
|
37
|
+
echo "pipework <hostinterface> [-i containerinterface] <guest> dhcp [macaddr][@vlan]"
|
38
|
+
echo "pipework --wait [-i containerinterface]"
|
39
|
+
exit 1
|
40
|
+
}
|
41
|
+
|
42
|
+
# First step: determine type of first argument (bridge, physical interface...), skip if --wait set
|
43
|
+
if [ -z "$WAIT" ]; then
|
44
|
+
if [ -d /sys/class/net/$IFNAME ]
|
45
|
+
then
|
46
|
+
if [ -d /sys/class/net/$IFNAME/bridge ]
|
47
|
+
then
|
48
|
+
IFTYPE=bridge
|
49
|
+
BRTYPE=linux
|
50
|
+
elif $(which ovs-vsctl >/dev/null 2>&1) && $(ovs-vsctl list-br|grep -q ^$IFNAME$)
|
51
|
+
then
|
52
|
+
IFTYPE=bridge
|
53
|
+
BRTYPE=openvswitch
|
54
|
+
elif [ $(cat /sys/class/net/$IFNAME/type) -eq 32 ]; # Infiniband IPoIB interface type 32
|
55
|
+
then
|
56
|
+
IFTYPE=ipoib
|
57
|
+
# The IPoIB kernel module is fussy, set device name to ib0 if not overridden
|
58
|
+
CONTAINER_IFNAME=${CONTAINER_IFNAME:-ib0}
|
59
|
+
else IFTYPE=phys
|
60
|
+
fi
|
61
|
+
else
|
62
|
+
# case "$IFNAME" in
|
63
|
+
# br*)
|
64
|
+
IFTYPE=bridge
|
65
|
+
BRTYPE=linux
|
66
|
+
# ;;
|
67
|
+
# ovs*)
|
68
|
+
# if ! $(which ovs-vsctl >/dev/null)
|
69
|
+
# then
|
70
|
+
# echo "Need OVS installed on the system to create an ovs bridge"
|
71
|
+
# exit 1
|
72
|
+
# fi
|
73
|
+
# IFTYPE=bridge
|
74
|
+
# BRTYPE=openvswitch
|
75
|
+
# ;;
|
76
|
+
# *)
|
77
|
+
# echo "I do not know how to setup interface $IFNAME."
|
78
|
+
# exit 1
|
79
|
+
# ;;
|
80
|
+
# esac
|
81
|
+
fi
|
82
|
+
fi
|
83
|
+
|
84
|
+
# Set the default container interface name to eth1 if not already set
|
85
|
+
CONTAINER_IFNAME=${CONTAINER_IFNAME:-eth1}
|
86
|
+
|
87
|
+
[ "$WAIT" ] && {
|
88
|
+
while ! grep -q ^1$ /sys/class/net/$CONTAINER_IFNAME/carrier 2>/dev/null
|
89
|
+
do sleep 1
|
90
|
+
done
|
91
|
+
exit 0
|
92
|
+
}
|
93
|
+
|
94
|
+
[ $IFTYPE = bridge ] && [ $BRTYPE = linux ] && [ "$VLAN" ] && {
|
95
|
+
echo "VLAN configuration currently unsupported for Linux bridge."
|
96
|
+
exit 1
|
97
|
+
}
|
98
|
+
|
99
|
+
[ $IFTYPE = ipoib ] && [ $MACADDR ] && {
|
100
|
+
echo "MACADDR configuration unsupported for IPoIB interfaces."
|
101
|
+
exit 1
|
102
|
+
}
|
103
|
+
|
104
|
+
# Second step: find the guest (for now, we only support LXC containers)
|
105
|
+
while read dev mnt fstype options dump fsck
|
106
|
+
do
|
107
|
+
[ "$fstype" != "cgroup" ] && continue
|
108
|
+
echo $options | grep -qw devices || continue
|
109
|
+
CGROUPMNT=$mnt
|
110
|
+
done < /proc/mounts
|
111
|
+
|
112
|
+
[ "$CGROUPMNT" ] || {
|
113
|
+
echo "Could not locate cgroup mount point."
|
114
|
+
exit 1
|
115
|
+
}
|
116
|
+
|
117
|
+
# Try to find a cgroup matching exactly the provided name.
|
118
|
+
N=$(find "$CGROUPMNT" -name "$GUESTNAME" | wc -l)
|
119
|
+
case "$N" in
|
120
|
+
0)
|
121
|
+
# If we didn't find anything, try to lookup the container with Docker.
|
122
|
+
if which docker >/dev/null
|
123
|
+
then
|
124
|
+
RETRIES=3
|
125
|
+
while [ $RETRIES -gt 0 ]; do
|
126
|
+
DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' $GUESTNAME)
|
127
|
+
[ $DOCKERPID != 0 ] && break
|
128
|
+
sleep 1
|
129
|
+
RETRIES=$((RETRIES - 1))
|
130
|
+
done
|
131
|
+
|
132
|
+
[ "$DOCKERPID" = 0 ] && {
|
133
|
+
echo "Docker inspect returned invalid PID 0"
|
134
|
+
exit 1
|
135
|
+
}
|
136
|
+
|
137
|
+
[ "$DOCKERPID" = "<no value>" ] && {
|
138
|
+
echo "Container $GUESTNAME not found, and unknown to Docker."
|
139
|
+
exit 1
|
140
|
+
}
|
141
|
+
else
|
142
|
+
echo "Container $GUESTNAME not found, and Docker not installed."
|
143
|
+
exit 1
|
144
|
+
fi
|
145
|
+
;;
|
146
|
+
1)
|
147
|
+
true
|
148
|
+
;;
|
149
|
+
*)
|
150
|
+
echo "Found more than one container matching $GUESTNAME."
|
151
|
+
exit 1
|
152
|
+
;;
|
153
|
+
esac
|
154
|
+
|
155
|
+
if [ "$IPADDR" = "dhcp" ]
|
156
|
+
then
|
157
|
+
# Check for first available dhcp client
|
158
|
+
DHCP_CLIENT_LIST="udhcpc dhcpcd dhclient"
|
159
|
+
for CLIENT in $DHCP_CLIENT_LIST; do
|
160
|
+
which $CLIENT >/dev/null && {
|
161
|
+
DHCP_CLIENT=$CLIENT
|
162
|
+
break
|
163
|
+
}
|
164
|
+
done
|
165
|
+
[ -z $DHCP_CLIENT ] && {
|
166
|
+
echo "You asked for DHCP; but no DHCP client could be found."
|
167
|
+
exit 1
|
168
|
+
}
|
169
|
+
else
|
170
|
+
# Check if a subnet mask was provided.
|
171
|
+
echo $IPADDR | grep -q / || {
|
172
|
+
echo "The IP address should include a netmask."
|
173
|
+
echo "Maybe you meant $IPADDR/24 ?"
|
174
|
+
exit 1
|
175
|
+
}
|
176
|
+
# Check if a gateway address was provided.
|
177
|
+
if echo $IPADDR | grep -q @
|
178
|
+
then
|
179
|
+
GATEWAY=$(echo $IPADDR | cut -d@ -f2)
|
180
|
+
IPADDR=$(echo $IPADDR | cut -d@ -f1)
|
181
|
+
else
|
182
|
+
GATEWAY=
|
183
|
+
fi
|
184
|
+
fi
|
185
|
+
|
186
|
+
if [ $DOCKERPID ]; then
|
187
|
+
NSPID=$DOCKERPID
|
188
|
+
else
|
189
|
+
NSPID=$(head -n 1 $(find "$CGROUPMNT" -name "$GUESTNAME" | head -n 1)/tasks)
|
190
|
+
[ "$NSPID" ] || {
|
191
|
+
echo "Could not find a process inside container $GUESTNAME."
|
192
|
+
exit 1
|
193
|
+
}
|
194
|
+
fi
|
195
|
+
|
196
|
+
# Check if an incompatible VLAN device already exists
|
197
|
+
[ $IFTYPE = phys ] && [ "$VLAN" ] && [ -d /sys/class/net/$IFNAME.VLAN ] && {
|
198
|
+
[ -z "$(ip -d link show $IFNAME.$VLAN | grep "vlan.*id $VLAN")" ] && {
|
199
|
+
echo "$IFNAME.VLAN already exists but is not a VLAN device for tag $VLAN"
|
200
|
+
exit 1
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
[ ! -d /var/run/netns ] && mkdir -p /var/run/netns
|
205
|
+
[ -f /var/run/netns/$NSPID ] && rm -f /var/run/netns/$NSPID
|
206
|
+
ln -s /proc/$NSPID/ns/net /var/run/netns/$NSPID
|
207
|
+
|
208
|
+
# Check if we need to create a bridge.
|
209
|
+
[ $IFTYPE = bridge ] && [ ! -d /sys/class/net/$IFNAME ] && {
|
210
|
+
[ $BRTYPE = linux ] && {
|
211
|
+
(ip link add dev $IFNAME type bridge > /dev/null 2>&1) || (brctl addbr $IFNAME)
|
212
|
+
ip link set $IFNAME up
|
213
|
+
}
|
214
|
+
[ $BRTYPE = openvswitch ] && {
|
215
|
+
ovs-vsctl add-br $IFNAME
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
MTU=$(ip link show $IFNAME | awk '{print $5}')
|
220
|
+
# If it's a bridge, we need to create a veth pair
|
221
|
+
[ $IFTYPE = bridge ] && {
|
222
|
+
LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${NSPID}"
|
223
|
+
GUEST_IFNAME="v${CONTAINER_IFNAME}pg${NSPID}"
|
224
|
+
ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU
|
225
|
+
case "$BRTYPE" in
|
226
|
+
linux)
|
227
|
+
(ip link set $LOCAL_IFNAME master $IFNAME > /dev/null 2>&1) || (brctl addif $IFNAME $LOCAL_IFNAME)
|
228
|
+
;;
|
229
|
+
openvswitch)
|
230
|
+
ovs-vsctl add-port $IFNAME $LOCAL_IFNAME ${VLAN:+"tag=$VLAN"}
|
231
|
+
;;
|
232
|
+
esac
|
233
|
+
ip link set $LOCAL_IFNAME up
|
234
|
+
}
|
235
|
+
|
236
|
+
# Note: if no container interface name was specified, pipework will default to ib0
|
237
|
+
# Note: no macvlan subinterface or ethernet bridge can be created against an
|
238
|
+
# ipoib interface. Infiniband is not ethernet. ipoib is an IP layer for it.
|
239
|
+
# To provide additional ipoib interfaces to containers use SR-IOV and pipework
|
240
|
+
# to assign them.
|
241
|
+
[ $IFTYPE = ipoib ] && {
|
242
|
+
GUEST_IFNAME=$CONTAINER_IFNAME
|
243
|
+
}
|
244
|
+
|
245
|
+
# If it's a physical interface, create a macvlan subinterface
|
246
|
+
[ $IFTYPE = phys ] && {
|
247
|
+
[ "$VLAN" ] && {
|
248
|
+
[ ! -d /sys/class/net/$IFNAME.$VLAN ] && {
|
249
|
+
ip link add link $IFNAME name $IFNAME.$VLAN mtu $MTU type vlan id $VLAN
|
250
|
+
}
|
251
|
+
|
252
|
+
ip link set $IFNAME up
|
253
|
+
IFNAME=$IFNAME.$VLAN
|
254
|
+
}
|
255
|
+
GUEST_IFNAME=ph$NSPID$CONTAINER_IFNAME
|
256
|
+
ip link add link $IFNAME dev $GUEST_IFNAME mtu $MTU type macvlan mode bridge
|
257
|
+
ip link set $IFNAME up
|
258
|
+
}
|
259
|
+
|
260
|
+
ip link set $GUEST_IFNAME netns $NSPID
|
261
|
+
ip netns exec $NSPID ip link set $GUEST_IFNAME name $CONTAINER_IFNAME
|
262
|
+
[ "$MACADDR" ] && ip netns exec $NSPID ip link set dev $CONTAINER_IFNAME address $MACADDR
|
263
|
+
if [ "$IPADDR" = "dhcp" ]
|
264
|
+
then
|
265
|
+
[ $DHCP_CLIENT = "udhcpc" ] && ip netns exec $NSPID $DHCP_CLIENT -qi $CONTAINER_IFNAME -x hostname:$GUESTNAME
|
266
|
+
if [ $DHCP_CLIENT = "dhclient" ]
|
267
|
+
then
|
268
|
+
# kill dhclient after get ip address to prevent device be used after container close
|
269
|
+
ip netns exec $NSPID $DHCP_CLIENT -pf "/var/run/dhclient.$NSPID.pid" $CONTAINER_IFNAME
|
270
|
+
kill "$(cat "/var/run/dhclient.$NSPID.pid")"
|
271
|
+
rm "/var/run/dhclient.$NSPID.pid"
|
272
|
+
fi
|
273
|
+
[ $DHCP_CLIENT = "dhcpcd" ] && ip netns exec $NSPID $DHCP_CLIENT -q $CONTAINER_IFNAME -h $GUESTNAME
|
274
|
+
else
|
275
|
+
ip netns exec $NSPID ip addr add $IPADDR dev $CONTAINER_IFNAME
|
276
|
+
[ "$GATEWAY" ] && {
|
277
|
+
ip netns exec $NSPID ip route delete default >/dev/null 2>&1 && true
|
278
|
+
}
|
279
|
+
ip netns exec $NSPID ip link set $CONTAINER_IFNAME up
|
280
|
+
[ "$GATEWAY" ] && {
|
281
|
+
ip netns exec $NSPID ip route get $GATEWAY >/dev/null 2>&1 || \
|
282
|
+
ip netns exec $NSPID ip route add $GATEWAY/32 dev $CONTAINER_IFNAME
|
283
|
+
ip netns exec $NSPID ip route replace default via $GATEWAY
|
284
|
+
}
|
285
|
+
fi
|
286
|
+
|
287
|
+
# Give our ARP neighbors a nudge about the new interface
|
288
|
+
if which arping > /dev/null 2>&1
|
289
|
+
then
|
290
|
+
IPADDR=$(echo $IPADDR | cut -d/ -f1)
|
291
|
+
ip netns exec $NSPID arping -c 1 -A -I $CONTAINER_IFNAME $IPADDR > /dev/null 2>&1 || true
|
292
|
+
else
|
293
|
+
echo "Warning: arping not found; interface may not be immediately reachable"
|
294
|
+
fi
|
295
|
+
|
296
|
+
# Remove NSPID to avoid `ip netns` catch it.
|
297
|
+
[ -f /var/run/netns/$NSPID ] && rm -f /var/run/netns/$NSPID
|
298
|
+
exit 0
|