smart_proxy_efficient_ip 0.0.12

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.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Smart Proxy EfficientIP (plugin)
2
+
3
+ ## Requirements
4
+ - `Ruby >= 2.5`
5
+ - `Smart Proxy >= 2.3`
6
+
7
+ ## Docker
8
+
9
+ 1. Copy example of settings
10
+ ```bash
11
+ cp config/docker_smart-proxy_settings/settings.d/dhcp_efficient_ip.yml.example config/docker_smart-proxy_settings/settings.d/dhcp_efficient_ip.yml
12
+ ```
13
+ 2. Fill in 3 necessary settings in `dhcp_efficient_ip.yml`:
14
+ - username
15
+ - password
16
+ - server_ip
17
+
18
+ 3. Build and run container:
19
+
20
+ ```bash
21
+ $ docker build -t smart_proxy_efficient_ip:latest .
22
+ $ docker run --rm --name smart_proxy_efficient_ip -it -p 4567:4567 smart_proxy_efficient_ip:latest
23
+ ```
24
+
25
+ 4. Enter to the container (optionally if needed)
26
+ ```bash
27
+ $ docker exec -it smart_proxy_efficient_ip bash
28
+ ```
29
+
30
+ ## Testing
31
+
32
+ To run all tests:
33
+ `bundle exec rake test`
34
+
35
+ ## Postman
36
+
37
+ ### Import endpoints
38
+ File > Import (Ctrl + O)
39
+
40
+ file: `postman/smart_proxy.postman_collection.json`
41
+
42
+ ### Automated tests
43
+
44
+ 1. Install `newman`
45
+ ```bash
46
+ npm install -g newman
47
+ ```
48
+ 2. Run tests
49
+ ```bash
50
+ newname run postman/smart_proxy.postman_collection.json
51
+ ```
@@ -0,0 +1 @@
1
+ gem 'smart_proxy_efficient_ip'
@@ -0,0 +1,5 @@
1
+ ---
2
+ :enabled: true
3
+ :username: 'ipmadmin'
4
+ :password: 'admin'
5
+ :server_id: '3.65.1.16'
@@ -0,0 +1,3 @@
1
+ ---
2
+ :enabled: http
3
+ :use_provider: dhcp_efficient_ip
@@ -0,0 +1,5 @@
1
+ ---
2
+ :enabled: true
3
+ :username: ''
4
+ :password: ''
5
+ :server_ip: ''
@@ -0,0 +1,4 @@
1
+ ---
2
+ :trusted_hosts: ['*']
3
+ :bind_host: ['*']
4
+ :http_port: 4567
@@ -0,0 +1,8 @@
1
+ module Proxy
2
+ module DHCP
3
+ module EfficientIp
4
+ end
5
+ end
6
+ end
7
+
8
+ require 'smart_proxy_efficient_ip/plugin'
@@ -0,0 +1,102 @@
1
+ module Proxy
2
+ module DHCP
3
+ module EfficientIp
4
+ class Api
5
+ attr_reader :connection
6
+
7
+ def initialize(connection)
8
+ @connection = connection
9
+ end
10
+
11
+ def find_subnet(network_address)
12
+ result = connection.ip_subnet_list(
13
+ where: "start_hostaddr='#{network_address}' and is_terminal='1'",
14
+ limit: 1
15
+ )
16
+ parse(result.body)&.first
17
+ end
18
+
19
+ def subnets
20
+ result = connection.ip_subnet_list(
21
+ where: "is_terminal='1' and start_hostaddr!='0.0.0.0'"
22
+ )
23
+ parse(result.body)
24
+ end
25
+
26
+ def find_free(network_address, start_ip, end_ip)
27
+ subnet = find_subnet(network_address)
28
+
29
+ result = connection.ip_address_find_free(
30
+ subnet_id: subnet['subnet_id'],
31
+ begin_addr: start_ip,
32
+ end_addr: end_ip,
33
+ max_find: 1
34
+ )
35
+ parse(result.body)&.first
36
+ end
37
+
38
+ def find_record(ip_or_mac)
39
+ result = connection.ip_address_list(
40
+ where: "type='ip' and (hostaddr='#{ip_or_mac}' or mac_addr='#{ip_or_mac}')",
41
+ limit: 1
42
+ )
43
+ parse(result.body)&.first
44
+ end
45
+
46
+ def find_records(ip_or_mac)
47
+ result = connection.ip_address_list(
48
+ where: "type='ip' and (hostaddr='#{ip_or_mac}' or mac_addr='#{ip_or_mac}')",
49
+ )
50
+ parse(result.body)
51
+ end
52
+
53
+ def hosts(network_address)
54
+ subnet = find_subnet(network_address)
55
+ result = connection.ip_address_list(
56
+ where: "subnet_id=#{subnet['subnet_id']} and dhcphost_id > 0"
57
+ )
58
+ parse(result.body)
59
+ end
60
+
61
+ def leases(network_address)
62
+ subnet = find_subnet(network_address)
63
+ lease_ids = parse(connection.ip_address_list(
64
+ where: "subnet_id=#{subnet['subnet_id']} and dhcplease_id > 0"
65
+ ).body).map { |r| r['dhcplease_id'] }
66
+
67
+ result = connection.dhcp_lease_list(
68
+ where: "dhcplease_id IN (#{lease_ids})"
69
+ )
70
+
71
+ parse(result.body)
72
+ end
73
+
74
+ def add_record(params)
75
+ subnet = find_subnet(params['network'])
76
+
77
+ connection.ip_address_add(
78
+ site_name: subnet['site_name'],
79
+ ip_addr: params['ip'],
80
+ mac_addr: params['mac'],
81
+ name: params['name']
82
+ )
83
+ end
84
+
85
+ def delete_record(record)
86
+ subnet = find_subnet(record.subnet.network)
87
+
88
+ connection.ip_address_delete(
89
+ hostaddr: record.ip,
90
+ site_name: subnet['site_name'],
91
+ )
92
+ end
93
+
94
+ private
95
+
96
+ def parse(response)
97
+ response.empty? ? nil : JSON.parse(response)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,36 @@
1
+ module Proxy
2
+ module DHCP
3
+ module EfficientIp
4
+ class Configuration
5
+ def load_classes
6
+ require 'SOLIDserver'
7
+ require 'smart_proxy_efficient_ip/api'
8
+ require 'smart_proxy_efficient_ip/main'
9
+ end
10
+
11
+ def load_dependency_injection_wirings(container_instance, settings)
12
+ container_instance.dependency :connection, (lambda do
13
+ ::SOLIDserver::SOLIDserver.new(
14
+ settings[:server_ip],
15
+ settings[:username],
16
+ settings[:password]
17
+ )
18
+ end)
19
+
20
+ container_instance.dependency :api, (lambda do
21
+ ::Proxy::DHCP::EfficientIp::Api.new(
22
+ container_instance.get_dependency(:connection)
23
+ )
24
+ end)
25
+
26
+ container_instance.dependency :dhcp_provider, (lambda do
27
+ ::Proxy::DHCP::EfficientIp::Provider.new(
28
+ container_instance.get_dependency(:api),
29
+ settings[:subnets]
30
+ )
31
+ end)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,41 @@
1
+ module Proxy
2
+ module DHCP
3
+ module EfficientIp
4
+ SIZE_TO_MASK = {
5
+ 1 => '255.255.255.255',
6
+ 2 => '255.255.255.254',
7
+ 4 => '255.255.255.252',
8
+ 8 => '255.255.255.248',
9
+ 16 => '55.255.255.240',
10
+ 32 => '55.255.255.224',
11
+ 64 => '55.255.255.192',
12
+ 128 => '255.255.255.128',
13
+ 256 => '255.255.255.0',
14
+ 512 => '255.255.254.0',
15
+ 1024 => '55.255.252.0',
16
+ 2048 => '55.255.248.0',
17
+ 4096 => '55.255.240.0',
18
+ 8192 => '55.255.224.0',
19
+ 16384 => '255.255.192.0',
20
+ 32768 => '255.255.128.0',
21
+ 65536 => '255.255.0.0',
22
+ 131072 => '55.254.0.0',
23
+ 262144 => '55.252.0.0',
24
+ 524288 => '55.248.0.0',
25
+ 1048576 => '255.240.0.0',
26
+ 2097152 => '255.224.0.0',
27
+ 4194304 => '255.192.0.0',
28
+ 8388608 => '255.128.0.0',
29
+ 16777216 => '55.0.0.0',
30
+ 33554432 => '54.0.0.0',
31
+ 67108864 => '52.0.0.0',
32
+ 134217728 => '248.0.0.0',
33
+ 268435456 => '240.0.0.0',
34
+ 536870912 => '224.0.0.0',
35
+ 1073741824 => '92.0.0.0',
36
+ 2147483648 => '28.0.0.0',
37
+ 4294967296 => '0.0.0.0'
38
+ }.freeze
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,132 @@
1
+ require 'dhcp_common/server'
2
+ require 'smart_proxy_efficient_ip/const'
3
+ require 'smart_proxy_efficient_ip/api'
4
+
5
+ module Proxy
6
+ module DHCP
7
+ module EfficientIp
8
+ class Provider < ::Proxy::DHCP::Server
9
+ alias_method :find_record_by_mac, :find_record
10
+ alias_method :find_record_by_ip, :find_record
11
+
12
+ def initialize(api, managed_subnets)
13
+ @managed_subnets = managed_subnets
14
+ @api = api
15
+ super('efficient_ip', managed_subnets, nil)
16
+ end
17
+
18
+ def find_subnet(network_address)
19
+ logger.debug("Finding subnet #{network_address}")
20
+ subnet = api.find_subnet(network_address)
21
+ return nil unless subnet
22
+
23
+ netmask = SIZE_TO_MASK[subnet['subnet_size'].to_i]
24
+ ::Proxy::DHCP::Subnet.new(network_address, netmask)
25
+ end
26
+
27
+ def get_subnet(network_address)
28
+ find_subnet(network_address) ||
29
+ raise(Proxy::DHCP::SubnetNotFound.new("No such subnet: %s" % [network_address]))
30
+ end
31
+
32
+ def subnets
33
+ result = api.subnets
34
+
35
+ result.map do |subnet|
36
+ address = subnet['start_hostaddr']
37
+ subnet_size = subnet['subnet_size'].to_i
38
+ netmask = SIZE_TO_MASK[subnet_size]
39
+
40
+ if subnet_size >= 1 && managed_subnet?("#{address}/#{netmask}")
41
+ Proxy::DHCP::Subnet.new(address, netmask)
42
+ end
43
+ end.compact
44
+ end
45
+
46
+ def all_hosts(network_address)
47
+ logger.debug("Fetching hosts for #{network_address}")
48
+ hosts = api.hosts(network_address)
49
+ return [] unless hosts
50
+
51
+ subnet = find_subnet(network_address)
52
+ hosts.map do |host|
53
+ Proxy::DHCP::Reservation.new(
54
+ host['name'], host['hostaddr'], host['mac_addr'], subnet
55
+ )
56
+ end
57
+ end
58
+
59
+ def all_leases(network_address)
60
+ logger.debug("Fetching leases for #{network_address}")
61
+ leases = api.leases(network_address)
62
+ return [] unless leases
63
+
64
+ subnet = find_subnet(network_address)
65
+ leases.map do |lease|
66
+ Proxy::DHCP::Lease.new(
67
+ lease['dhcplease_name'],
68
+ lease['dhcplease_addr'],
69
+ lease['dhcplease_mac_addr'].split(':')[1..6].join(':'),
70
+ subnet,
71
+ DateTime.strptime(lease['dhcplease_first_time'], '%s'),
72
+ DateTime.strptime(lease['dhcplease_end_time'], '%s'),
73
+ lease['time_to_expire'].to_i > 0 ? 'active' : 'free'
74
+ )
75
+ end
76
+ end
77
+
78
+ def unused_ip(network_address, _, from_ip_address, to_ip_address)
79
+ logger.debug("Searching first unused ip from:#{from_ip_address} to:#{to_ip_address}")
80
+
81
+ free_ip = api.find_free(network_address, from_ip_address, to_ip_address)
82
+ free_ip['hostaddr'] if free_ip
83
+ end
84
+
85
+ def find_record(subnet_address, ip_or_mac_address)
86
+ logger.debug("Finding record for subnet:#{subnet_address} and address:#{ip_or_mac_address}")
87
+
88
+ subnet = find_subnet(subnet_address)
89
+ record = api.find_record(ip_or_mac_address)
90
+
91
+ record ? build_reservation(subnet, record) : nil
92
+ end
93
+
94
+ def find_records_by_ip(subnet_address, ip_or_mac)
95
+ logger.debug("Finding records by address: #{ip_or_mac}")
96
+
97
+ records = api.find_records(ip_or_mac)
98
+ return [] if records.empty?
99
+ subnet = find_subnet(subnet_address)
100
+
101
+ records.map do |record|
102
+ reserv = build_reservation(subnet, record)
103
+ reserv unless reserv.nil?
104
+ end.compact
105
+ end
106
+
107
+ def add_record(params)
108
+ logger.debug("Adding record with: #{params.to_s}")
109
+ api.add_record(params)
110
+ end
111
+
112
+ def del_record(record)
113
+ logger.debug("Deleting record: #{record.to_s}")
114
+ api.delete_record(record)
115
+ end
116
+
117
+ private
118
+
119
+ attr_reader :api, :managed_subnets
120
+
121
+ def build_reservation(subnet, record)
122
+ return nil if record.empty? || record['hostaddr'].empty? || record['mac_addr'].empty?
123
+
124
+ opts = { hostname: record['name'] }
125
+ Proxy::DHCP::Reservation.new(
126
+ record['name'], record['hostaddr'], record['mac_addr'], subnet, opts
127
+ )
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,28 @@
1
+ require 'smart_proxy_efficient_ip/version'
2
+ require 'smart_proxy_efficient_ip/configuration'
3
+
4
+ module Proxy
5
+ module DHCP
6
+ module EfficientIp
7
+ class Plugin < ::Proxy::Provider
8
+ plugin :dhcp_efficient_ip, ::Proxy::DHCP::EfficientIp::VERSION
9
+
10
+ validate_presence :username, :password, :server_ip
11
+
12
+ # Settings listed under default_settings are required.
13
+ # An exception will be raised if they are initialized with nil values.
14
+ # Settings not listed under default_settings are considered optional and by default have nil value.
15
+ # default_settings :required_setting => 'default_value'
16
+
17
+ requires :dhcp, '>= 2.3'
18
+
19
+ # Verifies that a file exists and is readable.
20
+ # Uninitialized optional settings will not trigger validation errors.
21
+ # validate_readable :required_path, :optional_path
22
+
23
+ load_classes ::Proxy::DHCP::EfficientIp::Configuration
24
+ load_dependency_injection_wirings ::Proxy::DHCP::EfficientIp::Configuration
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module Proxy
2
+ module DHCP
3
+ module EfficientIp
4
+ VERSION = '0.0.12'.freeze
5
+ end
6
+ end
7
+ end