From d6df8d856fa33444942447933dcbaae6731261db Mon Sep 17 00:00:00 2001 From: David Crespo Date: Fri, 5 Jan 2024 13:37:33 -0500 Subject: [PATCH] bump API for IP pools and implement mock API --- OMICRON_VERSION | 2 +- libs/api-mocks/index.ts | 1 + libs/api-mocks/ip-pool.ts | 38 ++++ libs/api-mocks/msw/db.ts | 24 +++ libs/api-mocks/msw/handlers.ts | 66 +++++- libs/api/__generated__/Api.ts | 278 ++++++++++++++++++++----- libs/api/__generated__/OMICRON_VERSION | 2 +- libs/api/__generated__/msw-handlers.ts | 77 +++++-- libs/api/__generated__/validate.ts | 199 +++++++++++++++--- libs/api/path-params.ts | 1 + 10 files changed, 586 insertions(+), 102 deletions(-) create mode 100644 libs/api-mocks/ip-pool.ts diff --git a/OMICRON_VERSION b/OMICRON_VERSION index d5bccabd46..0fb87828ce 100644 --- a/OMICRON_VERSION +++ b/OMICRON_VERSION @@ -1 +1 @@ -b1ebae8ab2cb7e636f04d847e0ac77aba891ff30 +19059b1bedc8bbc7a9d293486842a9a4fd264ea3 diff --git a/libs/api-mocks/index.ts b/libs/api-mocks/index.ts index 7a5b5fc160..24f0009f17 100644 --- a/libs/api-mocks/index.ts +++ b/libs/api-mocks/index.ts @@ -9,6 +9,7 @@ export * from './disk' export * from './image' export * from './instance' +export * from './ip-pool' export * from './network-interface' export * from './physical-disk' export * from './project' diff --git a/libs/api-mocks/ip-pool.ts b/libs/api-mocks/ip-pool.ts new file mode 100644 index 0000000000..f3accd49cd --- /dev/null +++ b/libs/api-mocks/ip-pool.ts @@ -0,0 +1,38 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * Copyright Oxide Computer Company + */ + +import { type IpPool, type IpPoolSilo } from '@oxide/api' + +import type { Json } from './json-type' +import { defaultSilo } from './silo' + +const ipPool1: Json = { + id: '69b5c583-74a9-451a-823d-0741c1ec66e2', + name: 'ip-pool-1', + description: '', + time_created: new Date().toISOString(), + time_modified: new Date().toISOString(), +} + +const ipPool2: Json = { + id: 'af2fbe06-b21d-4364-96b7-a58220bc3242', + name: 'ip-pool-2', + description: '', + time_created: new Date().toISOString(), + time_modified: new Date().toISOString(), +} + +export const ipPools: Json[] = [ipPool1, ipPool2] + +export const ipPoolSilos: Json[] = [ + { + ip_pool_id: ipPool1.id, + silo_id: defaultSilo.id, + is_default: true, + }, +] diff --git a/libs/api-mocks/msw/db.ts b/libs/api-mocks/msw/db.ts index 4f0babddb3..f489aeb37f 100644 --- a/libs/api-mocks/msw/db.ts +++ b/libs/api-mocks/msw/db.ts @@ -130,6 +130,28 @@ export const lookup = { if (!image) throw notFoundErr return image }, + ipPool({ pool: id }: PP.IpPool): Json { + if (!id) throw notFoundErr + + if (isUuid(id)) return lookupById(db.ipPools, id) + + const pool = db.ipPools.find((p) => p.name === id) + if (!pool) throw notFoundErr + + return pool + }, + // unusual one because it's a sibling relationship. we look up both the pool and the silo first + ipPoolSilo({ pool: poolId, silo: siloId }: PP.IpPool & PP.Silo): Json { + const pool = lookup.ipPool({ pool: poolId }) + const silo = lookup.silo({ silo: siloId }) + + const ipPoolSilo = db.ipPoolSilos.find( + (ips) => ips.ip_pool_id === pool.id && ips.silo_id === silo.id + ) + if (!ipPoolSilo) throw notFoundErr + + return ipPoolSilo + }, samlIdp({ provider: id, ...siloSelector @@ -203,6 +225,8 @@ const initDb = { groupMemberships: [...mock.groupMemberships], images: [...mock.images], instances: [...mock.instances], + ipPools: [...mock.ipPools], + ipPoolSilos: [...mock.ipPoolSilos], networkInterfaces: [mock.networkInterface], physicalDisks: [...mock.physicalDisks], projects: [...mock.projects], diff --git a/libs/api-mocks/msw/handlers.ts b/libs/api-mocks/msw/handlers.ts index f7e35eaf54..38b55b9026 100644 --- a/libs/api-mocks/msw/handlers.ts +++ b/libs/api-mocks/msw/handlers.ts @@ -547,6 +547,66 @@ export const handlers = makeHandlers({ return json(instance, { status: 202 }) }, + ipPoolList: ({ query }) => paginated(query, db.ipPools), + ipPoolView: ({ path }) => lookup.ipPool(path), + ipPoolSiloList({ path /*query*/ }) { + // TODO: paginated wants an id field, but this is a join table, so it has a + // composite pk + // return paginated(query, db.ipPoolResources) + + const pool = lookup.ipPool(path) + const assocs = db.ipPoolSilos.filter((ipr) => ipr.ip_pool_id === pool.id) + return { items: assocs } + }, + ipPoolSiloLink({ path, body }) { + const pool = lookup.ipPool(path) + const silo_id = lookup.silo({ silo: body.silo }).id + + const assoc = { + ip_pool_id: pool.id, + silo_id, + is_default: body.is_default, + } + + const alreadyThere = db.ipPoolSilos.find( + (ips) => ips.ip_pool_id === pool.id && ips.silo_id === silo_id + ) + + // TODO: this matches current API logic but makes no sense because is_default + // could be different. Need to fix that. Should 400 or 409 on conflict. + if (!alreadyThere) db.ipPoolSilos.push(assoc) + + return assoc + }, + ipPoolSiloUnlink({ path }) { + const pool = lookup.ipPool(path) + const silo = lookup.silo(path) + + // ignore is_default when deleting, it's not part of the pk + db.ipPoolSilos = db.ipPoolSilos.filter( + (ips) => !(ips.ip_pool_id === pool.id && ips.silo_id === silo.id) + ) + + return 204 + }, + ipPoolSiloUpdate: ({ path, body }) => { + const ipPoolSilo = lookup.ipPoolSilo(path) + + // if we're setting default, we need to set is_default false on the existing default + if (body.is_default) { + const silo = lookup.silo(path) + const existingDefault = db.ipPoolSilos.find( + (ips) => ips.silo_id === silo.id && ips.is_default + ) + if (existingDefault) { + existingDefault.is_default = false + } + } + + ipPoolSilo.is_default = body.is_default + + return ipPoolSilo + }, projectPolicyView({ path }) { const project = lookup.project(path) @@ -960,7 +1020,6 @@ export const handlers = makeHandlers({ siloMetric: handleMetrics, // Misc endpoints we're not using yet in the console - addSledToInitializedRack: NotImplemented, certificateCreate: NotImplemented, certificateDelete: NotImplemented, certificateList: NotImplemented, @@ -973,7 +1032,6 @@ export const handlers = makeHandlers({ instanceSerialConsoleStream: NotImplemented, ipPoolCreate: NotImplemented, ipPoolDelete: NotImplemented, - ipPoolList: NotImplemented, ipPoolRangeAdd: NotImplemented, ipPoolRangeList: NotImplemented, ipPoolRangeRemove: NotImplemented, @@ -982,7 +1040,6 @@ export const handlers = makeHandlers({ ipPoolServiceRangeRemove: NotImplemented, ipPoolServiceView: NotImplemented, ipPoolUpdate: NotImplemented, - ipPoolView: NotImplemented, localIdpUserCreate: NotImplemented, localIdpUserDelete: NotImplemented, localIdpUserSetPassword: NotImplemented, @@ -1021,12 +1078,13 @@ export const handlers = makeHandlers({ siloQuotasView: NotImplemented, siloUserList: NotImplemented, siloUserView: NotImplemented, + sledAdd: NotImplemented, + sledListUninitialized: NotImplemented, sledSetProvisionState: NotImplemented, switchList: NotImplemented, switchView: NotImplemented, systemPolicyUpdate: NotImplemented, systemQuotasList: NotImplemented, - uninitializedSledList: NotImplemented, userBuiltinList: NotImplemented, userBuiltinView: NotImplemented, }) diff --git a/libs/api/__generated__/Api.ts b/libs/api/__generated__/Api.ts index 39ea69f2db..d981991c33 100644 --- a/libs/api/__generated__/Api.ts +++ b/libs/api/__generated__/Api.ts @@ -1367,17 +1367,15 @@ export type InstanceSerialConsoleData = { } /** - * Identity-related metadata that's included in nearly all public API objects + * A collection of IP ranges. If a pool is linked to a silo, IP addresses from the pool can be allocated within that silo */ export type IpPool = { /** human-readable free-form text about a resource */ description: string /** unique, immutable, system-controlled identifier for each resource */ id: string - isDefault: boolean /** unique, mutable, user-controlled identifier for each resource */ name: Name - siloId?: string /** timestamp when this resource was created */ timeCreated: Date /** timestamp when this resource was last modified */ @@ -1387,14 +1385,7 @@ export type IpPool = { /** * Create-time parameters for an `IpPool` */ -export type IpPoolCreate = { - description: string - /** Whether the IP pool is considered a default pool for its scope (fleet or silo). If a pool is marked default and is associated with a silo, instances created in that silo will draw IPs from that pool unless another pool is specified at instance create time. */ - isDefault?: boolean - name: Name - /** If an IP pool is associated with a silo, instance IP allocations in that silo can draw from that pool. */ - silo?: NameOrId -} +export type IpPoolCreate = { description: string; name: Name } /** * A non-decreasing IPv4 address range, inclusive of both ends. @@ -1439,6 +1430,37 @@ export type IpPoolResultsPage = { nextPage?: string } +/** + * A link between an IP pool and a silo that allows one to allocate IPs from the pool within the silo + */ +export type IpPoolSilo = { + ipPoolId: string + /** When a pool is the default for a silo, floating IPs and instance ephemeral IPs will come from that pool when no other pool is specified. There can be at most one default for a given silo. */ + isDefault: boolean + siloId: string +} + +export type IpPoolSiloLink = { + /** When a pool is the default for a silo, floating IPs and instance ephemeral IPs will come from that pool when no other pool is specified. There can be at most one default for a given silo. */ + isDefault: boolean + silo: NameOrId +} + +/** + * A single page of results + */ +export type IpPoolSiloResultsPage = { + /** list of items on this page of results */ + items: IpPoolSilo[] + /** token used to fetch the next page of results (if any) */ + nextPage?: string +} + +export type IpPoolSiloUpdate = { + /** When a pool is the default for a silo, floating IPs and instance ephemeral IPs will come from that pool when no other pool is specified. There can be at most one default for a given silo, so when a pool is made default, an existing default will remain linked but will no longer be the default. */ + isDefault: boolean +} + /** * Parameters for updating an IP Pool */ @@ -1465,7 +1487,7 @@ export type LinkFec = /** * The LLDP configuration associated with a port. LLDP may be either enabled or disabled, if enabled, an LLDP configuration must be provided by name or id. */ -export type LldpServiceConfig = { +export type LldpServiceConfigCreate = { /** Whether or not LLDP is enabled. */ enabled: boolean /** A reference to the LLDP configuration used. Must not be `None` when `enabled` is `true`. */ @@ -1498,19 +1520,31 @@ export type LinkSpeed = /** * Switch link configuration. */ -export type LinkConfig = { +export type LinkConfigCreate = { /** Whether or not to set autonegotiation */ autoneg: boolean /** The forward error correction mode of the link. */ fec: LinkFec /** The link-layer discovery protocol (LLDP) configuration for the link. */ - lldp: LldpServiceConfig + lldp: LldpServiceConfigCreate /** Maximum transmission unit for the link. */ mtu: number /** The speed of the link. */ speed: LinkSpeed } +/** + * A link layer discovery protocol (LLDP) service configuration. + */ +export type LldpServiceConfig = { + /** Whether or not the LLDP service is enabled. */ + enabled: boolean + /** The id of this LLDP service instance. */ + id: string + /** The link-layer discovery protocol configuration for this service. */ + lldpConfigId?: string +} + /** * A loopback address is an address that is assigned to a rack switch but is not associated with any particular port. */ @@ -2154,6 +2188,33 @@ export type Switch = { timeModified: Date } +/** + * Describes the kind of an switch interface. + */ +export type SwitchInterfaceKind2 = + /** Primary interfaces are associated with physical links. There is exactly one primary interface per physical link. */ + | 'primary' + /** VLAN interfaces allow physical interfaces to be multiplexed onto multiple logical links, each distinguished by a 12-bit 802.1Q Ethernet tag. */ + | 'vlan' + /** Loopback interfaces are anchors for IP addresses that are not specific to any particular port. */ + | 'loopback' + +/** + * A switch port interface configuration for a port settings object. + */ +export type SwitchInterfaceConfig = { + /** A unique identifier for this switch interface. */ + id: string + /** The name of this switch interface. */ + interfaceName: string + /** The switch interface kind. */ + kind: SwitchInterfaceKind2 + /** The port settings object this switch interface configuration belongs to. */ + portSettingsId: string + /** Whether or not IPv6 is enabled on this interface. */ + v6Enabled: boolean +} + /** * Indicates the kind for a switch interface. */ @@ -2172,7 +2233,7 @@ export type SwitchInterfaceKind = /** * A layer-3 switch interface configuration. When IPv6 is enabled, a link local address will be created for the interface. */ -export type SwitchInterfaceConfig = { +export type SwitchInterfaceConfigCreate = { /** What kind of switch interface this configuration represents. */ kind: SwitchInterfaceKind /** Whether or not IPv6 is enabled. */ @@ -2231,6 +2292,27 @@ export type SwitchPortBgpPeerConfig = { portSettingsId: string } +/** + * The link geometry associated with a switch port. + */ +export type SwitchPortGeometry2 = + /** The port contains a single QSFP28 link with four lanes. */ + | 'qsfp28x1' + /** The port contains two QSFP28 links each with two lanes. */ + | 'qsfp28x2' + /** The port contains four SFP28 links each with one lane. */ + | 'sfp28x4' + +/** + * A physical port configuration for a port settings object. + */ +export type SwitchPortConfig = { + /** The physical link geometry of the port. */ + geometry: SwitchPortGeometry2 + /** The id of the port settings object this configuration belongs to. */ + portSettingsId: string +} + /** * The link geometry associated with a switch port. */ @@ -2245,7 +2327,7 @@ export type SwitchPortGeometry = /** * Physical switch port configuration. */ -export type SwitchPortConfig = { +export type SwitchPortConfigCreate = { /** Link geometry for the switch port. */ geometry: SwitchPortGeometry } @@ -2317,11 +2399,11 @@ export type SwitchPortSettingsCreate = { description: string groups: NameOrId[] /** Interfaces indexed by link name. */ - interfaces: Record + interfaces: Record /** Links indexed by phy name. On ports that are not broken out, this is always phy0. On a 2x breakout the options are phy0 and phy1, on 4x phy0-phy3, etc. */ - links: Record + links: Record name: Name - portConfig: SwitchPortConfig + portConfig: SwitchPortConfigCreate /** Routes indexed by interface name. */ routes: Record } @@ -2397,6 +2479,21 @@ export type SwitchResultsPage = { */ export type UninitializedSled = { baseboard: Baseboard; cubby: number; rackId: string } +/** + * The unique hardware ID for a sled + */ +export type UninitializedSledId = { part: string; serial: string } + +/** + * A single page of results + */ +export type UninitializedSledResultsPage = { + /** list of items on this page of results */ + items: UninitializedSled[] + /** token used to fetch the next page of results (if any) */ + nextPage?: string +} + /** * View of a User */ @@ -3039,7 +3136,6 @@ export interface InstanceStopQueryParams { export interface ProjectIpPoolListQueryParams { limit?: number pageToken?: string - project?: NameOrId sortBy?: NameOrIdSortMode } @@ -3047,10 +3143,6 @@ export interface ProjectIpPoolViewPathParams { pool: NameOrId } -export interface ProjectIpPoolViewQueryParams { - project?: NameOrId -} - export interface LoginLocalPathParams { siloName: Name } @@ -3231,6 +3323,11 @@ export interface SledSetProvisionStatePathParams { sledId: string } +export interface SledListUninitializedQueryParams { + limit?: number + pageToken?: string +} + export interface NetworkingSwitchPortListQueryParams { limit?: number pageToken?: string @@ -3340,6 +3437,30 @@ export interface IpPoolRangeRemovePathParams { pool: NameOrId } +export interface IpPoolSiloListPathParams { + pool: NameOrId +} + +export interface IpPoolSiloListQueryParams { + limit?: number + pageToken?: string + sortBy?: IdSortMode +} + +export interface IpPoolSiloLinkPathParams { + pool: NameOrId +} + +export interface IpPoolSiloUpdatePathParams { + pool: NameOrId + silo: NameOrId +} + +export interface IpPoolSiloUnlinkPathParams { + pool: NameOrId + silo: NameOrId +} + export interface IpPoolServiceRangeListQueryParams { limit?: number pageToken?: string @@ -3636,10 +3757,10 @@ export type ApiListMethods = Pick< | 'sledInstanceList' | 'networkingSwitchPortList' | 'switchList' - | 'uninitializedSledList' | 'siloIdentityProviderList' | 'ipPoolList' | 'ipPoolRangeList' + | 'ipPoolSiloList' | 'ipPoolServiceRangeList' | 'networkingAddressLotList' | 'networkingAddressLotBlockList' @@ -3917,7 +4038,7 @@ export class Api extends HttpClient { }) }, /** - * List all Floating IPs + * List all floating IPs */ floatingIpList: ( { query = {} }: { query?: FloatingIpListQueryParams }, @@ -3931,7 +4052,7 @@ export class Api extends HttpClient { }) }, /** - * Create a Floating IP + * Create a floating IP */ floatingIpCreate: ( { query, body }: { query?: FloatingIpCreateQueryParams; body: FloatingIpCreate }, @@ -3963,7 +4084,7 @@ export class Api extends HttpClient { }) }, /** - * Delete a Floating IP + * Delete a floating IP */ floatingIpDelete: ( { @@ -4332,7 +4453,7 @@ export class Api extends HttpClient { }) }, /** - * List all IP Pools that can be used by a given project. + * List all IP pools */ projectIpPoolList: ( { query = {} }: { query?: ProjectIpPoolListQueryParams }, @@ -4349,16 +4470,12 @@ export class Api extends HttpClient { * Fetch an IP pool */ projectIpPoolView: ( - { - path, - query = {}, - }: { path: ProjectIpPoolViewPathParams; query?: ProjectIpPoolViewQueryParams }, + { path }: { path: ProjectIpPoolViewPathParams }, params: FetchParams = {} ) => { return this.request({ path: `/v1/ip-pools/${path.pool}`, method: 'GET', - query, ...params, }) }, @@ -4814,10 +4931,7 @@ export class Api extends HttpClient { /** * Add a sled to an initialized rack */ - addSledToInitializedRack: ( - { body }: { body: UninitializedSled }, - params: FetchParams = {} - ) => { + sledAdd: ({ body }: { body: UninitializedSledId }, params: FetchParams = {}) => { return this.request({ path: `/v1/system/hardware/sleds`, method: 'POST', @@ -4870,7 +4984,7 @@ export class Api extends HttpClient { }) }, /** - * Set the sled's provision state. + * Set the sled's provision state */ sledSetProvisionState: ( { @@ -4886,6 +5000,20 @@ export class Api extends HttpClient { ...params, }) }, + /** + * List uninitialized sleds in a given rack + */ + sledListUninitialized: ( + { query = {} }: { query?: SledListUninitializedQueryParams }, + params: FetchParams = {} + ) => { + return this.request({ + path: `/v1/system/hardware/sleds-uninitialized`, + method: 'GET', + query, + ...params, + }) + }, /** * List switch ports */ @@ -4967,16 +5095,6 @@ export class Api extends HttpClient { ...params, }) }, - /** - * List uninitialized sleds in a given rack - */ - uninitializedSledList: (_: EmptyObj, params: FetchParams = {}) => { - return this.request({ - path: `/v1/system/hardware/uninitialized-sleds`, - method: 'GET', - ...params, - }) - }, /** * List a silo's IdP's name */ @@ -5123,7 +5241,7 @@ export class Api extends HttpClient { }) }, /** - * Update an IP Pool + * Update an IP pool */ ipPoolUpdate: ( { path, body }: { path: IpPoolUpdatePathParams; body: IpPoolUpdate }, @@ -5137,7 +5255,7 @@ export class Api extends HttpClient { }) }, /** - * Delete an IP Pool + * Delete an IP pool */ ipPoolDelete: ( { path }: { path: IpPoolDeletePathParams }, @@ -5194,6 +5312,64 @@ export class Api extends HttpClient { ...params, }) }, + /** + * List an IP pool's linked silos + */ + ipPoolSiloList: ( + { + path, + query = {}, + }: { path: IpPoolSiloListPathParams; query?: IpPoolSiloListQueryParams }, + params: FetchParams = {} + ) => { + return this.request({ + path: `/v1/system/ip-pools/${path.pool}/silos`, + method: 'GET', + query, + ...params, + }) + }, + /** + * Make an IP pool available within a silo + */ + ipPoolSiloLink: ( + { path, body }: { path: IpPoolSiloLinkPathParams; body: IpPoolSiloLink }, + params: FetchParams = {} + ) => { + return this.request({ + path: `/v1/system/ip-pools/${path.pool}/silos`, + method: 'POST', + body, + ...params, + }) + }, + /** + * Make an IP pool default or not-default for a silo + */ + ipPoolSiloUpdate: ( + { path, body }: { path: IpPoolSiloUpdatePathParams; body: IpPoolSiloUpdate }, + params: FetchParams = {} + ) => { + return this.request({ + path: `/v1/system/ip-pools/${path.pool}/silos/${path.silo}`, + method: 'PUT', + body, + ...params, + }) + }, + /** + * Unlink an IP pool from a silo + */ + ipPoolSiloUnlink: ( + { path }: { path: IpPoolSiloUnlinkPathParams }, + params: FetchParams = {} + ) => { + return this.request({ + path: `/v1/system/ip-pools/${path.pool}/silos/${path.silo}`, + method: 'DELETE', + ...params, + }) + }, /** * Fetch the IP pool used for Oxide services */ diff --git a/libs/api/__generated__/OMICRON_VERSION b/libs/api/__generated__/OMICRON_VERSION index 6fb1540af7..2f27ab7450 100644 --- a/libs/api/__generated__/OMICRON_VERSION +++ b/libs/api/__generated__/OMICRON_VERSION @@ -1,2 +1,2 @@ # generated file. do not update manually. see docs/update-pinned-api.md -b1ebae8ab2cb7e636f04d847e0ac77aba891ff30 +19059b1bedc8bbc7a9d293486842a9a4fd264ea3 diff --git a/libs/api/__generated__/msw-handlers.ts b/libs/api/__generated__/msw-handlers.ts index 3122a89dae..3e76270e71 100644 --- a/libs/api/__generated__/msw-handlers.ts +++ b/libs/api/__generated__/msw-handlers.ts @@ -344,7 +344,6 @@ export interface MSWHandlers { /** `GET /v1/ip-pools/:pool` */ projectIpPoolView: (params: { path: Api.ProjectIpPoolViewPathParams - query: Api.ProjectIpPoolViewQueryParams req: Request cookies: Record }) => Promisable> @@ -549,8 +548,8 @@ export interface MSWHandlers { cookies: Record }) => Promisable> /** `POST /v1/system/hardware/sleds` */ - addSledToInitializedRack: (params: { - body: Json + sledAdd: (params: { + body: Json req: Request cookies: Record }) => Promisable @@ -581,6 +580,12 @@ export interface MSWHandlers { req: Request cookies: Record }) => Promisable> + /** `GET /v1/system/hardware/sleds-uninitialized` */ + sledListUninitialized: (params: { + query: Api.SledListUninitializedQueryParams + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/hardware/switch-port` */ networkingSwitchPortList: (params: { query: Api.NetworkingSwitchPortListQueryParams @@ -614,11 +619,6 @@ export interface MSWHandlers { req: Request cookies: Record }) => Promisable> - /** `GET /v1/system/hardware/uninitialized-sleds` */ - uninitializedSledList: (params: { - req: Request - cookies: Record - }) => Promisable /** `GET /v1/system/identity-providers` */ siloIdentityProviderList: (params: { query: Api.SiloIdentityProviderListQueryParams @@ -713,6 +713,33 @@ export interface MSWHandlers { req: Request cookies: Record }) => Promisable + /** `GET /v1/system/ip-pools/:pool/silos` */ + ipPoolSiloList: (params: { + path: Api.IpPoolSiloListPathParams + query: Api.IpPoolSiloListQueryParams + req: Request + cookies: Record + }) => Promisable> + /** `POST /v1/system/ip-pools/:pool/silos` */ + ipPoolSiloLink: (params: { + path: Api.IpPoolSiloLinkPathParams + body: Json + req: Request + cookies: Record + }) => Promisable> + /** `PUT /v1/system/ip-pools/:pool/silos/:silo` */ + ipPoolSiloUpdate: (params: { + path: Api.IpPoolSiloUpdatePathParams + body: Json + req: Request + cookies: Record + }) => Promisable> + /** `DELETE /v1/system/ip-pools/:pool/silos/:silo` */ + ipPoolSiloUnlink: (params: { + path: Api.IpPoolSiloUnlinkPathParams + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/ip-pools-service` */ ipPoolServiceView: (params: { req: Request @@ -1526,7 +1553,7 @@ export function makeHandlers(handlers: MSWHandlers): HttpHandler[] { ), http.post( '/v1/system/hardware/sleds', - handler(handlers['addSledToInitializedRack'], null, schema.UninitializedSled) + handler(handlers['sledAdd'], null, schema.UninitializedSledId) ), http.get( '/v1/system/hardware/sleds/:sledId', @@ -1548,6 +1575,10 @@ export function makeHandlers(handlers: MSWHandlers): HttpHandler[] { schema.SledProvisionStateParams ) ), + http.get( + '/v1/system/hardware/sleds-uninitialized', + handler(handlers['sledListUninitialized'], schema.SledListUninitializedParams, null) + ), http.get( '/v1/system/hardware/switch-port', handler( @@ -1580,10 +1611,6 @@ export function makeHandlers(handlers: MSWHandlers): HttpHandler[] { '/v1/system/hardware/switches/:switchId', handler(handlers['switchView'], schema.SwitchViewParams, null) ), - http.get( - '/v1/system/hardware/uninitialized-sleds', - handler(handlers['uninitializedSledList'], null, null) - ), http.get( '/v1/system/identity-providers', handler( @@ -1660,6 +1687,30 @@ export function makeHandlers(handlers: MSWHandlers): HttpHandler[] { '/v1/system/ip-pools/:pool/ranges/remove', handler(handlers['ipPoolRangeRemove'], schema.IpPoolRangeRemoveParams, schema.IpRange) ), + http.get( + '/v1/system/ip-pools/:pool/silos', + handler(handlers['ipPoolSiloList'], schema.IpPoolSiloListParams, null) + ), + http.post( + '/v1/system/ip-pools/:pool/silos', + handler( + handlers['ipPoolSiloLink'], + schema.IpPoolSiloLinkParams, + schema.IpPoolSiloLink + ) + ), + http.put( + '/v1/system/ip-pools/:pool/silos/:silo', + handler( + handlers['ipPoolSiloUpdate'], + schema.IpPoolSiloUpdateParams, + schema.IpPoolSiloUpdate + ) + ), + http.delete( + '/v1/system/ip-pools/:pool/silos/:silo', + handler(handlers['ipPoolSiloUnlink'], schema.IpPoolSiloUnlinkParams, null) + ), http.get( '/v1/system/ip-pools-service', handler(handlers['ipPoolServiceView'], null, null) diff --git a/libs/api/__generated__/validate.ts b/libs/api/__generated__/validate.ts index 2eb55410a8..0fab944772 100644 --- a/libs/api/__generated__/validate.ts +++ b/libs/api/__generated__/validate.ts @@ -67,7 +67,7 @@ export const Name = z.preprocess( .min(1) .max(63) .regex( - /^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)^[a-z][a-z0-9-]*[a-zA-Z0-9]*$/ + /^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)^[a-z]([a-zA-Z0-9-]*[a-zA-Z0-9]+)?$/ ) ) @@ -1451,16 +1451,14 @@ export const InstanceSerialConsoleData = z.preprocess( ) /** - * Identity-related metadata that's included in nearly all public API objects + * A collection of IP ranges. If a pool is linked to a silo, IP addresses from the pool can be allocated within that silo */ export const IpPool = z.preprocess( processResponseBody, z.object({ description: z.string(), id: z.string().uuid(), - isDefault: SafeBoolean, name: Name, - siloId: z.string().uuid().optional(), timeCreated: z.coerce.date(), timeModified: z.coerce.date(), }) @@ -1471,12 +1469,7 @@ export const IpPool = z.preprocess( */ export const IpPoolCreate = z.preprocess( processResponseBody, - z.object({ - description: z.string(), - isDefault: SafeBoolean.default(false).optional(), - name: Name, - silo: NameOrId.optional(), - }) + z.object({ description: z.string(), name: Name }) ) /** @@ -1527,6 +1520,36 @@ export const IpPoolResultsPage = z.preprocess( z.object({ items: IpPool.array(), nextPage: z.string().optional() }) ) +/** + * A link between an IP pool and a silo that allows one to allocate IPs from the pool within the silo + */ +export const IpPoolSilo = z.preprocess( + processResponseBody, + z.object({ + ipPoolId: z.string().uuid(), + isDefault: SafeBoolean, + siloId: z.string().uuid(), + }) +) + +export const IpPoolSiloLink = z.preprocess( + processResponseBody, + z.object({ isDefault: SafeBoolean, silo: NameOrId }) +) + +/** + * A single page of results + */ +export const IpPoolSiloResultsPage = z.preprocess( + processResponseBody, + z.object({ items: IpPoolSilo.array(), nextPage: z.string().optional() }) +) + +export const IpPoolSiloUpdate = z.preprocess( + processResponseBody, + z.object({ isDefault: SafeBoolean }) +) + /** * Parameters for updating an IP Pool */ @@ -1557,7 +1580,7 @@ export const LinkFec = z.preprocess(processResponseBody, z.enum(['firecode', 'no /** * The LLDP configuration associated with a port. LLDP may be either enabled or disabled, if enabled, an LLDP configuration must be provided by name or id. */ -export const LldpServiceConfig = z.preprocess( +export const LldpServiceConfigCreate = z.preprocess( processResponseBody, z.object({ enabled: SafeBoolean, lldpConfig: NameOrId.optional() }) ) @@ -1583,17 +1606,29 @@ export const LinkSpeed = z.preprocess( /** * Switch link configuration. */ -export const LinkConfig = z.preprocess( +export const LinkConfigCreate = z.preprocess( processResponseBody, z.object({ autoneg: SafeBoolean, fec: LinkFec, - lldp: LldpServiceConfig, + lldp: LldpServiceConfigCreate, mtu: z.number().min(0).max(65535), speed: LinkSpeed, }) ) +/** + * A link layer discovery protocol (LLDP) service configuration. + */ +export const LldpServiceConfig = z.preprocess( + processResponseBody, + z.object({ + enabled: SafeBoolean, + id: z.string().uuid(), + lldpConfigId: z.string().uuid().optional(), + }) +) + /** * A loopback address is an address that is assigned to a rack switch but is not associated with any particular port. */ @@ -2183,6 +2218,28 @@ export const Switch = z.preprocess( }) ) +/** + * Describes the kind of an switch interface. + */ +export const SwitchInterfaceKind2 = z.preprocess( + processResponseBody, + z.enum(['primary', 'vlan', 'loopback']) +) + +/** + * A switch port interface configuration for a port settings object. + */ +export const SwitchInterfaceConfig = z.preprocess( + processResponseBody, + z.object({ + id: z.string().uuid(), + interfaceName: z.string(), + kind: SwitchInterfaceKind2, + portSettingsId: z.string().uuid(), + v6Enabled: SafeBoolean, + }) +) + /** * Indicates the kind for a switch interface. */ @@ -2198,7 +2255,7 @@ export const SwitchInterfaceKind = z.preprocess( /** * A layer-3 switch interface configuration. When IPv6 is enabled, a link local address will be created for the interface. */ -export const SwitchInterfaceConfig = z.preprocess( +export const SwitchInterfaceConfigCreate = z.preprocess( processResponseBody, z.object({ kind: SwitchInterfaceKind, v6Enabled: SafeBoolean }) ) @@ -2251,6 +2308,22 @@ export const SwitchPortBgpPeerConfig = z.preprocess( }) ) +/** + * The link geometry associated with a switch port. + */ +export const SwitchPortGeometry2 = z.preprocess( + processResponseBody, + z.enum(['qsfp28x1', 'qsfp28x2', 'sfp28x4']) +) + +/** + * A physical port configuration for a port settings object. + */ +export const SwitchPortConfig = z.preprocess( + processResponseBody, + z.object({ geometry: SwitchPortGeometry2, portSettingsId: z.string().uuid() }) +) + /** * The link geometry associated with a switch port. */ @@ -2262,7 +2335,7 @@ export const SwitchPortGeometry = z.preprocess( /** * Physical switch port configuration. */ -export const SwitchPortConfig = z.preprocess( +export const SwitchPortConfigCreate = z.preprocess( processResponseBody, z.object({ geometry: SwitchPortGeometry }) ) @@ -2326,10 +2399,10 @@ export const SwitchPortSettingsCreate = z.preprocess( bgpPeers: z.record(z.string().min(1), BgpPeerConfig), description: z.string(), groups: NameOrId.array(), - interfaces: z.record(z.string().min(1), SwitchInterfaceConfig), - links: z.record(z.string().min(1), LinkConfig), + interfaces: z.record(z.string().min(1), SwitchInterfaceConfigCreate), + links: z.record(z.string().min(1), LinkConfigCreate), name: Name, - portConfig: SwitchPortConfig, + portConfig: SwitchPortConfigCreate, routes: z.record(z.string().min(1), RouteConfig), }) ) @@ -2397,6 +2470,22 @@ export const UninitializedSled = z.preprocess( }) ) +/** + * The unique hardware ID for a sled + */ +export const UninitializedSledId = z.preprocess( + processResponseBody, + z.object({ part: z.string(), serial: z.string() }) +) + +/** + * A single page of results + */ +export const UninitializedSledResultsPage = z.preprocess( + processResponseBody, + z.object({ items: UninitializedSled.array(), nextPage: z.string().optional() }) +) + /** * View of a User */ @@ -2441,7 +2530,7 @@ export const UserId = z.preprocess( .min(1) .max(63) .regex( - /^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)^[a-z][a-z0-9-]*[a-zA-Z0-9]*$/ + /^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)^[a-z]([a-zA-Z0-9-]*[a-zA-Z0-9]+)?$/ ) ) @@ -3251,7 +3340,6 @@ export const ProjectIpPoolListParams = z.preprocess( query: z.object({ limit: z.number().min(1).max(4294967295).optional(), pageToken: z.string().optional(), - project: NameOrId.optional(), sortBy: NameOrIdSortMode.optional(), }), }) @@ -3263,9 +3351,7 @@ export const ProjectIpPoolViewParams = z.preprocess( path: z.object({ pool: NameOrId, }), - query: z.object({ - project: NameOrId.optional(), - }), + query: z.object({}), }) ) @@ -3615,7 +3701,7 @@ export const SledListParams = z.preprocess( }) ) -export const AddSledToInitializedRackParams = z.preprocess( +export const SledAddParams = z.preprocess( processResponseBody, z.object({ path: z.object({}), @@ -3671,6 +3757,17 @@ export const SledSetProvisionStateParams = z.preprocess( }) ) +export const SledListUninitializedParams = z.preprocess( + processResponseBody, + z.object({ + path: z.object({}), + query: z.object({ + limit: z.number().min(1).max(4294967295).optional(), + pageToken: z.string().optional(), + }), + }) +) + export const NetworkingSwitchPortListParams = z.preprocess( processResponseBody, z.object({ @@ -3732,14 +3829,6 @@ export const SwitchViewParams = z.preprocess( }) ) -export const UninitializedSledListParams = z.preprocess( - processResponseBody, - z.object({ - path: z.object({}), - query: z.object({}), - }) -) - export const SiloIdentityProviderListParams = z.preprocess( processResponseBody, z.object({ @@ -3892,6 +3981,52 @@ export const IpPoolRangeRemoveParams = z.preprocess( }) ) +export const IpPoolSiloListParams = z.preprocess( + processResponseBody, + z.object({ + path: z.object({ + pool: NameOrId, + }), + query: z.object({ + limit: z.number().min(1).max(4294967295).optional(), + pageToken: z.string().optional(), + sortBy: IdSortMode.optional(), + }), + }) +) + +export const IpPoolSiloLinkParams = z.preprocess( + processResponseBody, + z.object({ + path: z.object({ + pool: NameOrId, + }), + query: z.object({}), + }) +) + +export const IpPoolSiloUpdateParams = z.preprocess( + processResponseBody, + z.object({ + path: z.object({ + pool: NameOrId, + silo: NameOrId, + }), + query: z.object({}), + }) +) + +export const IpPoolSiloUnlinkParams = z.preprocess( + processResponseBody, + z.object({ + path: z.object({ + pool: NameOrId, + silo: NameOrId, + }), + query: z.object({}), + }) +) + export const IpPoolServiceViewParams = z.preprocess( processResponseBody, z.object({ diff --git a/libs/api/path-params.ts b/libs/api/path-params.ts index 9f561d1623..8f2ccc8270 100644 --- a/libs/api/path-params.ts +++ b/libs/api/path-params.ts @@ -21,5 +21,6 @@ export type IdentityProvider = Merge export type SystemUpdate = { version: string } export type SshKey = { sshKey: string } export type Sled = { sledId?: string } +export type IpPool = { pool?: string } export type Id = { id: string }