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

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
}
}