You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
67 lines
2.2 KiB
67 lines
2.2 KiB
import {Injectable, Model, Field, FieldType, TypeTag, Maybe, collect, ErrorWithContext} from '@extollo/lib'
|
|
import * as IPCIDR from 'ip-cidr'
|
|
import {IpAddress, Subnet} from '../types'
|
|
import {Node} from './Node.model'
|
|
|
|
/**
|
|
* IpRange Model
|
|
* -----------------------------------
|
|
* Represents an available IP address pool from which we can allocate node addresses.
|
|
*/
|
|
@Injectable()
|
|
export class IpRange extends Model<IpRange> {
|
|
protected static table = 'p5x_ip_ranges'
|
|
protected static key = 'id'
|
|
|
|
public static async getDefault(): Promise<IpRange> {
|
|
const ipRange = await this.query<IpRange>().first()
|
|
if ( !ipRange ) {
|
|
throw new Error('Unable to find default IP range!')
|
|
}
|
|
return ipRange
|
|
}
|
|
|
|
@Field(FieldType.serial)
|
|
public id?: number
|
|
|
|
@Field(FieldType.varchar)
|
|
public name!: string
|
|
|
|
@Field(FieldType.varchar, 'start_ip')
|
|
public startIp!: IpAddress
|
|
|
|
@Field(FieldType.varchar, 'end_ip')
|
|
public endIp!: IpAddress
|
|
|
|
@Field(FieldType.int)
|
|
public subnet!: Subnet
|
|
|
|
@Field(FieldType.varchar, 'gateway_ip')
|
|
public gatewayIp!: IpAddress
|
|
|
|
/** Get the next IP address in this range that has not already been allocated to a node. */
|
|
public async getNextAvailable(): Promise<Maybe<IpAddress>> {
|
|
// Generate the address pool
|
|
const cidr = new IPCIDR(`${this.gatewayIp}/${this.subnet}`)
|
|
const possibleIps = collect(cidr.toArray({ from: this.startIp, to: this.endIp }) as IpAddress[])
|
|
.filterOut(x => x.endsWith('.0'))
|
|
|
|
// Look up already-allocated addresses
|
|
const allocatedIps = await Node.query<Node>().get().pluck('assignedIp')
|
|
|
|
// Find the first available address
|
|
return possibleIps.firstWhere(ip => !allocatedIps.includes(ip))
|
|
}
|
|
|
|
/** Like `getNextAvailable(...)`, but it will throw if the range is exhausted */
|
|
public async getNextAvailableOrFail(): Promise<IpAddress> {
|
|
const ip = await this.getNextAvailable()
|
|
if ( !ip ) {
|
|
throw new ErrorWithContext('Unable to get next available IP from range: is range exhausted?', {
|
|
range: this.toObject(),
|
|
})
|
|
}
|
|
return ip
|
|
}
|
|
}
|