Class: VagrantPlugins::VCloud::Action::HandleNATPortCollisions

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant-vcloud/action/handle_nat_port_collisions.rb

Overview

This middleware class will detect and handle collisions with forwarded ports, whether that means raising an error or repairing them automatically.

Parameters it takes from the environment hash:

* `:port_collision_repair` - If true, it will attempt to repair
  port collisions. If false, it will raise an exception when
  there is a collision.

* `:port_collision_extra_in_use` - An array of ports that are
  considered in use.

* `:port_collision_remap` - A hash remapping certain host ports
  to other host ports.

Instance Method Summary collapse

Constructor Details

#initialize(app, env) ⇒ HandleNATPortCollisions

Returns a new instance of HandleNATPortCollisions.



23
24
25
26
27
28
# File 'lib/vagrant-vcloud/action/handle_nat_port_collisions.rb', line 23

def initialize(app, env)
  @app    = app
  @logger = Log4r::Logger.new(
    'vagrant_vcloud::action::handle_port_collisions'
  )
end

Instance Method Details

#call(env) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/vagrant-vcloud/action/handle_nat_port_collisions.rb', line 30

def call(env)
  @logger.info('Detecting any forwarded port collisions...')

  # Determine a list of usable ports for repair
  usable_ports = Set.new(env[:machine].config.vm.usable_port_range)

  # Pass one, remove all defined host ports from usable ports
  with_forwarded_ports(env) do |options|
    usable_ports.delete(options[:host])
  end

  cfg = env[:machine].provider_config
  cnx = cfg.vcloud_cnx.driver
  vapp_id = env[:machine].get_vapp_id

  @logger.debug('Getting port forwarding rules...')
  rules = cnx.get_vapp_port_forwarding_external_ports(vapp_id)

  # Pass two, detect/handle any collisions
  with_forwarded_ports(env) do |options|
    guest_port = options[:guest]
    host_port  = options[:host]

    # If the port is open (listening for TCP connections)
    if rules.include?(host_port)
      if !options[:auto_correct]
        raise Errors::ForwardPortCollision,
          :guest_port => guest_port.to_s,
          :host_port  => host_port.to_s
      end

      @logger.info("Attempting to repair FP collision: #{host_port}")

      repaired_port = nil
      while !usable_ports.empty?
        # Attempt to repair the forwarded port
        repaired_port = usable_ports.to_a.sort[0]
        usable_ports.delete(repaired_port)

        # If the port is in use, then we can't use this either...
        if rules.include?(repaired_port)
          @logger.info(
            "Repaired port also in use: #{repaired_port}." +
            'Trying another...'
          )
          next
        end

        # We have a port so break out
        break
      end

      # If we have no usable ports then we can't repair
      if !repaired_port && usable_ports.empty?
        raise Errors::ForwardPortAutolistEmpty,
              :vm_name    => env[:machine].name,
              :guest_port => guest_port.to_s,
              :host_port  => host_port.to_s
      end

      # Modify the args in place
      options[:host] = repaired_port

      @logger.info(
        "Repaired FP collision: #{host_port} to #{repaired_port}"
      )

      # Notify the user
      env[:ui].info(
        I18n.t(
          'vagrant.actions.vm.forward_ports.fixed_collision',
          :host_port  => host_port.to_s,
          :guest_port => guest_port.to_s,
          :new_port   => repaired_port.to_s
        )
      )
    end
  end

  @app.call(env)
end