vagrant-clusterfuck 0.0.1 → 0.0.2
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/.gitignore +1 -0
- data/README.md +54 -0
- data/Vagrantfile +2 -1
- data/cluster.rb +43 -13
- data/clusterfuck.gemspec +2 -2
- data/lib/clusterfuck/bgp_peer.rb +5 -11
- data/lib/clusterfuck/cluster.rb +30 -39
- data/lib/clusterfuck/core_ext/ipaddr.rb +4 -0
- data/lib/clusterfuck/machine.rb +8 -8
- data/lib/clusterfuck/quagga_bgp_router.rb +2 -2
- data/lib/clusterfuck/subnet_factory.rb +8 -7
- data/lib/clusterfuck/version.rb +1 -1
- data/lib/clusterfuck.rb +0 -2
- data/templates/quagga.bgpd.conf.erb +6 -6
- data/templates/{quagga.daemons.erb → quagga.daemons} +0 -0
- data/templates/{quagga.zebra.conf.erb → quagga.zebra.conf} +0 -0
- data/templates/routes.sh.erb +2 -2
- data/test/bgp_network_test.rb +51 -0
- data/test/bgp_peer_test.rb +0 -17
- data/test/cluster_test.rb +130 -0
- data/test/machine_test.rb +1 -0
- data/test/subnet_test.rb +8 -16
- metadata +10 -12
- data/lib/clusterfuck/leaf_spine_dsl.rb +0 -45
- data/lib/clusterfuck/test_helper.rb +0 -36
- data/test/helper_test.rb +0 -19
- data/test/network_test.rb +0 -201
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5e7cd97a8254d5f76623b8405587f7b98985713
|
4
|
+
data.tar.gz: 617d505a9df006f677d39c7019ad66fc701f4733
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3e23a8411a5442b73c75659a912fe8c9c758d8fefa634293913c9e283ad9a9822eadef1da232daad3ae4c2e5182239f7a14a08e4b36861e441ed7e3dd836779
|
7
|
+
data.tar.gz: e1e7df9a9205bd49e923608cf8609cb27f3195d9e1a4564a90ce7e0e711ed44d237c275906530a9bcfe6fe6c22f898109e513fa68730e6aca800a1a7a5309ace
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -3,4 +3,58 @@
|
|
3
3
|
Clusterfuck is a library for setting up a networked cluster of servers to test
|
4
4
|
failure conditions against. It's a library to fuck with clusters.
|
5
5
|
|
6
|
+
Currently it ships with a DSL for EBGP leaf-spine architectures, but has a
|
7
|
+
generic backend that allows configuring any topology.
|
8
|
+
|
9
|
+
It's currently using Virtualbox for probably no good reason other than
|
10
|
+
`network_lab`, which this works derives from, used it. VMWare will be
|
11
|
+
investigated in the future.
|
12
|
+
|
6
13
|
## Usage
|
14
|
+
|
15
|
+
Install the `vagrant-clusterfuck` Vagrant plugin:
|
16
|
+
|
17
|
+
```bash
|
18
|
+
$ vagrant plugin install vagrant-clusterfuck
|
19
|
+
```
|
20
|
+
|
21
|
+
Create a `cluster.rb` file to define your cluster (see below). This must be a separate file,
|
22
|
+
because it needs to be loaded in your test environment which does not implement
|
23
|
+
the Vagrant DSL.
|
24
|
+
|
25
|
+
In your `Vagrantfile`, build your cluster:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
cluster = Clusterfuck::Cluster[:test]
|
29
|
+
cluster[:host1].build(config) do |box|
|
30
|
+
box.vm.provision :shell, inline: "echo 1 > /tmp/test"
|
31
|
+
end
|
32
|
+
|
33
|
+
cluster[:host2].build(config)
|
34
|
+
```
|
35
|
+
|
36
|
+
For a complete example, see the `Vagrantfile` and `cluster.rb` in this
|
37
|
+
repository.
|
38
|
+
|
39
|
+
```bash
|
40
|
+
$ vagrant up --provider virtualbox
|
41
|
+
```
|
42
|
+
|
43
|
+
When making changes to `cluster.rb` and `Vagrantfile` make sure to reload and
|
44
|
+
reprovision the cluster:
|
45
|
+
|
46
|
+
```bash
|
47
|
+
$ vagrant reload --provision
|
48
|
+
```
|
49
|
+
|
50
|
+
## Building Clusters
|
51
|
+
|
52
|
+
Defining a cluster consists of two steps:
|
53
|
+
|
54
|
+
1. **Creating instances.** You must specify which VMs your topology requires.
|
55
|
+
2. **Layer 2 network.** You must specify the relationships between the VMs, that
|
56
|
+
is, the layer 2 network of which nodes belong to the same subnets and can
|
57
|
+
thus directly communicate with each other.
|
58
|
+
|
59
|
+
Please see `./cluster.rb` for an annotated example. In the future we may build
|
60
|
+
DSLs on top of the low-level graph DSL.
|
data/Vagrantfile
CHANGED
@@ -10,7 +10,7 @@ Vagrant.configure('2') do |config|
|
|
10
10
|
box.memory = 256
|
11
11
|
end
|
12
12
|
|
13
|
-
cluster = Clusterfuck::
|
13
|
+
cluster = Clusterfuck::BGPCluster[:test]
|
14
14
|
cluster[:spine].build(config)
|
15
15
|
cluster[:leaf1].build(config)
|
16
16
|
|
@@ -20,4 +20,5 @@ Vagrant.configure('2') do |config|
|
|
20
20
|
|
21
21
|
cluster[:leaf2].build(config)
|
22
22
|
cluster[:host21].build(config)
|
23
|
+
cluster[:host22].build(config)
|
23
24
|
end
|
data/cluster.rb
CHANGED
@@ -1,13 +1,43 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
require_relative "lib/clusterfuck"
|
2
|
+
include Clusterfuck
|
3
|
+
|
4
|
+
# Create a BGPCluster. The BGPCluster is just a cluster that knows how to
|
5
|
+
# discover neighbouring BGP nodes.
|
6
|
+
cluster = BGPCluster.create(:test)
|
7
|
+
|
8
|
+
# The VIP subnet includes all the VIPs that any BGPPeer can take and announce.
|
9
|
+
# All hosts, even if they're on the same subnet as the node that owns the IP,
|
10
|
+
# will go through their gateway to route the IP.
|
11
|
+
vip_subnet = IPAddr.new("192.168.99.0/24")
|
12
|
+
|
13
|
+
# Spine becomes the catchall for 10.0.0.0/16 which is the subnet all the nodes
|
14
|
+
# belong to (each subnet is a /24).
|
15
|
+
priv_subnet = IPAddr.new("10.0.0.0/16")
|
16
|
+
|
17
|
+
# The Spine announces the VIP and private subnet.
|
18
|
+
spine = QuaggaBGPRouter.new(:spine, announce: [vip_subnet, priv_subnet])
|
19
|
+
|
20
|
+
# Define the leaf, announcements come later
|
21
|
+
leaf1_subnet = SubnetFactory.next_subnet
|
22
|
+
leaf1 = QuaggaBGPRouter.new(:leaf1, announce: leaf1_subnet.last_ip)
|
23
|
+
|
24
|
+
leaf2_subnet = SubnetFactory.next_subnet
|
25
|
+
leaf2 = QuaggaBGPRouter.new(:leaf2, announce: leaf2_subnet.last_ip)
|
26
|
+
|
27
|
+
# A normal host. All the Spine's BGP announcements (vip + priv) will go through
|
28
|
+
# the gateway.
|
29
|
+
host11 = Machine.new(:host11, routes: spine.bgp_announce, gateway: leaf1)
|
30
|
+
|
31
|
+
# A BGPPeer is just a machine with BGP information so that its BGP neighbours
|
32
|
+
# will automatically add it as a BGP peer. This is for a node to run e.g.
|
33
|
+
# ExaBGP.
|
34
|
+
host21 = BGPPeer.new(:host21, routes: spine.bgp_announce, gateway: leaf2)
|
35
|
+
host22 = Machine.new(:host22, routes: spine.bgp_announce, gateway: leaf2)
|
36
|
+
|
37
|
+
# The spines and leaves have their own subnets
|
38
|
+
cluster.connect(spine, leaf1)
|
39
|
+
cluster.connect(spine, leaf2)
|
40
|
+
|
41
|
+
# The leaves create a subnet with all their hosts.
|
42
|
+
cluster.connect(leaf1, host11, subnet: leaf1_subnet)
|
43
|
+
cluster.connect(leaf2, host21, host22, subnet: leaf2_subnet)
|
data/clusterfuck.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Clusterfuck::VERSION
|
9
9
|
spec.authors = ["Simon Eskildsen"]
|
10
10
|
spec.email = ["sirup@sirupsen.com"]
|
11
|
-
spec.summary = %q{Clusterfuck
|
12
|
-
spec.description = %q{Clusterfuck
|
11
|
+
spec.summary = %q{Clusterfuck lets you fuck with clusters}
|
12
|
+
spec.description = %q{Clusterfuck lets you fuck with clusters}
|
13
13
|
spec.homepage = ""
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
data/lib/clusterfuck/bgp_peer.rb
CHANGED
@@ -2,26 +2,18 @@ module Clusterfuck
|
|
2
2
|
class BGPPeer < Machine
|
3
3
|
attr_accessor :bgp_asn,
|
4
4
|
:bgp_router_id,
|
5
|
-
:
|
5
|
+
:bgp_announce
|
6
6
|
|
7
7
|
def initialize(name, **args)
|
8
8
|
super
|
9
9
|
|
10
|
-
@
|
10
|
+
@bgp_announce = args[:announce] ? Array(args[:announce]) : []
|
11
11
|
@bgp_asn = args[:asn] || ASNFactory.next
|
12
12
|
@bgp_router_id = args[:router_id] || RouterIDFactory.next
|
13
13
|
end
|
14
14
|
|
15
|
-
def bgp_announced
|
16
|
-
@bgp_announced.map { |a|
|
17
|
-
a.kind_of?(SubnetFactory) ? a.subnet : a
|
18
|
-
}
|
19
|
-
end
|
20
|
-
|
21
15
|
def bgp_neighbors
|
22
|
-
|
23
|
-
neighbor.kind_of?(BGPPeer)
|
24
|
-
}
|
16
|
+
cluster.bgp_neighbors(self)
|
25
17
|
end
|
26
18
|
|
27
19
|
class ASNFactory
|
@@ -32,6 +24,8 @@ module Clusterfuck
|
|
32
24
|
end
|
33
25
|
end
|
34
26
|
|
27
|
+
# A router ID just an IP to identify the router on BGP. It does not have to
|
28
|
+
# be equal to the IP; just has to be an IP. We just pick start somewhere.
|
35
29
|
class RouterIDFactory
|
36
30
|
@id = IPAddr.new("10.0.20.1")
|
37
31
|
|
data/lib/clusterfuck/cluster.rb
CHANGED
@@ -1,74 +1,65 @@
|
|
1
1
|
module Clusterfuck
|
2
2
|
class Cluster
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :name, :network
|
4
4
|
|
5
5
|
def initialize(name)
|
6
6
|
@name = name
|
7
|
-
@nodes = {}
|
8
7
|
@graph = {}
|
9
8
|
end
|
10
9
|
|
11
10
|
def self.create(name)
|
12
|
-
|
13
|
-
|
11
|
+
@@clusters ||= {}
|
12
|
+
@@clusters[name] = self.new(name)
|
14
13
|
end
|
15
14
|
|
16
15
|
def self.[](name)
|
17
|
-
|
16
|
+
@@clusters[name]
|
18
17
|
end
|
19
18
|
|
20
19
|
def self.all
|
21
|
-
|
20
|
+
@@clusters.values
|
22
21
|
end
|
23
22
|
|
24
|
-
def
|
25
|
-
|
23
|
+
def find_by_name(name)
|
24
|
+
nodes.find { |node| node.name == name }
|
26
25
|
end
|
26
|
+
alias_method :[], :find_by_name
|
27
27
|
|
28
|
-
def
|
29
|
-
@
|
28
|
+
def nodes
|
29
|
+
@graph.keys
|
30
30
|
end
|
31
31
|
|
32
|
-
def connect(
|
33
|
-
nodes
|
34
|
-
|
35
|
-
|
36
|
-
unless in_subnet?(subnet_factory.subnet, node)
|
37
|
-
node.subnets << subnet_factory.next
|
38
|
-
node.network = self
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
32
|
+
def connect(*nodes, subnet: SubnetFactory.next_subnet)
|
33
|
+
# Create a complete graph (a graph where all nodes are connected to each
|
34
|
+
# other) between all the nodes, because that's what a subnet is.
|
42
35
|
nodes.permutation(2).each do |(a, b)|
|
36
|
+
@graph[a] ||= []
|
43
37
|
@graph[a] << b
|
44
38
|
end
|
45
|
-
end
|
46
39
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
def adjacent(a)
|
52
|
-
@graph[a]
|
53
|
-
end
|
40
|
+
nodes.each do |node|
|
41
|
+
node.ips << subnet.next_ip
|
42
|
+
node.cluster = self
|
43
|
+
end
|
54
44
|
|
55
|
-
|
56
|
-
@nodes[node.name] = node unless @nodes[node.name]
|
57
|
-
@graph[node] = [] unless @graph[node]
|
45
|
+
subnet.last_ip
|
58
46
|
end
|
59
47
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
a_subnet.include?(b_subnet)
|
48
|
+
def ip_in_same_subnet(a, b)
|
49
|
+
a.ips.find { |a_ip|
|
50
|
+
b.ips.find { |b_ip|
|
51
|
+
a_ip.include?(b_ip)
|
65
52
|
}
|
66
53
|
}
|
67
54
|
end
|
55
|
+
alias_method :neighbor?, :ip_in_same_subnet
|
56
|
+
end
|
68
57
|
|
69
|
-
|
70
|
-
def
|
71
|
-
node.
|
58
|
+
class BGPCluster < Cluster
|
59
|
+
def bgp_neighbors(node)
|
60
|
+
@graph[node].select { |neighbor|
|
61
|
+
neighbor.is_a?(BGPPeer)
|
62
|
+
}
|
72
63
|
end
|
73
64
|
end
|
74
65
|
end
|
data/lib/clusterfuck/machine.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
module Clusterfuck
|
2
2
|
class Machine
|
3
|
-
attr_reader :name, :ssh_port
|
4
|
-
attr_accessor :
|
3
|
+
attr_reader :name, :ssh_port
|
4
|
+
attr_accessor :ips, :gateway, :routes, :cluster
|
5
5
|
|
6
6
|
def initialize(name, **args)
|
7
7
|
@name = name
|
8
|
-
@
|
8
|
+
@ips = []
|
9
9
|
@ssh_port = args[:ssh_port] || PortFactory.next
|
10
10
|
@gateway = args[:gateway]
|
11
11
|
@routes = args[:routes]
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
14
|
+
def ip_in_same_subnet(other)
|
15
|
+
cluster.ip_in_same_subnet(self, other)
|
16
16
|
end
|
17
17
|
|
18
18
|
def build(config)
|
@@ -26,11 +26,11 @@ module Clusterfuck
|
|
26
26
|
host: ssh_port,
|
27
27
|
id: "ssh"
|
28
28
|
|
29
|
-
|
29
|
+
ips.each do |ip|
|
30
30
|
box.vm.network :private_network,
|
31
|
-
ip:
|
31
|
+
ip: ip.addr,
|
32
32
|
# TODO Troll Rubby people by using a refinement
|
33
|
-
netmask:
|
33
|
+
netmask: ip.netmask,
|
34
34
|
# TODO Vagrant already provides an abstraction for declaring networks
|
35
35
|
# so we should not need to duplicate this abstraction by creating
|
36
36
|
# subclasses for each provider. This bullshit needs to be gone for
|
@@ -43,11 +43,11 @@ EOS
|
|
43
43
|
private
|
44
44
|
# TODO clean up these messy functions
|
45
45
|
def tmp_zebra_config
|
46
|
-
File.expand_path("../../../templates/quagga.zebra.conf
|
46
|
+
File.expand_path("../../../templates/quagga.zebra.conf", __FILE__)
|
47
47
|
end
|
48
48
|
|
49
49
|
def tmp_daemons_config
|
50
|
-
File.expand_path("../../../templates/quagga.daemons
|
50
|
+
File.expand_path("../../../templates/quagga.daemons", __FILE__)
|
51
51
|
end
|
52
52
|
|
53
53
|
def tmp_bgp_config
|
@@ -1,20 +1,21 @@
|
|
1
1
|
module Clusterfuck
|
2
2
|
class SubnetFactory
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :last_ip
|
4
4
|
|
5
5
|
START_SUBNET = "10.0.39.0/24"
|
6
6
|
@previous = IPAddr.new(START_SUBNET)
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@
|
8
|
+
def initialize(last_ip)
|
9
|
+
@last_ip = last_ip
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
@
|
12
|
+
def next_ip
|
13
|
+
@last_ip = @last_ip.succ
|
14
14
|
end
|
15
15
|
|
16
|
-
def self.
|
17
|
-
# succ twice to start at x.x.
|
16
|
+
def self.next_subnet(cidr = 24)
|
17
|
+
# `succ` twice to start at x.x.2.1, for example:
|
18
|
+
# 10.0.39.1 -> 10.0.40.1
|
18
19
|
@previous = @previous.mask(cidr).to_range.last.succ.succ
|
19
20
|
SubnetFactory.new(@previous)
|
20
21
|
end
|
data/lib/clusterfuck/version.rb
CHANGED
data/lib/clusterfuck.rb
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
require "ipaddr"
|
2
2
|
require "erb"
|
3
|
-
require_relative "clusterfuck/test_helper"
|
4
3
|
require_relative "clusterfuck/cluster"
|
5
4
|
require_relative "clusterfuck/subnet_factory"
|
6
5
|
require_relative "clusterfuck/machine"
|
7
6
|
require_relative "clusterfuck/bgp_peer"
|
8
7
|
require_relative "clusterfuck/quagga_bgp_router"
|
9
8
|
require_relative "clusterfuck/core_ext/ipaddr"
|
10
|
-
require_relative "clusterfuck/leaf_spine_dsl"
|
11
9
|
|
12
10
|
module Clusterfuck
|
13
11
|
end
|
@@ -11,15 +11,15 @@ password zebra
|
|
11
11
|
router bgp <%= bgp_asn %>
|
12
12
|
bgp router-id <%= bgp_router_id %>
|
13
13
|
timers bgp 1 3
|
14
|
-
<%
|
15
|
-
network <%=
|
14
|
+
<% bgp_announce.each do |announce| %>
|
15
|
+
network <%= announce.to_cidr %>
|
16
16
|
<% end %>
|
17
17
|
|
18
18
|
<% bgp_neighbors.each do |node| %>
|
19
|
-
neighbor <%= node.
|
20
|
-
neighbor <%= node.
|
21
|
-
neighbor <%= node.
|
22
|
-
neighbor <%= node.
|
19
|
+
neighbor <%= node.ip_in_same_subnet(self) %> remote-as <%= node.bgp_asn %>
|
20
|
+
neighbor <%= node.ip_in_same_subnet(self) %> route-map DOWNSTREAM in
|
21
|
+
neighbor <%= node.ip_in_same_subnet(self) %> next-hop-self
|
22
|
+
neighbor <%= node.ip_in_same_subnet(self) %> advertisement-interval 1
|
23
23
|
<% end %>
|
24
24
|
|
25
25
|
ip community-list standard cm-prefmod-300 permit 65534:300
|
File without changes
|
File without changes
|
data/templates/routes.sh.erb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# We can't do this in `/etc/networks/interfaces` because Vagrant doesn't support
|
5
5
|
# adding arbritrary commands to its generated interfaces. This sucks, because it
|
6
|
-
# means we have to
|
6
|
+
# means we have to hardcode the device names and the user has to guess them from
|
7
7
|
# the order of the network definitions. The alternative to this is to run the ip
|
8
8
|
# commands on every boot, which is arguably even worse.
|
9
9
|
#
|
@@ -28,7 +28,7 @@ if [ "$IFACE" != eth1 ]; then
|
|
28
28
|
fi
|
29
29
|
|
30
30
|
<% routes.each do |route| %>
|
31
|
-
ip route add <%= route.to_cidr %> via <%= gateway.
|
31
|
+
ip route add <%= route.to_cidr %> via <%= gateway.ip_in_same_subnet(self) %> dev eth1 || true
|
32
32
|
<% end %>
|
33
33
|
|
34
34
|
exit 0
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class TestBGPCluster < Minitest::Unit::TestCase
|
4
|
+
include Clusterfuck
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@cluster = BGPCluster.new(:test)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_bgp_neighbors_simple
|
11
|
+
one = BGPPeer.new(:peer1)
|
12
|
+
two = BGPPeer.new(:peer2)
|
13
|
+
|
14
|
+
@cluster.connect(one, two)
|
15
|
+
|
16
|
+
assert_equal [one], @cluster.bgp_neighbors(two)
|
17
|
+
assert_equal [two], @cluster.bgp_neighbors(one)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_bgp_neighbors_simple_tree
|
21
|
+
spine = BGPPeer.new(:spine)
|
22
|
+
leaf1 = BGPPeer.new(:leaf1)
|
23
|
+
leaf2 = BGPPeer.new(:leaf2)
|
24
|
+
|
25
|
+
@cluster.connect(spine, leaf1)
|
26
|
+
@cluster.connect(spine, leaf2)
|
27
|
+
|
28
|
+
assert_equal [leaf1, leaf2], @cluster.bgp_neighbors(spine)
|
29
|
+
assert_equal [spine], @cluster.bgp_neighbors(leaf1)
|
30
|
+
assert_equal [spine], @cluster.bgp_neighbors(leaf2)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_bgp_neighbors_tree_with_non_bgp_peers
|
34
|
+
spine = BGPPeer.new(:spine)
|
35
|
+
leaf1 = BGPPeer.new(:leaf1)
|
36
|
+
host11 = Machine.new(:host11)
|
37
|
+
|
38
|
+
leaf2 = BGPPeer.new(:leaf2)
|
39
|
+
host21 = Machine.new(:host21)
|
40
|
+
|
41
|
+
@cluster.connect(spine, leaf1)
|
42
|
+
@cluster.connect(spine, leaf2)
|
43
|
+
|
44
|
+
@cluster.connect(leaf1, host11)
|
45
|
+
@cluster.connect(leaf2, host21)
|
46
|
+
|
47
|
+
assert_equal [leaf1, leaf2], @cluster.bgp_neighbors(spine)
|
48
|
+
assert_equal [spine], @cluster.bgp_neighbors(leaf1)
|
49
|
+
assert_equal [spine], @cluster.bgp_neighbors(leaf2)
|
50
|
+
end
|
51
|
+
end
|
data/test/bgp_peer_test.rb
CHANGED
@@ -25,21 +25,4 @@ class TestBGPPeer < Minitest::Unit::TestCase
|
|
25
25
|
def test_assigned_different_default_ips_as_router_id
|
26
26
|
refute_equal @peer.bgp_router_id, @peer2.bgp_router_id
|
27
27
|
end
|
28
|
-
|
29
|
-
def test_bgp_neighbors_returns_direct_neighbours
|
30
|
-
@cluster.connect(SubnetFactory.next, @peer, @peer2)
|
31
|
-
|
32
|
-
assert_equal [@peer2], @peer.bgp_neighbors
|
33
|
-
assert_equal [@peer], @peer2.bgp_neighbors
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_bgp_neighbours_doesnt_include_non_bgp_peers
|
37
|
-
@non_peer = Machine.new(:test)
|
38
|
-
|
39
|
-
@cluster.connect(SubnetFactory.next, @peer2, @peer)
|
40
|
-
@cluster.connect(SubnetFactory.next, @peer2, @non_peer)
|
41
|
-
|
42
|
-
assert_equal [@peer2], @peer.bgp_neighbors
|
43
|
-
assert_equal [@peer], @peer2.bgp_neighbors
|
44
|
-
end
|
45
28
|
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require "minitest/autorun"
|
2
|
+
require "minitest/unit"
|
3
|
+
require_relative "../lib/clusterfuck"
|
4
|
+
|
5
|
+
class TestCluster < Minitest::Unit::TestCase
|
6
|
+
include Clusterfuck
|
7
|
+
|
8
|
+
def setup
|
9
|
+
SubnetFactory.reset
|
10
|
+
@cluster = Cluster.new(:test)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_two_machine_cluster
|
14
|
+
one = Machine.new(:one)
|
15
|
+
two = Machine.new(:two)
|
16
|
+
|
17
|
+
@cluster.connect(one, two)
|
18
|
+
|
19
|
+
assert_neighbor one, two
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_simple_tree
|
23
|
+
root = Machine.new(:root)
|
24
|
+
leaf1 = Machine.new(:leaf1)
|
25
|
+
leaf2 = Machine.new(:leaf2)
|
26
|
+
|
27
|
+
@cluster.connect(root, leaf1)
|
28
|
+
@cluster.connect(root, leaf2)
|
29
|
+
|
30
|
+
assert_neighbor root, leaf1
|
31
|
+
assert_neighbor root, leaf2
|
32
|
+
refute_neighbor leaf1, leaf2
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_different_ips_on_subnet
|
36
|
+
root = Machine.new(:root)
|
37
|
+
leaf1 = Machine.new(:leaf1)
|
38
|
+
leaf2 = Machine.new(:leaf2)
|
39
|
+
|
40
|
+
@cluster.connect(root, leaf1)
|
41
|
+
@cluster.connect(root, leaf2)
|
42
|
+
|
43
|
+
root_ips = root.ips.map(&:to_s)
|
44
|
+
refute root_ips.include?(*leaf1.ips), "Did not expect leaf1 and root to share ips"
|
45
|
+
refute root_ips.include?(*leaf2.ips), "Did not expect leaf2 and root to share ips"
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_adjacent_on_simple_tree
|
49
|
+
root = Machine.new(:root)
|
50
|
+
leaf1 = Machine.new(:leaf1)
|
51
|
+
leaf2 = Machine.new(:leaf2)
|
52
|
+
|
53
|
+
@cluster.connect(root, leaf1)
|
54
|
+
@cluster.connect(root, leaf2)
|
55
|
+
|
56
|
+
assert_neighbor root, leaf1
|
57
|
+
assert_neighbor leaf1, root
|
58
|
+
|
59
|
+
assert_neighbor root, leaf2
|
60
|
+
assert_neighbor leaf2, root
|
61
|
+
|
62
|
+
refute_neighbor leaf1, leaf2
|
63
|
+
refute_neighbor leaf2, leaf1
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_apply_subnetwork_to_larger_tree
|
67
|
+
root = Machine.new(:root)
|
68
|
+
|
69
|
+
leaf1 = Machine.new(:leaf1)
|
70
|
+
host11 = Machine.new(:host11)
|
71
|
+
|
72
|
+
leaf2 = Machine.new(:leaf2)
|
73
|
+
host21 = Machine.new(:host21)
|
74
|
+
host22 = Machine.new(:host22)
|
75
|
+
|
76
|
+
@cluster.connect(root, leaf1)
|
77
|
+
@cluster.connect(root, leaf2)
|
78
|
+
@cluster.connect(leaf2, host21, host22)
|
79
|
+
@cluster.connect(leaf1, host11)
|
80
|
+
|
81
|
+
assert_neighbor root, leaf1
|
82
|
+
assert_neighbor root, leaf2
|
83
|
+
|
84
|
+
refute_neighbor leaf1, leaf2
|
85
|
+
refute_neighbor leaf1, host21
|
86
|
+
refute_neighbor leaf1, host22
|
87
|
+
|
88
|
+
assert_neighbor leaf2, host21
|
89
|
+
assert_neighbor leaf2, host22
|
90
|
+
assert_neighbor host21, host22
|
91
|
+
|
92
|
+
assert_neighbor leaf1, host11
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_node_is_assigned_ip_with_netmask
|
96
|
+
one = Machine.new(:one)
|
97
|
+
two = Machine.new(:two)
|
98
|
+
|
99
|
+
@cluster.connect(one, two)
|
100
|
+
|
101
|
+
assert_neighbor one, two
|
102
|
+
|
103
|
+
one_ip = one.ips.first
|
104
|
+
two_ip = two.ips.first
|
105
|
+
|
106
|
+
assert_equal "255.255.255.0", one_ip.netmask
|
107
|
+
assert_equal "10.0.40.2", one_ip.addr
|
108
|
+
|
109
|
+
assert_equal "255.255.255.0", two_ip.netmask
|
110
|
+
assert_equal "10.0.40.3", two_ip.addr
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_ip_in_same_subnet_returns_shared_ip
|
114
|
+
one = Machine.new(:one)
|
115
|
+
two = Machine.new(:two)
|
116
|
+
|
117
|
+
@cluster.connect(one, two)
|
118
|
+
|
119
|
+
assert_equal "10.0.40.2/24", @cluster.ip_in_same_subnet(one, two).to_cidr
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
def assert_neighbor(*nodes)
|
124
|
+
assert @cluster.neighbor?(*nodes), "Expected #{nodes.inspect} to be neighbors"
|
125
|
+
end
|
126
|
+
|
127
|
+
def refute_neighbor(*nodes)
|
128
|
+
refute @cluster.neighbor?(*nodes), "Did not expect #{nodes.inspect} to be neighbors"
|
129
|
+
end
|
130
|
+
end
|
data/test/machine_test.rb
CHANGED
data/test/subnet_test.rb
CHANGED
@@ -7,35 +7,27 @@ class TestSubnet < Minitest::Unit::TestCase
|
|
7
7
|
|
8
8
|
def setup
|
9
9
|
SubnetFactory.reset
|
10
|
+
@subnet = SubnetFactory.next_subnet
|
10
11
|
end
|
11
12
|
|
12
13
|
def test_next_subnet
|
13
|
-
|
14
|
-
assert_equal IPAddr.new("10.0.40.2"), subnet.next
|
14
|
+
assert_equal IPAddr.new("10.0.40.2"), @subnet.next_ip
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_next_ip_on_subnet
|
18
|
-
|
19
|
-
assert_equal IPAddr.new("10.0.40.
|
20
|
-
assert_equal IPAddr.new("10.0.40.3"), subnet.next
|
18
|
+
assert_equal IPAddr.new("10.0.40.2"), @subnet.next_ip
|
19
|
+
assert_equal IPAddr.new("10.0.40.3"), @subnet.next_ip
|
21
20
|
end
|
22
21
|
|
23
22
|
def test_next_subnet_twice
|
24
|
-
|
25
|
-
assert_equal IPAddr.new("10.0.40.2"), subnet.next
|
23
|
+
assert_equal IPAddr.new("10.0.40.2"), @subnet.next_ip
|
26
24
|
|
27
|
-
subnet = SubnetFactory.
|
28
|
-
assert_equal IPAddr.new("10.0.41.2"), subnet.
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_next_16_subnet
|
32
|
-
subnet = SubnetFactory.next(16)
|
33
|
-
assert_equal IPAddr.new("10.1.0.2"), subnet.next
|
25
|
+
subnet = SubnetFactory.next_subnet
|
26
|
+
assert_equal IPAddr.new("10.0.41.2"), subnet.next_ip
|
34
27
|
end
|
35
28
|
|
36
29
|
def test_mask_returns_classic_syntax_netmask
|
37
|
-
|
38
|
-
assert_equal "255.255.255.0", subnet.next.netmask
|
30
|
+
assert_equal "255.255.255.0", @subnet.next_ip.netmask
|
39
31
|
end
|
40
32
|
|
41
33
|
def test_cidr_on_subet
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vagrant-clusterfuck
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Eskildsen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,7 +52,7 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
description: Clusterfuck
|
55
|
+
description: Clusterfuck lets you fuck with clusters
|
56
56
|
email:
|
57
57
|
- sirup@sirupsen.com
|
58
58
|
executables: []
|
@@ -71,20 +71,18 @@ files:
|
|
71
71
|
- lib/clusterfuck/bgp_peer.rb
|
72
72
|
- lib/clusterfuck/cluster.rb
|
73
73
|
- lib/clusterfuck/core_ext/ipaddr.rb
|
74
|
-
- lib/clusterfuck/leaf_spine_dsl.rb
|
75
74
|
- lib/clusterfuck/machine.rb
|
76
75
|
- lib/clusterfuck/quagga_bgp_router.rb
|
77
76
|
- lib/clusterfuck/subnet_factory.rb
|
78
|
-
- lib/clusterfuck/test_helper.rb
|
79
77
|
- lib/clusterfuck/version.rb
|
80
78
|
- templates/quagga.bgpd.conf.erb
|
81
|
-
- templates/quagga.daemons
|
82
|
-
- templates/quagga.zebra.conf
|
79
|
+
- templates/quagga.daemons
|
80
|
+
- templates/quagga.zebra.conf
|
83
81
|
- templates/routes.sh.erb
|
82
|
+
- test/bgp_network_test.rb
|
84
83
|
- test/bgp_peer_test.rb
|
85
|
-
- test/
|
84
|
+
- test/cluster_test.rb
|
86
85
|
- test/machine_test.rb
|
87
|
-
- test/network_test.rb
|
88
86
|
- test/subnet_test.rb
|
89
87
|
- test/test_helper.rb
|
90
88
|
homepage: ''
|
@@ -110,11 +108,11 @@ rubyforge_project:
|
|
110
108
|
rubygems_version: 2.2.2
|
111
109
|
signing_key:
|
112
110
|
specification_version: 4
|
113
|
-
summary: Clusterfuck
|
111
|
+
summary: Clusterfuck lets you fuck with clusters
|
114
112
|
test_files:
|
113
|
+
- test/bgp_network_test.rb
|
115
114
|
- test/bgp_peer_test.rb
|
116
|
-
- test/
|
115
|
+
- test/cluster_test.rb
|
117
116
|
- test/machine_test.rb
|
118
|
-
- test/network_test.rb
|
119
117
|
- test/subnet_test.rb
|
120
118
|
- test/test_helper.rb
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module Clusterfuck
|
2
|
-
module LeafSpineDSL
|
3
|
-
def cluster(name, &block)
|
4
|
-
@cluster = Cluster.create(name)
|
5
|
-
yield(@cluster)
|
6
|
-
end
|
7
|
-
|
8
|
-
def spine(name, **args, &block)
|
9
|
-
vip_subnet = args[:vip_subnet] || IPAddr.new("192.168.99.1/24")
|
10
|
-
priv_subnet = args[:priv_subnet] || IPAddr.new("10.0.0.0/16")
|
11
|
-
args = { announced: [vip_subnet, priv_subnet] }.merge(args)
|
12
|
-
|
13
|
-
driver = args[:driver] || QuaggaBGPRouter
|
14
|
-
|
15
|
-
@spine = driver.new(name, args)
|
16
|
-
yield(@spine)
|
17
|
-
end
|
18
|
-
|
19
|
-
def leaf(name, **args, &block)
|
20
|
-
driver = args[:driver] || QuaggaBGPRouter
|
21
|
-
leaf_spine_subnet = args[:spine_subnet] || SubnetFactory.next
|
22
|
-
|
23
|
-
@leaf = driver.new(name, **args)
|
24
|
-
@cluster.connect(leaf_spine_subnet, @leaf, @spine)
|
25
|
-
|
26
|
-
yield(@leaf)
|
27
|
-
end
|
28
|
-
|
29
|
-
def host(name, **args)
|
30
|
-
driver = args[:driver] || Machine
|
31
|
-
leaf_subnet = args[:leaf_subnet] || SubnetFactory.next
|
32
|
-
|
33
|
-
args = {
|
34
|
-
gateway: @leaf,
|
35
|
-
routes: @spine.bgp_announced,
|
36
|
-
}.merge(args)
|
37
|
-
|
38
|
-
machine = driver.new(name, args)
|
39
|
-
|
40
|
-
# TODO this sucks
|
41
|
-
@leaf.bgp_announced = @leaf.bgp_announced.push(leaf_subnet.subnet)
|
42
|
-
@cluster.connect(leaf_subnet, @leaf, machine)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module Clusterfuck
|
2
|
-
module TestHelper
|
3
|
-
SSHCommandFailedError = Class.new(StandardError)
|
4
|
-
|
5
|
-
def self.included(base)
|
6
|
-
require "./cluster"
|
7
|
-
end
|
8
|
-
|
9
|
-
# We do not check exit status, don't want to control people.
|
10
|
-
def ssh_command(host, command, cluster: nil)
|
11
|
-
`ssh -i .vagrant/machines/#{host}/virtualbox/private_key vagrant@127.0.0.1 -p #{host(host, cluster: cluster).ssh_port} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o PasswordAuthentication=no -o IdentitiesOnly=yes -o LogLevel=fatal "#{command}"`
|
12
|
-
end
|
13
|
-
|
14
|
-
def host(name, cluster: nil)
|
15
|
-
cluster(cluster).nodes.find { |node| node.name == name }
|
16
|
-
end
|
17
|
-
|
18
|
-
def ip_to_host(ip, cluster: nil)
|
19
|
-
cluster(cluster).nodes.find { |host|
|
20
|
-
host.subnets.find { |subnet|
|
21
|
-
subnet == ip
|
22
|
-
}
|
23
|
-
}
|
24
|
-
end
|
25
|
-
|
26
|
-
# TODO add iptables(1) and nc(1) wrappers to cause ~chaos~
|
27
|
-
private
|
28
|
-
def cluster(name)
|
29
|
-
if name
|
30
|
-
Clusterfuck::Cluster[name]
|
31
|
-
else
|
32
|
-
Clusterfuck::Cluster.all.first
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
data/test/helper_test.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require_relative "test_helper"
|
2
|
-
|
3
|
-
class TestTestHelper < Minitest::Unit::TestCase
|
4
|
-
include Clusterfuck::TestHelper
|
5
|
-
|
6
|
-
def test_host
|
7
|
-
assert_equal :leaf1, host(:leaf1).name
|
8
|
-
end
|
9
|
-
|
10
|
-
def test_ssh_command_into_node
|
11
|
-
output = ssh_command :leaf1, "hostname"
|
12
|
-
assert_equal "leaf1\n", output
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_ip_to_host
|
16
|
-
spine_ip = host(:spine).subnets.first.to_s
|
17
|
-
assert_equal :spine, ip_to_host(spine_ip).name
|
18
|
-
end
|
19
|
-
end
|
data/test/network_test.rb
DELETED
@@ -1,201 +0,0 @@
|
|
1
|
-
require "minitest/autorun"
|
2
|
-
require "minitest/unit"
|
3
|
-
require_relative "../lib/clusterfuck"
|
4
|
-
|
5
|
-
class TestNetwork < Minitest::Unit::TestCase
|
6
|
-
include Clusterfuck
|
7
|
-
|
8
|
-
def setup
|
9
|
-
SubnetFactory.reset
|
10
|
-
@cluster = Cluster.new(:test)
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_one_machine_cluster
|
14
|
-
one = Machine.new(:one)
|
15
|
-
@cluster.register(one)
|
16
|
-
|
17
|
-
assert_equal({one => []}, @cluster.graph)
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_two_machine_cluster
|
21
|
-
one = Machine.new(:one)
|
22
|
-
two = Machine.new(:two)
|
23
|
-
|
24
|
-
@cluster.connect(SubnetFactory.next, one, two)
|
25
|
-
|
26
|
-
assert_equal({one => [two], two => [one]}, @cluster.graph)
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_simple_tree
|
30
|
-
root = Machine.new(:root)
|
31
|
-
leaf1 = Machine.new(:leaf1)
|
32
|
-
leaf2 = Machine.new(:leaf2)
|
33
|
-
|
34
|
-
@cluster.connect(SubnetFactory.next, root, leaf1)
|
35
|
-
@cluster.connect(SubnetFactory.next, root, leaf2)
|
36
|
-
|
37
|
-
assert_equal({root => [leaf1, leaf2], leaf1 => [root], leaf2 => [root]}, @cluster.graph)
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_different_ips_on_subnet
|
41
|
-
root = Machine.new(:root)
|
42
|
-
leaf1 = Machine.new(:leaf1)
|
43
|
-
leaf2 = Machine.new(:leaf2)
|
44
|
-
|
45
|
-
@cluster.connect(SubnetFactory.next, root, leaf1)
|
46
|
-
@cluster.connect(SubnetFactory.next, root, leaf2)
|
47
|
-
|
48
|
-
refute root.subnets.map(&:to_s).include?(leaf1.subnets.first.to_s)
|
49
|
-
refute root.subnets.map(&:to_s).include?(leaf2.subnets.first.to_s)
|
50
|
-
end
|
51
|
-
|
52
|
-
def test_adjacent_on_simple_tree
|
53
|
-
root = Machine.new(:root)
|
54
|
-
leaf1 = Machine.new(:leaf1)
|
55
|
-
leaf2 = Machine.new(:leaf2)
|
56
|
-
|
57
|
-
@cluster.connect(SubnetFactory.next, root, leaf1)
|
58
|
-
@cluster.connect(SubnetFactory.next, root, leaf2)
|
59
|
-
|
60
|
-
assert @cluster.adjacent?(root, leaf1)
|
61
|
-
assert @cluster.adjacent?(leaf1, root)
|
62
|
-
|
63
|
-
assert @cluster.adjacent?(root, leaf2)
|
64
|
-
assert @cluster.adjacent?(leaf2, root)
|
65
|
-
|
66
|
-
refute @cluster.adjacent?(leaf1, leaf2)
|
67
|
-
refute @cluster.adjacent?(leaf2, leaf1)
|
68
|
-
end
|
69
|
-
|
70
|
-
def test_apply_subnetwork_to_connected_graph
|
71
|
-
one = Machine.new(:one)
|
72
|
-
two = Machine.new(:two)
|
73
|
-
|
74
|
-
@cluster.connect(SubnetFactory.next, one, two)
|
75
|
-
|
76
|
-
assert_equal [IPAddr.new("10.0.40.2")], one.subnets
|
77
|
-
assert_equal [IPAddr.new("10.0.40.3")], two.subnets
|
78
|
-
|
79
|
-
assert_overlapping_subnets one, two
|
80
|
-
|
81
|
-
assert_equal 1, one.subnets.size
|
82
|
-
assert_equal 1, two.subnets.size
|
83
|
-
end
|
84
|
-
|
85
|
-
def test_apply_subnetwork_to_simple_tree
|
86
|
-
root = Machine.new(:root)
|
87
|
-
leaf1 = Machine.new(:leaf1)
|
88
|
-
leaf2 = Machine.new(:leaf2)
|
89
|
-
|
90
|
-
@cluster.connect(SubnetFactory.next, root, leaf1)
|
91
|
-
@cluster.connect(SubnetFactory.next, root, leaf2)
|
92
|
-
|
93
|
-
assert_overlapping_subnets root, leaf1
|
94
|
-
assert_overlapping_subnets root, leaf2
|
95
|
-
refute_overlapping_subnets leaf1, leaf2
|
96
|
-
|
97
|
-
assert_equal 2, root.subnets.size
|
98
|
-
assert_equal 1, leaf1.subnets.size
|
99
|
-
assert_equal 1, leaf2.subnets.size
|
100
|
-
end
|
101
|
-
|
102
|
-
def test_apply_subnetwork_to_larger_tree
|
103
|
-
root = Machine.new(:root)
|
104
|
-
|
105
|
-
leaf1 = Machine.new(:leaf1)
|
106
|
-
host11 = Machine.new(:host11)
|
107
|
-
|
108
|
-
leaf2 = Machine.new(:leaf2)
|
109
|
-
host21 = Machine.new(:host21)
|
110
|
-
host22 = Machine.new(:host21)
|
111
|
-
|
112
|
-
@cluster.connect(SubnetFactory.next, root, leaf1)
|
113
|
-
@cluster.connect(SubnetFactory.next, root, leaf2)
|
114
|
-
|
115
|
-
leaf2_subnet = SubnetFactory.next
|
116
|
-
@cluster.connect(leaf2_subnet, leaf2, host21)
|
117
|
-
@cluster.connect(leaf2_subnet, leaf2, host22)
|
118
|
-
@cluster.connect(leaf2_subnet, host21, host22)
|
119
|
-
|
120
|
-
leaf1_subnet = SubnetFactory.next
|
121
|
-
@cluster.connect(leaf1_subnet, leaf1, host11)
|
122
|
-
|
123
|
-
assert_overlapping_subnets root, leaf1
|
124
|
-
assert_overlapping_subnets root, leaf2
|
125
|
-
|
126
|
-
refute_overlapping_subnets leaf1, leaf2
|
127
|
-
refute_overlapping_subnets leaf1, host21
|
128
|
-
refute_overlapping_subnets leaf1, host22
|
129
|
-
|
130
|
-
assert_overlapping_subnets leaf2, host21
|
131
|
-
assert_overlapping_subnets leaf2, host22
|
132
|
-
assert_overlapping_subnets host21, host22
|
133
|
-
|
134
|
-
assert_overlapping_subnets leaf1, host11
|
135
|
-
|
136
|
-
assert_equal 2, root.subnets.size
|
137
|
-
|
138
|
-
assert_equal 2, leaf2.subnets.size
|
139
|
-
assert_equal 1, host21.subnets.size
|
140
|
-
assert_equal 1, host22.subnets.size
|
141
|
-
|
142
|
-
assert_equal 2, leaf1.subnets.size
|
143
|
-
assert_equal 1, host11.subnets.size
|
144
|
-
end
|
145
|
-
|
146
|
-
def test_overlapping_subnets_with_one
|
147
|
-
one = Machine.new(:one)
|
148
|
-
two = Machine.new(:two)
|
149
|
-
|
150
|
-
@cluster.connect(SubnetFactory.next, one, two)
|
151
|
-
|
152
|
-
assert_equal [IPAddr.new("10.0.40.2")], @cluster.overlapping_subnets(one, two)
|
153
|
-
end
|
154
|
-
|
155
|
-
def test_overlapping_subnets_in_tree
|
156
|
-
root = Machine.new(:root)
|
157
|
-
leaf1 = Machine.new(:leaf1)
|
158
|
-
leaf2 = Machine.new(:leaf2)
|
159
|
-
|
160
|
-
@cluster.connect(SubnetFactory.next, root, leaf1)
|
161
|
-
@cluster.connect(SubnetFactory.next, root, leaf2)
|
162
|
-
|
163
|
-
assert_equal [IPAddr.new("10.0.40.2")], @cluster.overlapping_subnets(root, leaf1)
|
164
|
-
assert_equal [IPAddr.new("10.0.41.2")], @cluster.overlapping_subnets(root, leaf2)
|
165
|
-
assert_equal [], @cluster.overlapping_subnets(leaf1, leaf2)
|
166
|
-
end
|
167
|
-
|
168
|
-
def test_support_multiple_arguments_to_connect
|
169
|
-
one = Machine.new(:one)
|
170
|
-
two = Machine.new(:two)
|
171
|
-
three = Machine.new(:three)
|
172
|
-
|
173
|
-
@cluster.connect(SubnetFactory.next, one, two, three)
|
174
|
-
|
175
|
-
assert_overlapping_subnets one, two
|
176
|
-
assert_overlapping_subnets one, three
|
177
|
-
assert_overlapping_subnets two, three
|
178
|
-
end
|
179
|
-
|
180
|
-
private
|
181
|
-
|
182
|
-
def assert_overlapping_subnets(a, b)
|
183
|
-
assert overlapping_subnets?(a, b), <<-EOE
|
184
|
-
Expected networks to overlap:
|
185
|
-
#{a.name}: #{a.subnets}
|
186
|
-
#{b.name}: #{b.subnets}
|
187
|
-
EOE
|
188
|
-
end
|
189
|
-
|
190
|
-
def refute_overlapping_subnets(a, b)
|
191
|
-
refute overlapping_subnets?(a, b), <<-EOE
|
192
|
-
Expected networks to not overlap:
|
193
|
-
#{a.name}: #{a.subnets}
|
194
|
-
#{b.name}: #{b.subnets}
|
195
|
-
EOE
|
196
|
-
end
|
197
|
-
|
198
|
-
def overlapping_subnets?(a, b)
|
199
|
-
@cluster.overlapping_subnets(a, b).any?
|
200
|
-
end
|
201
|
-
end
|