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.
- checksums.yaml +7 -0
- data/LICENSE +736 -0
- data/README.md +51 -0
- data/bundler.d/efficient_ip.rb +1 -0
- data/config/dhcp_efficient_ip.yml.example +5 -0
- data/config/docker_smart-proxy_settings/settings.d/dhcp.yml +3 -0
- data/config/docker_smart-proxy_settings/settings.d/dhcp_efficient_ip.yml.example +5 -0
- data/config/docker_smart-proxy_settings/settings.yml +4 -0
- data/lib/smart_proxy_efficient_ip.rb +8 -0
- data/lib/smart_proxy_efficient_ip/api.rb +102 -0
- data/lib/smart_proxy_efficient_ip/configuration.rb +36 -0
- data/lib/smart_proxy_efficient_ip/const.rb +41 -0
- data/lib/smart_proxy_efficient_ip/main.rb +132 -0
- data/lib/smart_proxy_efficient_ip/plugin.rb +28 -0
- data/lib/smart_proxy_efficient_ip/version.rb +7 -0
- data/test/api_test.rb +179 -0
- data/test/configuration_test.rb +38 -0
- data/test/provider_test.rb +142 -0
- data/test/test_helper.rb +3 -0
- metadata +121 -0
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,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
|