cyoi 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NWU4Y2U2YzZjMjI1ZmI0ZTdlNzM4OTA3ZjBhZmY2NmY2MjVlNGM5ZA==
5
- data.tar.gz: !binary |-
6
- MDhjN2E4NzE4ODgyOTk5ZDA4ODBhYTgxZWU0MzM4NmZiMjAyMDc3Yg==
2
+ SHA1:
3
+ metadata.gz: a3fba8bd87ffbf411952d31cf1c8e9cabfcf8cd0
4
+ data.tar.gz: 55688a09f5dba6f443ed83330eef6c0bc46349f1
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NGNjNTU4ZWMzMDA0M2JkMzRlMDRhYWJhYjZlMDFjNDVjYzVmYTIyNmI3NGIy
10
- NGJmYTJjN2ZkOGYwOTcyNzYyZTI3NTI4OTVhNmQ1MTFjNzE3NThlNGZlYmMw
11
- Yzc2NTExNTdiZWE3MTBiYWJmZmNkNmQ4MzdiYzY0YTcxMDJlODQ=
12
- data.tar.gz: !binary |-
13
- NTIzZTEyMWRhMjcyZmEyZDExMDM0MjRhMWJlODlkZDNmNzdhNWY3NWE2MTBl
14
- NGExMWQ0NDhiYTZkYmQ2NjgyZjc2MjFjOTIxYTlmYWQ2MzhhYjBjZDk2ZTA1
15
- ZWZhOTFjMmJkZThkODMzMjIzMDAwOGE0ZjRiOWNlZTcwODVhMDg=
6
+ metadata.gz: 6e5c897fccb38b5fb9ded020a03bd5bdb1b28346ec41945d5d63fb4d331b4f2fea8fc1d929a05b06c1484c748c1b83faaec41ee8c95568502174f6fa0fc988e8
7
+ data.tar.gz: 08208b223d39521fa851a1767716c3e6c4b25242aa46917fb6438c1bc2aeb741217a4284d0121e6aef9630e7120e31d4b6169de04c2fc17f846a194407a9a53e
data/ChangeLog.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  Cyoi (choose-your-own-infrastructure) is a library to ask an end-user to choose an infrastructure (AWS, OpenStack, etc), region, and login credentials.
4
4
 
5
+ ## v0.8
6
+
7
+ * openstack - detection of nova vs neutron networking
8
+ * openstack nova - continues to provision a floating IP
9
+ * openstack neutron - asks to select a subnet and then an available IP
10
+
5
11
  ## v0.7
6
12
 
7
13
  * vsphere provising
data/cyoi.gemspec CHANGED
@@ -26,8 +26,9 @@ README
26
26
  spec.add_dependency "highline", "~> 1.6"
27
27
  spec.add_dependency "readwritesettings", "~> 3.0"
28
28
 
29
- spec.add_development_dependency "bundler", "~> 1.3"
29
+ spec.add_development_dependency "bundler"
30
30
  spec.add_development_dependency "rake"
31
31
  spec.add_development_dependency "rspec"
32
+ spec.add_development_dependency "rspec-fire"
32
33
  spec.add_development_dependency "aruba"
33
34
  end
@@ -13,7 +13,12 @@ class Cyoi::Cli::Addresses::AddressCliOpenstack
13
13
 
14
14
  def perform_and_return_attributes
15
15
  unless valid_address?
16
- provision_address
16
+ if networks?
17
+ subnet = select_subnet
18
+ choose_address_from_subnet(subnet)
19
+ else
20
+ provision_address
21
+ end
17
22
  end
18
23
  export_attributes
19
24
  end
@@ -39,6 +44,48 @@ class Cyoi::Cli::Addresses::AddressCliOpenstack
39
44
  attributes["ip"] = provider_client.provision_public_ip_address
40
45
  puts attributes.ip
41
46
  end
47
+
48
+ def networks?
49
+ provider_client.networks?
50
+ end
51
+
52
+ def select_subnet
53
+ subnets = provider_client.subnets
54
+ subnet = if subnets.size == 0
55
+ $stderr.puts "ERROR: Your OpenStack is configured for Neutron networking but you have not yet created any subnets."
56
+ exit 1
57
+ elsif subnets.size == 1
58
+ subnets.first
59
+ else
60
+ hl.choose do |menu|
61
+ menu.prompt = "Choose a subnet: "
62
+ # menu.choice("AWS") { "aws" }
63
+ subnets.each do |subnet|
64
+ menu.choice("#{pretty_subnet_name(subnet)}") { subnet }
65
+ end
66
+ end
67
+ end
68
+ attributes["subnet_id"] = subnet.id
69
+ subnet
70
+ end
71
+
72
+ def choose_address_from_subnet(subnet)
73
+ default_ip = provider_client.next_available_ip_in_subnet(subnet)
74
+ puts "\n"
75
+ ip = hl.ask("Choose IP ") { |q| q.default = default_ip }.to_s
76
+ attributes["ip"] = ip
77
+ end
78
+
79
+ def pretty_ip_pool_ranges(subnet)
80
+ ranges = subnet.allocation_pools.map do |pool|
81
+ "#{pool['start']}-#{pool['end']}"
82
+ end
83
+ ranges.join(',')
84
+ end
85
+
86
+ def pretty_subnet_name(subnet)
87
+ "#{subnet.name} (#{subnet.cidr})"
88
+ end
42
89
  end
43
90
 
44
91
  Cyoi::Cli::Address.register_address_cli("openstack", Cyoi::Cli::Addresses::AddressCliOpenstack)
@@ -2,17 +2,31 @@
2
2
 
3
3
  module Cyoi; module Providers; module Clients; end; end; end
4
4
 
5
+ require "ipaddr"
5
6
  require "cyoi/providers/clients/fog_provider_client"
6
7
  require "cyoi/providers/constants/openstack_constants"
7
8
 
8
9
  class Cyoi::Providers::Clients::OpenStackProviderClient < Cyoi::Providers::Clients::FogProviderClient
10
+ # @return [boolean] true if target OpenStack running Neutron networks
11
+ def networks?
12
+ fog_network
13
+ end
14
+
15
+ def subnets
16
+ fog_network.subnets
17
+ end
18
+
9
19
  # @return [String] provisions a new public IP address in target region
10
- # TODO nil if none available
11
20
  def provision_public_ip_address(options={})
12
- pool = fog_compute.addresses.get_address_pools.first
13
- address = fog_compute.addresses.create(:pool => pool["name"])
21
+ pool_name = options.delete("pool_name")
22
+ pool_name ||= begin
23
+ pool = fog_compute.addresses.get_address_pools.first
24
+ pool["name"]
25
+ end
26
+ address = fog_compute.addresses.create(:pool => pool_name)
14
27
  address.ip
15
- # TODO catch error and return nil
28
+ rescue NoMethodError
29
+ print "No Public IP Found"
16
30
  end
17
31
 
18
32
  def associate_ip_address_with_server(ip_address, server)
@@ -20,6 +34,36 @@ class Cyoi::Providers::Clients::OpenStackProviderClient < Cyoi::Providers::Clien
20
34
  address.server = server
21
35
  end
22
36
 
37
+ # @return [Array] of IPs that are not allocated to a server
38
+ # Defaults to the first address pool unless
39
+ # "pool_name" is provided in options
40
+ def unallocated_floating_ip_addresses(options={})
41
+ pool_name = options.delete("pool_name")
42
+ pool_name ||= begin
43
+ pool = fog_compute.addresses.get_address_pools.first
44
+ pool["name"]
45
+ end
46
+ fog_compute.addresses.
47
+ select { |a| a.pool == pool_name && a.instance_id.nil? }.
48
+ map(&:ip)
49
+ end
50
+
51
+ # @return [String] IP that is available for a new VM to use
52
+ # allocation_pools look like:
53
+ # "allocation_pools" => [{"start"=>"192.168.101.2", "end"=>"192.168.101.254"}]
54
+ def next_available_ip_in_subnet(subnet)
55
+ ip = IPAddr.new(subnet.allocation_pools.first["start"])
56
+ skip_ips = ip_addresses_assigned_to_servers
57
+ while skip_ips.include?(ip.to_s)
58
+ ip = ip.succ
59
+ end
60
+ ip.to_s
61
+ end
62
+
63
+ def ip_addresses_assigned_to_servers
64
+ fog_compute.servers.map {|s| s.addresses}.map {|address_hash| address_hash.map {|name, addrs| addrs}}.flatten.map {|addr| addr["addr"]}
65
+ end
66
+
23
67
  # Hook method for FogProviderClient#create_security_group
24
68
  def ip_permissions(sg)
25
69
  sg.rules
@@ -59,17 +103,27 @@ class Cyoi::Providers::Clients::OpenStackProviderClient < Cyoi::Providers::Clien
59
103
  raise "not implemented yet"
60
104
  end
61
105
 
62
- # Construct a Fog::Compute object
63
- # Uses +attributes+ which normally originates from +settings.provider+
64
- def setup_fog_connection
106
+ def configuration
65
107
  configuration = Fog.symbolize_credentials(attributes.credentials)
66
108
  configuration[:provider] = "OpenStack"
67
109
  if attributes.credentials.openstack_region && attributes.credentials.openstack_region.empty?
68
110
  configuration.delete(:openstack_region)
69
111
  end
112
+ configuration
113
+ end
114
+
115
+ # Construct a Fog::Compute object
116
+ # Uses +attributes+ which normally originates from +settings.provider+
117
+ def setup_fog_connection
70
118
  @fog_compute = Fog::Compute.new(configuration)
71
119
  end
72
120
 
121
+ def fog_network
122
+ @fog_network ||= Fog::Network.new(configuration)
123
+ rescue Fog::Errors::NotFound
124
+ nil
125
+ end
126
+
73
127
  def openstack_constants
74
128
  Cyoi::Providers::Constants::OpenStackConstants
75
129
  end
data/lib/cyoi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Cyoi
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -0,0 +1,9 @@
1
+ require 'rspec/fire'
2
+
3
+ RSpec.configure do |config|
4
+ config.include(RSpec::Fire)
5
+ end
6
+
7
+ RSpec::Fire.configure do |config|
8
+ config.verify_constant_names = true
9
+ end
@@ -0,0 +1,110 @@
1
+ require "fog"
2
+ require "fog/openstack"
3
+ require 'fog/openstack/models/network/subnets'
4
+ require "cyoi/providers"
5
+
6
+ describe "cyoi address openstack" do
7
+ before { Fog.mock!; Fog::Mock.reset }
8
+ let(:provider_attributes) do
9
+ {
10
+ "name" => "openstack",
11
+ "credentials" => {
12
+ "openstack_username" => "USERNAME",
13
+ "openstack_api_key" => "PASSWORD",
14
+ "openstack_tenant" => "TENANT",
15
+ "openstack_auth_url" => "http://someurl.com/v2/tokens",
16
+ "openstack_region" => "REGION"
17
+ }
18
+ }
19
+ end
20
+
21
+ let(:fog_compute) { instance_double("Fog::Compute::OpenStack::Real") }
22
+ let(:fog_network_const) { class_double("Fog::Network").as_stubbed_const(:transfer_nested_constants => [:OpenStack]) }
23
+ let(:fog_network) { instance_double("Fog::Network::OpenStack::Real") }
24
+ let(:addresses) { instance_double("Fog::Compute::OpenStack::Addresses") }
25
+ let(:address) { instance_double("Fog::Compute::OpenStack::Address", ip: '1.2.3.4') }
26
+ let(:unallocated_address) { instance_double("Fog::Compute::OpenStack::Address", ip: '1.2.3.4', instance_id: nil, pool: 'INTERNET') }
27
+ let(:allocated_address) { instance_double("Fog::Compute::OpenStack::Address", ip: '1.2.3.4', instance_id: 'XXX', pool: 'INTERNET') }
28
+ let(:subnets) { instance_double("Fog::Network::OpenStack::Subnets") }
29
+ let(:networks) {}
30
+
31
+ subject { Cyoi::Providers.provider_client(provider_attributes) }
32
+
33
+ it "is an OpenStack provider" do
34
+ expect(subject).to be_instance_of(Cyoi::Providers::Clients::OpenStackProviderClient)
35
+ end
36
+
37
+ context "provision_public_ip_address" do
38
+ it "defaults to pool: 'nova' for nova" do
39
+ expect(subject).to receive(:fog_compute).exactly(2).times.and_return(fog_compute)
40
+ expect(fog_compute).to receive(:addresses).exactly(2).times.and_return(addresses)
41
+ expect(addresses).to receive(:get_address_pools).and_return([{"name"=>"nova"}])
42
+ expect(addresses).to receive(:create).with(pool: "nova").and_return(address)
43
+ expect(subject.provision_public_ip_address).to eq('1.2.3.4')
44
+ end
45
+
46
+ it "specify a pool name" do
47
+ expect(subject).to receive(:fog_compute).and_return(fog_compute)
48
+ expect(fog_compute).to receive(:addresses).and_return(addresses)
49
+ expect(addresses).to receive(:create).with(pool: "INTERNET").and_return(address)
50
+ expect(subject.provision_public_ip_address("pool_name" => "INTERNET")).to eq('1.2.3.4')
51
+ end
52
+ end
53
+
54
+ it "returns unallocated floating IPs for a pool" do
55
+ expect(subject).to receive(:fog_compute).and_return(fog_compute)
56
+ expect(fog_compute).to receive(:addresses).and_return([unallocated_address, allocated_address])
57
+ expect(subject.unallocated_floating_ip_addresses("pool_name" => "INTERNET")).to eq(['1.2.3.4'])
58
+ end
59
+
60
+ context "networks" do
61
+ it "has no networks" do
62
+ expect(subject).to receive(:fog_network).and_return(nil)
63
+ expect(subject.networks?).to be_nil
64
+ end
65
+
66
+ context "has networks" do
67
+ before do
68
+ end
69
+
70
+ it "for sure" do
71
+ expect(subject).to receive(:fog_network).and_return(fog_network)
72
+ expect(subject.networks?).to_not be_nil
73
+ end
74
+
75
+ it "available subnets" do
76
+ expect(subject).to receive(:fog_network).and_return(fog_network)
77
+ expect(fog_network).to receive(:subnets).and_return(subnets)
78
+ expect(subject.subnets).to eq(subnets)
79
+ end
80
+ end
81
+
82
+ context 'next_available_ip_in_subnet' do
83
+ let(:subnet) { Fog::Network::OpenStack::Subnet.new(
84
+ "cidr" => "192.168.101.0/24",
85
+ "gateway_ip" => "192.168.101.1",
86
+ "allocation_pools" => [{"start"=>"192.168.101.2", "end"=>"192.168.101.254"}]
87
+ ) }
88
+ it "returns next IP in allocation pools" do
89
+ expect(subject.next_available_ip_in_subnet(subnet)).to eq("192.168.101.2")
90
+ end
91
+
92
+ it "avoids IP addresses already allocated to other servers" do
93
+ expect(subject).to receive(:ip_addresses_assigned_to_servers).and_return(["192.168.101.2", "192.168.101.3"])
94
+ expect(subject.next_available_ip_in_subnet(subnet)).to eq("192.168.101.4")
95
+ end
96
+ end
97
+
98
+ context 'ip_addresses_assigned_to_servers' do
99
+ let(:addresses) do
100
+ {"Internet Access "=>[{"OS-EXT-IPS-MAC:mac_addr"=>"fa:16:3e:c0:4b:b3", "version"=>4, "addr"=>"192.168.101.2", "OS-EXT-IPS:type"=>"fixed"}, {"OS-EXT-IPS-MAC:mac_addr"=>"fa:16:3e:c0:4b:b3", "version"=>4, "addr"=>"174.128.50.11", "OS-EXT-IPS:type"=>"floating"}]}
101
+ end
102
+ let(:servers) { [instance_double("Fog::Compute::OpenStack::Server", addresses: addresses)] }
103
+ it "list of IPs" do
104
+ expect(subject).to receive(:fog_compute).and_return(fog_compute)
105
+ expect(fog_compute).to receive(:servers).and_return(servers)
106
+ expect(subject.ip_addresses_assigned_to_servers).to eq(["192.168.101.2", "174.128.50.11"])
107
+ end
108
+ end
109
+ end
110
+ end
metadata CHANGED
@@ -1,111 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cyoi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dr Nic Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-15 00:00:00.000000000 Z
11
+ date: 2014-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fog
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ! '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ! '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: highline
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.6'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.6'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: readwritesettings
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '3.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ~>
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '1.3'
61
+ version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '1.3'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ! '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ! '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ! '>='
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec-fire
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
88
102
  - !ruby/object:Gem::Version
89
103
  version: '0'
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
- - - ! '>='
108
+ - - ">="
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: aruba
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
- - - ! '>='
115
+ - - ">="
102
116
  - !ruby/object:Gem::Version
103
117
  version: '0'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
- - - ! '>='
122
+ - - ">="
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
111
125
  description: A library to ask an end-user to choose an infrastructure (AWS, OpenStack,
@@ -117,9 +131,9 @@ executables:
117
131
  extensions: []
118
132
  extra_rdoc_files: []
119
133
  files:
120
- - .gitignore
121
- - .rspec
122
- - .travis.yml
134
+ - ".gitignore"
135
+ - ".rspec"
136
+ - ".travis.yml"
123
137
  - ChangeLog.md
124
138
  - Gemfile
125
139
  - Guardfile
@@ -172,11 +186,13 @@ files:
172
186
  - spec/integration/cli/provider/provider_aws_spec.rb
173
187
  - spec/integration/cli/provider/provider_openstack_spec.rb
174
188
  - spec/spec_helper.rb
189
+ - spec/support/rspec-fire.rb
175
190
  - spec/support/settings_helper.rb
176
191
  - spec/support/stdout_capture.rb
177
192
  - spec/unit/.gitkeep
178
193
  - spec/unit/cli/image_spec.rb
179
194
  - spec/unit/cli/key_pair_spec.rb
195
+ - spec/unit/providers/clients/openstack_provider_client_spec.rb
180
196
  homepage: https://github.com/drnic/cyoi
181
197
  licenses:
182
198
  - MIT
@@ -187,17 +203,17 @@ require_paths:
187
203
  - lib
188
204
  required_ruby_version: !ruby/object:Gem::Requirement
189
205
  requirements:
190
- - - ! '>='
206
+ - - ">="
191
207
  - !ruby/object:Gem::Version
192
208
  version: '0'
193
209
  required_rubygems_version: !ruby/object:Gem::Requirement
194
210
  requirements:
195
- - - ! '>='
211
+ - - ">="
196
212
  - !ruby/object:Gem::Version
197
213
  version: '0'
198
214
  requirements: []
199
215
  rubyforge_project:
200
- rubygems_version: 2.1.2
216
+ rubygems_version: 2.2.1
201
217
  signing_key:
202
218
  specification_version: 4
203
219
  summary: A library to ask an end-user to choose an infrastructure (AWS, OpenStack,
@@ -217,8 +233,10 @@ test_files:
217
233
  - spec/integration/cli/provider/provider_aws_spec.rb
218
234
  - spec/integration/cli/provider/provider_openstack_spec.rb
219
235
  - spec/spec_helper.rb
236
+ - spec/support/rspec-fire.rb
220
237
  - spec/support/settings_helper.rb
221
238
  - spec/support/stdout_capture.rb
222
239
  - spec/unit/.gitkeep
223
240
  - spec/unit/cli/image_spec.rb
224
241
  - spec/unit/cli/key_pair_spec.rb
242
+ - spec/unit/providers/clients/openstack_provider_client_spec.rb