From 4d63cad3658d08b5336552802f64c45e5346b4ed Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Tue, 26 Oct 2021 21:28:36 +0900 Subject: [PATCH] Use openstack cli to manage neutron ports Partial-Bug: #1808317 Change-Id: I919ea80bcf485b9f45884725ef6406b439bb66df --- lib/puppet/provider/neutron_port/neutron.rb | 211 ----------- lib/puppet/provider/neutron_port/openstack.rb | 234 ++++++++++++ .../provider/neutron_port/neutron_spec.rb | 110 ------ .../provider/neutron_port/openstack_spec.rb | 334 ++++++++++++++++++ 4 files changed, 568 insertions(+), 321 deletions(-) delete mode 100644 lib/puppet/provider/neutron_port/neutron.rb create mode 100644 lib/puppet/provider/neutron_port/openstack.rb delete mode 100644 spec/unit/provider/neutron_port/neutron_spec.rb create mode 100644 spec/unit/provider/neutron_port/openstack_spec.rb diff --git a/lib/puppet/provider/neutron_port/neutron.rb b/lib/puppet/provider/neutron_port/neutron.rb deleted file mode 100644 index 383cd19c0..000000000 --- a/lib/puppet/provider/neutron_port/neutron.rb +++ /dev/null @@ -1,211 +0,0 @@ -require File.join(File.dirname(__FILE__), "..","..","..", - "puppet/provider/neutron") - -Puppet::Type.type(:neutron_port).provide( - :neutron, - :parent => Puppet::Provider::Neutron -) do - desc <<-EOT - Neutron provider to manage neutron_port type. - - Assumes that the neutron service is configured on the same host. - EOT - #TODO No security group support - - mk_resource_methods - - def self.instances - list_neutron_resources("port").collect do |id| - attrs = get_neutron_resource_attrs("port", id) - attrs["name"] = attrs["id"] if attrs["name"].empty? - new( - :ensure => :present, - :name => attrs["name"], - :id => attrs["id"], - :status => attrs["status"], - :tenant_id => attrs["tenant_id"], - :network_id => attrs["network_id"], - :admin_state_up => attrs["admin_state_up"], - :network_name => get_network_name(attrs["network_id"]), - :subnet_name => get_subnet_name(parse_subnet_id(attrs["fixed_ips"])), - :subnet_id => parse_subnet_id(attrs["fixed_ips"]), - :ip_address => parse_ip_address(attrs["fixed_ips"]), - :binding_profile => parse_binding_profile_interface_name(attrs["binding:profile"]), - :binding_host_id => attrs["binding:host_id"], - ) - end - end - - def self.prefetch(resources) - instances_ = instances - resources.keys.each do |name| - if provider = instances_.find{ |instance| instance.name == name } - resources[name].provider = provider - end - end - end - - def exists? - @property_hash[:ensure] == :present - end - - def create - opts = Array.new - dict_opts = Array.new - - if @resource[:admin_state_up] == "False" - opts << "--admin-state-down" - end - - if @resource[:ip_address] - # The spec says that multiple ip addresses may be specified, but this - # doesn't seem to work yet. - opts << "--fixed-ip" - opts << resource[:ip_address].map{|ip|"ip_address=#{ip}"}.join(',') - end - - if @resource[:subnet_name] - # The spec says that multiple subnets may be specified, but this doesn't - # seem to work yet. - opts << "--fixed-ip" - opts << resource[:subnet_name].map{|s|"subnet_id=#{s}"}.join(',') - end - - if @resource[:tenant_name] - tenant_id = self.class.get_tenant_id( - @resource.catalog, - @resource[:tenant_name] - ) - opts << "--tenant_id=#{tenant_id}" - elsif @resource[:tenant_id] - opts << "--tenant_id=#{@resource[:tenant_id]}" - end - - if @resource[:binding_host_id] - opts << "--binding:host_id=#{@resource[:binding_host_id]}" - end - - if @resource[:binding_profile] - binding_profile_opts = @resource[:binding_profile].map{|k,v| "#{k}=#{v}"}.join(' ') - dict_opts << "--binding:profile" - dict_opts << "type=dict" - dict_opts << "#{binding_profile_opts}" - end - - results = auth_neutron( - "port-create", - "--format=shell", - "--name=#{resource[:name]}", - opts, - resource[:network_name], - dict_opts - ) - - attrs = self.class.parse_creation_output(results) - @property_hash = { - :ensure => :present, - :name => resource[:name], - :id => attrs["id"], - :status => attrs["status"], - :tenant_id => attrs["tenant_id"], - :network_id => attrs["network_id"], - :admin_state_up => attrs["admin_state_up"], - :network_name => resource[:network_name], - :subnet_name => resource[:subnet_name], - :subnet_id => self.class.parse_subnet_id(attrs["fixed_ips"]), - :ip_address => self.class.parse_ip_address(attrs["fixed_ips"]), - :binding_profile => self.class.parse_binding_profile_interface_name(attrs["binding:profile"]), - :binding_host_id => attrs["binding:host_id"], - } - end - - def destroy - auth_neutron("port-delete", name) - @property_hash[:ensure] = :absent - end - - def admin_state_up=(value) - auth_neutron("port-update", "--admin-state-up=#{value}", name) - end - - private - - def self.get_network_name(network_id_) - if network_id_ - network_instances = Puppet::Type.type("neutron_network").instances - network_name = network_instances.find do |instance| - instance.provider.id == network_id_ - end.provider.name - end - network_name - end - - def get_network_name(network_id_) - @get_network_name ||= self.class.get_network_name(network_id_) - end - - def self.get_subnet_name(subnet_id_) - if subnet_id_ - subnet_ids = Array(subnet_id_) - subnet_instances = Puppet::Type.type("neutron_subnet").instances - subnet_names = subnet_instances.collect do |instance| - if subnet_ids.include?(instance.provider.id) - instance.provider.name - else - nil - end - end.compact - if subnet_names.length > 1 - subnet_names - else - subnet_names.first - end - end - end - - def get_subnet_name(subnet_id_) - @subnet_name ||= self.class.subnet_name(subnet_id_) - end - - def self.parse_subnet_id(fixed_ips_) - subnet_ids = Array(fixed_ips_).collect do |json| - match_data = /\{"subnet_id": "(.*)", /.match(json) - if match_data - match_data[1] - else - nil - end - end.compact - if subnet_ids.length > 1 - subnet_ids - else - subnet_ids.first - end - end - - def self.parse_ip_address(fixed_ips_) - ip_addresses = Array(fixed_ips_).collect do |json| - match_data = /"ip_address": "(.*)"\}/.match(json) - if match_data - match_data[1] - else - nil - end - end.compact - if ip_addresses.length > 1 - ip_addresses - else - ip_addresses.first - end - end - - def self.parse_binding_profile_interface_name(binding_profile_) - match_data = /\{"interface_name": "(.*)"\}/.match(binding_profile_) - if match_data - match_data[1] - else - nil - end - end - -end diff --git a/lib/puppet/provider/neutron_port/openstack.rb b/lib/puppet/provider/neutron_port/openstack.rb new file mode 100644 index 000000000..2d9b780a5 --- /dev/null +++ b/lib/puppet/provider/neutron_port/openstack.rb @@ -0,0 +1,234 @@ +require File.join(File.dirname(__FILE__), '..','..','..', + 'puppet/provider/neutron') + +Puppet::Type.type(:neutron_port).provide( + :openstack, + :parent => Puppet::Provider::Neutron +) do + desc <<-EOT + Neutron provider to manage neutron_port type. + + Assumes that the neutron service is configured on the same host. + EOT + + @credentials = Puppet::Provider::Openstack::CredentialsV3.new + + mk_resource_methods + + def initialize(value={}) + super(value) + @property_flush = {} + end + + def self.do_not_manage + @do_not_manage + end + + def self.do_not_manage=(value) + @do_not_manage = value + end + + def self.instances + self.do_not_manage = true + list = request('port', 'list').collect do |attrs| + port = request('port', 'show', attrs[:id]) + port[:name] = port[:id] if port[:name].empty? + new( + :ensure => :present, + :name => port[:name], + :id => port[:id], + :status => port[:status], + :tenant_id => port[:project_id], + :network_id => port[:network_id], + :network_name => get_network_name(port[:network_id]), + :admin_state_up => port[:admin_state_up], + :subnet_id => parse_subnet_id(port[:fixed_ips]), + :subnet_name => get_subnet_name(parse_subnet_id(port[:fixed_ips])), + :ip_address => parse_ip_address(port[:fixed_ips]), + :binding_profile => parse_binding_profile_interface_name(port[:binding_profile]), + :binding_host_id => port[:binding_host_id], + ) + end + self.do_not_manage = false + list + end + + def self.prefetch(resources) + ports = instances + resources.keys.each do |name| + if provider = ports.find{ |net| net.name == name } + resources[name].provider = provider + end + end + end + + def exists? + @property_hash[:ensure] == :present + end + + def create + if self.class.do_not_manage + fail("Not managing Neutron_port[#{@resource[:name]}] due to earlier Neutron API failures.") + end + + opts = [@resource[:name]] + + if @resource[:network_name] + opts << "--network=#{@resource[:network_name]}" + elsif @resource[:network_id] + opts << "--network=#{@resource[:network_id]}" + end + + if @resource[:admin_state_up] == 'False' + opts << '--disable' + end + + if @resource[:ip_address] + Array(resource[:ip_address]).each do |ip| + opts << "--fixed-ip ip_address=#{ip}" + end + end + + if @resource[:subnet_name] + Array(resource[:subnet_name]).each do |subnet| + opts << "--fixed-ip subnet=#{subnet}" + end + end + + if @resource[:tenant_name] + opts << "--project=#{@resource[:tenant_name]}" + elsif @resource[:tenant_id] + opts << "--project=#{@resource[:tenant_id]}" + end + + if @resource[:binding_host_id] + opts << "--host=#{@resource[:binding_host_id]}" + end + + if @resource[:binding_profile] + @resource[:binding_profile].each do |k,v| + opts << "--binding-profile #{k}=#{v}" + end + end + + port = self.class.request('port', 'create', opts) + @property_hash = { + :ensure => :present, + :name => port[:name], + :id => port[:id], + :status => port[:status], + :tenant_id => port[:project_id], + :network_id => port[:network_id], + :network_name => self.class.get_network_name(port[:network_id]), + :admin_state_up => port[:admin_state_up], + :subnet_id => self.class.parse_subnet_id(port[:fixed_ips]), + :subnet_name => self.class.get_subnet_name(self.class.parse_subnet_id(port[:fixed_ips])), + :ip_address => self.class.parse_ip_address(port[:fixed_ips]), + :binding_profile => self.class.parse_binding_profile_interface_name(port[:binding_profile]), + :binding_host_id => port[:binding_host_id], + } + end + + def flush + if !@property_flush.empty? + opts = [@resource[:name]] + + if @property_flush.has_key?(:admin_state_up) + if @property_flush[:admin_state_up] == 'False' + opts << '--disable' + else + opts << '--enable' + end + end + + if @property_flush.has_key?(:shared) + if @property_flush[:shared] == 'False' + opts << '--no-share' + else + opts << '--share' + end + end + + if @property_flush.has_key?(:router_external) + if @property_flush[:router_external] == 'False' + opts << '--internal' + else + opts << '--external' + end + end + + if @property_flush.has_key?(:availability_zone_hint) + opts << "--avialability-zone-hint=#{@property_flush[:availability_zone_hint]}" + end + + self.class.request('port', 'set', opts) + @property_flush.clear + end + end + + def destroy + if self.class.do_not_manage + fail("Not managing Neutron_port[#{@resource[:name]}] due to earlier Neutron API failures.") + end + self.class.request('port', 'delete', @resource[:name]) + @property_hash.clear + @property_hash[:ensure] = :absent + end + + def self.parse_subnet_id(value) + fixed_ips = JSON.parse(value.gsub(/\\"/,'"').gsub('u\'', '"').gsub('\'','"')) + subnet_ids = [] + fixed_ips.each do |fixed_ip| + subnet_ids << fixed_ip['subnet_id'] + end + + if subnet_ids.length > 1 + subnet_ids + else + subnet_ids.first + end + end + + def self.parse_ip_address(value) + fixed_ips = JSON.parse(value.gsub(/\\"/,'"').gsub('u\'', '"').gsub('\'','"')) + ips = [] + fixed_ips.each do |fixed_ip| + ips << fixed_ip['ip_address'] + end + + if ips.length > 1 + ips + else + ips.first + end + end + + def self.parse_binding_profile_interface_name(value) + profile = JSON.parse(value.gsub(/\\"/,'"').gsub('u\'', '"').gsub('\'','"')) + profile['interface_name'] + end + + [ + :admin_state_up, + ].each do |attr| + define_method(attr.to_s + "=") do |value| + if self.class.do_not_manage + fail("Not managing Neutron_port[#{@resource[:name]}] due to earlier Neutron API failures.") + end + @property_flush[attr] = value + end + end + + [ + :network_id, + :subnet_id, + :ip_address, + :tenant_id, + :tenant_name, + ].each do |attr| + define_method(attr.to_s + "=") do |value| + fail("Property #{attr.to_s} does not support being updated") + end + end + +end diff --git a/spec/unit/provider/neutron_port/neutron_spec.rb b/spec/unit/provider/neutron_port/neutron_spec.rb deleted file mode 100644 index 7528d3bfa..000000000 --- a/spec/unit/provider/neutron_port/neutron_spec.rb +++ /dev/null @@ -1,110 +0,0 @@ -require 'puppet' -require 'spec_helper' -require 'puppet/provider/neutron_port/neutron' - -provider_class = Puppet::Type.type(:neutron_port).provider(:neutron) - -describe provider_class do - - let :port_name do - 'port1' - end - - let :port_attrs do - { - :name => port_name, - :ensure => 'present', - :admin_state_up => 'True', - :tenant_id => '60f9544eb94c42a6b7e8e98c2be981b1', - :network_name => 'net1', - } - end - - let :resource do - Puppet::Type::Neutron_port.new(port_attrs) - end - - let :provider do - provider_class.new(resource) - end - - describe 'when creating a port' do - - it 'should call port-create with appropriate command line options' do - provider.class.stubs(:get_tenant_id).returns(port_attrs[:tenant_id]) - - output = 'Created a new port: -admin_state_up="True" -device_id="" -device_owner="" -fixed_ips="{\"subnet_id\": \"40af01ac-52c7-4235-bbcf-d9c02325ab5e\", \"ip_address\": \"192.168.0.39\"}" -id="5222573b-314d-45f9-b6bd-299288ba667a" -mac_address="fa:16:3e:45:3c:10" -name="port1" -network_id="98873773-aa34-4b87-af05-70903659246f" -security_groups="f1f0c3a3-9f2c-46b9-b2a5-b97d9a87bd7e" -status="ACTIVE" -tenant_id="60f9544eb94c42a6b7e8e98c2be981b1"' - - provider.expects(:auth_neutron).with('port-create', - '--format=shell', "--name=#{port_attrs[:name]}", - ["--tenant_id=#{port_attrs[:tenant_id]}"], - port_attrs[:network_name], - []).returns(output) - - provider.create - end - end - - describe 'when creating a port with binding profile and host_id' do - - let :port_attrs do - { - :name => port_name, - :ensure => 'present', - :admin_state_up => 'True', - :tenant_id => '60f9544eb94c42a6b7e8e98c2be981b1', - :network_name => 'net1', - :binding_host_id => 'edge1', - :binding_profile => { 'interface_name' => 'eth1' }, - } - end - - let :resource do - Puppet::Type::Neutron_port.new(port_attrs) - end - - it 'should call port-create with appropriate command line options' do - - provider.class.stubs(:get_tenant_id).returns(port_attrs[:tenant_id]) - provider.class.stubs(:get_binding_host_id).returns(port_attrs[:binding_host_id]) - provider.class.stubs(:get_binding_profile).returns(port_attrs[:binding_profile]) - - output = 'Created a new port: -admin_state_up="True" -device_id="" -device_owner="" -fixed_ips="{\"subnet_id\": \"40af01ac-52c7-4235-bbcf-d9c02325ab5e\", \"ip_address\": \"192.168.0.39\"}" -id="5222573b-314d-45f9-b6bd-299288ba667a" -mac_address="fa:16:3e:45:3c:10" -name="port1" -network_id="98873773-aa34-4b87-af05-70903659246f" -security_groups="f1f0c3a3-9f2c-46b9-b2a5-b97d9a87bd7e" -status="ACTIVE" -tenant_id="60f9544eb94c42a6b7e8e98c2be981b1" -binding_host_id="edge1" -binding_profile="{\"interface_name\": \"eth1\"}"' - - provider.expects(:auth_neutron).with('port-create', - '--format=shell', "--name=#{port_attrs[:name]}", - ["--tenant_id=#{port_attrs[:tenant_id]}", - "--binding:host_id=#{port_attrs[:binding_host_id]}", - ], - port_attrs[:network_name], - ['--binding:profile','type=dict','interface_name=eth1']).returns(output) - - provider.create - end - end - -end diff --git a/spec/unit/provider/neutron_port/openstack_spec.rb b/spec/unit/provider/neutron_port/openstack_spec.rb new file mode 100644 index 000000000..d3e52b0f1 --- /dev/null +++ b/spec/unit/provider/neutron_port/openstack_spec.rb @@ -0,0 +1,334 @@ +require 'puppet' +require 'spec_helper' +require 'puppet/provider/neutron_port/openstack' + +provider_class = Puppet::Type.type(:neutron_port).provider(:openstack) + +describe provider_class do + + let(:set_env) do + ENV['OS_USERNAME'] = 'test' + ENV['OS_PASSWORD'] = 'abc123' + ENV['OS_PROJECT_NAME'] = 'test' + ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000' + end + + describe 'manage ports' do + let :port_name do + 'port1' + end + + let :port_attrs do + { + :ensure => 'present', + :name => port_name, + :network_name => 'net1', + } + end + + let :resource do + Puppet::Type::Neutron_port.new(port_attrs) + end + + let :provider do + provider_class.new(resource) + end + + before :each do + set_env + end + + describe '#create' do + context 'with defaults' do + it 'creates port' do + provider_class.expects(:openstack) + .with('port', 'create', '--format', 'shell', + ['port1', '--network=net1']) + .returns('admin_state_up="True" +allowed_address_pairs="[]" +binding_host_id="" +binding_profile="{}" +device_id="" +device_owner="" +fixed_ips="[{\'subnet_id\': \'dd5e0ef1-2c88-4b0b-ba08-7df65be87963\', \'ip_address\': \'10.0.0.2\'}]" +id="5222573b-314d-45f9-b6bd-299288ba667a" +mac_address="fa:16:3e:45:3c:10" +name="port1" +network_id="076520cc-b783-4cf5-a4a9-4cb5a5e93a9b" +project_id="60f9544eb94c42a6b7e8e98c2be981b1" +security_groups="f1f0c3a3-9f2c-46b9-b2a5-b97d9a87bd7e" +status="ACTIVE"') + provider_class.expects(:openstack) + .with('network', 'show', '--format', 'shell', + ['076520cc-b783-4cf5-a4a9-4cb5a5e93a9b']) + .returns('id="076520cc-b783-4cf5-a4a9-4cb5a5e93a9b" +name="net1"') + provider_class.expects(:openstack) + .with('subnet', 'show', '--format', 'shell', + ['dd5e0ef1-2c88-4b0b-ba08-7df65be87963']) + .returns('id="dd5e0ef1-2c88-4b0b-ba08-7df65be87963" +name="subnet1"') + provider.create + expect(provider.exists?).to be_truthy + expect(provider.admin_state_up).to eq('True') + expect(provider.ip_address).to eq('10.0.0.2') + expect(provider.network_name).to eq('net1') + expect(provider.subnet_id).to eq('dd5e0ef1-2c88-4b0b-ba08-7df65be87963') + expect(provider.subnet_name).to eq('subnet1') + end + end + + context 'with admin_state_up' do + let :port_attrs do + { + :ensure => 'present', + :name => port_name, + :network_name => 'net1', + :admin_state_up => 'False', + } + end + + it 'creates port' do + provider_class.expects(:openstack) + .with('port', 'create', '--format', 'shell', + ['port1', '--network=net1', '--disable']) + .returns('admin_state_up="False" +allowed_address_pairs="[]" +binding_host_id="" +binding_profile="{}" +device_id="" +device_owner="" +fixed_ips="[{\'subnet_id\': \'dd5e0ef1-2c88-4b0b-ba08-7df65be87963\', \'ip_address\': \'10.0.0.2\'}]" +id="5222573b-314d-45f9-b6bd-299288ba667a" +mac_address="fa:16:3e:45:3c:10" +name="port1" +network_id="076520cc-b783-4cf5-a4a9-4cb5a5e93a9b" +project_id="60f9544eb94c42a6b7e8e98c2be981b1" +security_groups="f1f0c3a3-9f2c-46b9-b2a5-b97d9a87bd7e" +status="ACTIVE"') + provider_class.expects(:openstack) + .with('network', 'show', '--format', 'shell', + ['076520cc-b783-4cf5-a4a9-4cb5a5e93a9b']) + .returns('id="076520cc-b783-4cf5-a4a9-4cb5a5e93a9b" +name="net1"') + provider_class.expects(:openstack) + .with('subnet', 'show', '--format', 'shell', + ['dd5e0ef1-2c88-4b0b-ba08-7df65be87963']) + .returns('id="dd5e0ef1-2c88-4b0b-ba08-7df65be87963" +name="subnet1"') + provider.create + expect(provider.exists?).to be_truthy + expect(provider.admin_state_up).to eq('False') + expect(provider.ip_address).to eq('10.0.0.2') + expect(provider.network_name).to eq('net1') + expect(provider.subnet_id).to eq('dd5e0ef1-2c88-4b0b-ba08-7df65be87963') + expect(provider.subnet_name).to eq('subnet1') + end + end + + context 'with subnet' do + let :port_attrs do + { + :ensure => 'present', + :name => port_name, + :network_name => 'net1', + :subnet_name => 'subnet1', + } + end + + it 'creates port' do + provider_class.expects(:openstack) + .with('port', 'create', '--format', 'shell', + ['port1', '--network=net1', + '--fixed-ip subnet=subnet1']) + .returns('admin_state_up="True" +allowed_address_pairs="[]" +binding_host_id="" +binding_profile="{}" +device_id="" +device_owner="" +fixed_ips="[{\'subnet_id\': \'dd5e0ef1-2c88-4b0b-ba08-7df65be87963\', \'ip_address\': \'10.0.0.2\'}]" +id="5222573b-314d-45f9-b6bd-299288ba667a" +mac_address="fa:16:3e:45:3c:10" +name="port1" +network_id="076520cc-b783-4cf5-a4a9-4cb5a5e93a9b" +project_id="60f9544eb94c42a6b7e8e98c2be981b1" +security_groups="f1f0c3a3-9f2c-46b9-b2a5-b97d9a87bd7e" +status="ACTIVE"') + provider_class.expects(:openstack) + .with('network', 'show', '--format', 'shell', + ['076520cc-b783-4cf5-a4a9-4cb5a5e93a9b']) + .returns('id="076520cc-b783-4cf5-a4a9-4cb5a5e93a9b" +name="net1"') + provider_class.expects(:openstack) + .with('subnet', 'show', '--format', 'shell', + ['dd5e0ef1-2c88-4b0b-ba08-7df65be87963']) + .returns('id="dd5e0ef1-2c88-4b0b-ba08-7df65be87963" +name="subnet1"') + provider.create + expect(provider.exists?).to be_truthy + expect(provider.admin_state_up).to eq('True') + expect(provider.ip_address).to eq('10.0.0.2') + expect(provider.network_name).to eq('net1') + expect(provider.subnet_id).to eq('dd5e0ef1-2c88-4b0b-ba08-7df65be87963') + expect(provider.subnet_name).to eq('subnet1') + end + end + + context 'with binding profile' do + let :port_attrs do + { + :ensure => 'present', + :name => port_name, + :binding_host_id => 'myhost', + :binding_profile => {'key1' => 'val1'}, + :network_name => 'net1', + } + end + + it 'creates port' do + provider_class.expects(:openstack) + .with('port', 'create', '--format', 'shell', + ['port1', '--network=net1', '--host=myhost', + '--binding-profile key1=val1']) + .returns('admin_state_up="True" +allowed_address_pairs="[]" +binding_host_id="myhost" +binding_profile="{\'key1\': \'val1\'}" +device_id="" +device_owner="" +fixed_ips="[{\'subnet_id\': \'dd5e0ef1-2c88-4b0b-ba08-7df65be87963\', \'ip_address\': \'10.0.0.2\'}]" +id="5222573b-314d-45f9-b6bd-299288ba667a" +mac_address="fa:16:3e:45:3c:10" +name="port1" +network_id="076520cc-b783-4cf5-a4a9-4cb5a5e93a9b" +project_id="60f9544eb94c42a6b7e8e98c2be981b1" +security_groups="f1f0c3a3-9f2c-46b9-b2a5-b97d9a87bd7e" +status="ACTIVE"') + provider_class.expects(:openstack) + .with('network', 'show', '--format', 'shell', + ['076520cc-b783-4cf5-a4a9-4cb5a5e93a9b']) + .returns('id="076520cc-b783-4cf5-a4a9-4cb5a5e93a9b" +name="net1"') + provider_class.expects(:openstack) + .with('subnet', 'show', '--format', 'shell', + ['dd5e0ef1-2c88-4b0b-ba08-7df65be87963']) + .returns('id="dd5e0ef1-2c88-4b0b-ba08-7df65be87963" +name="subnet1"') + provider.create + expect(provider.exists?).to be_truthy + expect(provider.ip_address).to eq('10.0.0.2') + expect(provider.network_name).to eq('net1') + expect(provider.subnet_id).to eq('dd5e0ef1-2c88-4b0b-ba08-7df65be87963') + expect(provider.subnet_name).to eq('subnet1') + end + end + + end + + describe '#destroy' do + it 'removes port' do + provider_class.expects(:openstack) + .with('port', 'delete', 'port1') + provider.destroy + expect(provider.exists?).to be_falsey + end + end + + describe '#flush' do + context 'admin_state_up' do + it 'updates port' do + provider_class.expects(:openstack) + .with('port', 'set', ['port1', '--disable']) + provider.admin_state_up = 'False' + provider.flush + + provider_class.expects(:openstack) + .with('port', 'set', ['port1', '--enable']) + provider.admin_state_up = 'True' + provider.flush + end + end + end + + describe '#list' do + it 'lists ports' do + provider_class.expects(:openstack) + .with('port', 'list', '--quiet', '--format', 'csv', []) + .returns('"ID","Name","MAC Address","Fixed IP Addresses","Status" +"5222573b-314d-45f9-b6bd-299288ba667a","port1","fa:16:3e:45:3c:10","[{\'subnet_id\': \'dd5e0ef1-2c88-4b0b-ba08-7df65be87963\', \'ip_address\': \'10.0.0.2\'}]","ACTIVE" +"c880affb-b15e-4632-b5e7-3adba6e3ab35","port2","fa:16:3e:45:3c:11","[{\'subnet_id\': \'0da7a631-0f8f-4e51-8b1c-7a29d0d4f7b5\', \'ip_address\': \'10.0.1.2\'}]","DOWN" +') + + provider_class.expects(:openstack) + .with('port', 'show', '--format', 'shell', '5222573b-314d-45f9-b6bd-299288ba667a') + .returns('admin_state_up="True" +allowed_address_pairs="[]" +binding_host_id="" +binding_profile="{}" +device_id="" +device_owner="" +fixed_ips="[{\'subnet_id\': \'dd5e0ef1-2c88-4b0b-ba08-7df65be87963\', \'ip_address\': \'10.0.0.2\'}]" +id="5222573b-314d-45f9-b6bd-299288ba667a" +mac_address="fa:16:3e:45:3c:10" +name="port1" +network_id="076520cc-b783-4cf5-a4a9-4cb5a5e93a9b" +project_id="60f9544eb94c42a6b7e8e98c2be981b1" +security_groups="f1f0c3a3-9f2c-46b9-b2a5-b97d9a87bd7e" +status="ACTIVE"') + provider_class.expects(:openstack) + .with('network', 'show', '--format', 'shell', ['076520cc-b783-4cf5-a4a9-4cb5a5e93a9b']) + .returns('name="net1"') + provider_class.expects(:openstack) + .with('subnet', 'show', '--format', 'shell', ['dd5e0ef1-2c88-4b0b-ba08-7df65be87963']) + .returns('name="subnet1"') + + provider_class.expects(:openstack) + .with('port', 'show', '--format', 'shell', 'c880affb-b15e-4632-b5e7-3adba6e3ab35') + .returns('admin_state_up="False" +allowed_address_pairs="[]" +binding_host_id="" +binding_profile="{}" +device_id="" +device_owner="" +fixed_ips="[{\'subnet_id\': \'0da7a631-0f8f-4e51-8b1c-7a29d0d4f7b5\', \'ip_address\': \'10.0.1.2\'}]" +id="c880affb-b15e-4632-b5e7-3adba6e3ab35" +mac_address="fa:16:3e:45:3c:11" +name="port2" +network_id="34e8f42b-89db-4a5b-92db-76ca7073414d" +project_id="60f9544eb94c42a6b7e8e98c2be981b1" +security_groups="f1f0c3a3-9f2c-46b9-b2a5-b97d9a87bd7e" +status="DOWN"') + provider_class.expects(:openstack) + .with('network', 'show', '--format', 'shell', ['34e8f42b-89db-4a5b-92db-76ca7073414d']) + .returns('name="net2"') + provider_class.expects(:openstack) + .with('subnet', 'show', '--format', 'shell', ['0da7a631-0f8f-4e51-8b1c-7a29d0d4f7b5']) + .returns('name="subnet2"') + + instances = provider_class.instances + expect(instances.length).to eq(2) + + expect(instances[0].id).to eq('5222573b-314d-45f9-b6bd-299288ba667a') + expect(instances[0].name).to eq('port1') + expect(instances[0].network_id).to eq('076520cc-b783-4cf5-a4a9-4cb5a5e93a9b') + expect(instances[0].network_name).to eq('net1') + expect(instances[0].subnet_id).to eq('dd5e0ef1-2c88-4b0b-ba08-7df65be87963') + expect(instances[0].subnet_name).to eq('subnet1') + expect(instances[0].status).to eq('ACTIVE') + expect(instances[0].admin_state_up).to eq('True') + expect(instances[0].tenant_id).to eq('60f9544eb94c42a6b7e8e98c2be981b1') + + expect(instances[1].id).to eq('c880affb-b15e-4632-b5e7-3adba6e3ab35') + expect(instances[1].name).to eq('port2') + expect(instances[1].network_id).to eq('34e8f42b-89db-4a5b-92db-76ca7073414d') + expect(instances[1].network_name).to eq('net2') + expect(instances[1].subnet_id).to eq('0da7a631-0f8f-4e51-8b1c-7a29d0d4f7b5') + expect(instances[1].subnet_name).to eq('subnet2') + expect(instances[1].status).to eq('DOWN') + expect(instances[1].admin_state_up).to eq('False') + expect(instances[0].tenant_id).to eq('60f9544eb94c42a6b7e8e98c2be981b1') + end + end + end +end