vagrant-clusterfuck 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|