-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathopenapi.yaml
More file actions
342 lines (339 loc) · 13 KB
/
Copy pathopenapi.yaml
File metadata and controls
342 lines (339 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
openapi: 3.0.3
info:
title: FindNextCIDRRange
version: 2.3.0
description: |
An HTTP API that answers one question well: what is the next free CIDR of a given size in an
Azure virtual network? A second endpoint, `/CheckCidr`, checks any CIDR against Azure's
subnet rules using nothing but arithmetic, so it needs no Azure access at all.
Two behaviours are preserved deliberately from the original 1.x contract, because existing
consumers depend on them:
* Responses are JSON in the body but carry `Content-Type: text/plain; charset=utf-8`.
* Every error travels with HTTP 400 on the wire; the meaningful status lives in the error
body's `code` field (`400` invalid input, `404` vnet or address space not found, `500`
unexpected failure).
A deployment can opt out of the second behaviour: with the `HONOR_HTTP_STATUS` app setting
set to `true`, errors travel with the status from the body's `code` field instead (so 404 and
500 appear on the wire). The bodies are identical in both modes; only the status line moves.
Ask the operator of the deployment you are calling which mode it runs.
The caller's identity is the function app's managed identity, which needs Reader on the scope
holding the virtual network being queried.
license:
name: MIT
url: https://github.com/libre-devops/FindNextCIDRRange/blob/main/LICENSE
contact:
name: Libre DevOps
url: https://libredevops.org
servers:
- url: /api
description: Relative to the function app host (the default route prefix)
paths:
/GetCidr:
get:
operationId: getCidr
summary: Propose the next free CIDR of the requested size
description: >-
Walks the vnet's address spaces in order and returns the first candidate subnet of the
requested size that does not overlap any existing subnet.
parameters:
- $ref: '#/components/parameters/subscriptionId'
- $ref: '#/components/parameters/resourceGroupName'
- $ref: '#/components/parameters/virtualNetworkName'
- $ref: '#/components/parameters/cidr'
- $ref: '#/components/parameters/addressSpace'
responses:
'200':
$ref: '#/components/responses/Success'
'400':
$ref: '#/components/responses/Error'
post:
operationId: getCidrPost
summary: Identical to GET; parameters are still read from the query string
description: >-
Accepted for compatibility with the original function. Any request body is ignored; all
parameters come from the query string, exactly as with GET.
parameters:
- $ref: '#/components/parameters/subscriptionId'
- $ref: '#/components/parameters/resourceGroupName'
- $ref: '#/components/parameters/virtualNetworkName'
- $ref: '#/components/parameters/cidr'
- $ref: '#/components/parameters/addressSpace'
responses:
'200':
$ref: '#/components/responses/Success'
'400':
$ref: '#/components/responses/Error'
/CheckCidr:
get:
operationId: checkCidr
summary: Check a CIDR against Azure's subnet rules, no Azure access needed
description: >-
Pure arithmetic plus Azure's subnet rules; nothing is looked up in Azure, so this endpoint
involves no identity and no Reader grant. Azure reserves five addresses in every subnet
(the network address, the default gateway, two addresses for Azure DNS, and the broadcast
address), which is why subnets run from /2 to /29 and a /30 cannot exist. This endpoint
postdates the 1.x contract and carries none of the preserved quirks: responses are
application/json and the wire status is always truthful, 200 for any parseable CIDR
(a verdict of "not allowed in Azure" is still a successful answer) and 400 only for input
that cannot be parsed.
parameters:
- $ref: '#/components/parameters/checkCidrInput'
responses:
'200':
$ref: '#/components/responses/CheckCidrResult'
'400':
$ref: '#/components/responses/CheckCidrBadInput'
post:
operationId: checkCidrPost
summary: Identical to GET; parameters are still read from the query string
description: >-
Accepted for symmetry with GetCidr. Any request body is ignored; the cidr parameter comes
from the query string, exactly as with GET.
parameters:
- $ref: '#/components/parameters/checkCidrInput'
responses:
'200':
$ref: '#/components/responses/CheckCidrResult'
'400':
$ref: '#/components/responses/CheckCidrBadInput'
components:
parameters:
subscriptionId:
name: subscriptionId
in: query
required: true
description: The Azure subscription holding the virtual network.
schema:
type: string
format: uuid
example: 00000000-0000-0000-0000-000000000000
resourceGroupName:
name: resourceGroupName
in: query
required: true
description: The resource group holding the virtual network.
schema:
type: string
example: rg-example
virtualNetworkName:
name: virtualNetworkName
in: query
required: true
description: The virtual network to search.
schema:
type: string
example: vnet-example-001
cidr:
name: cidr
in: query
required: true
description: The requested subnet size as a CIDR prefix length.
schema:
type: integer
minimum: 2
maximum: 29
example: 26
addressSpace:
name: addressSpace
in: query
required: false
description: >-
Narrows the search to one of the vnet's address spaces. The value must match the space
exactly in normalised form (for example 10.30.0.0/24). Omitted, the first address space
that can fit the request wins.
schema:
type: string
example: 10.30.0.0/24
checkCidrInput:
name: cidr
in: query
required: true
description: >-
The CIDR to check, with an explicit prefix length (a bare address would need a guessed
classful mask, so it is rejected). IPv4 only; Azure IPv6 subnets are always /64.
schema:
type: string
example: 10.0.0.0/29
schemas:
ProposedSubnetResponse:
type: object
description: A proposal; nothing is created or reserved by this API.
properties:
name:
type: string
description: The virtual network's name.
id:
type: string
description: The virtual network's ARM resource ID.
type:
type: string
description: Always Microsoft.Network/virtualNetworks.
location:
type: string
description: The virtual network's Azure region.
addressSpace:
type: string
description: The address space the proposal was carved from.
proposedCIDR:
type: string
description: The first free subnet of the requested size.
CustomError:
type: object
properties:
code:
type: string
description: >-
The meaningful HTTP status as a string. The wire status is 400 regardless by default;
deployments with HONOR_HTTP_STATUS enabled send this value on the wire too.
enum: ['400', '404', '500']
message:
type: string
description: The status name, a comma, then the detail.
CheckCidrResponse:
type: object
properties:
cidr:
type: string
description: The input, echoed.
normalized:
type: string
description: The network form of the input; host bits, if any, are dropped.
validAzureSubnet:
type: boolean
description: Whether Azure would accept a subnet of this size (/2 through /29).
prefixLength:
type: integer
totalAddresses:
type: integer
description: The arithmetic size of the range.
azureReservedAddresses:
type: integer
description: Always 5; the addresses Azure takes from every subnet.
usableAddresses:
type: integer
description: totalAddresses minus the reserved 5, or 0 when the subnet is not valid.
reserved:
type: object
nullable: true
description: The five reserved addresses; null when the subnet is not valid in Azure.
properties:
networkAddress:
type: string
defaultGateway:
type: string
azureDns:
type: array
items:
type: string
broadcast:
type: string
firstUsable:
type: string
nullable: true
description: The first address Azure would actually hand out.
lastUsable:
type: string
nullable: true
reason:
type: string
nullable: true
description: Why the subnet is not valid in Azure; null when it is.
CheckCidrError:
type: object
properties:
error:
type: string
responses:
Success:
description: >-
A subnet of the requested size fits. The body is JSON, served as text/plain (a preserved
1.x behaviour).
content:
text/plain:
schema:
$ref: '#/components/schemas/ProposedSubnetResponse'
example:
name: vnet-example-001
id: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-example/providers/Microsoft.Network/virtualNetworks/vnet-example-001
type: Microsoft.Network/virtualNetworks
location: uksouth
addressSpace: 10.30.0.0/24
proposedCIDR: 10.30.0.128/26
Error:
description: >-
Any failure. The wire status is always 400 by default (a preserved 1.x behaviour); read
the body's code field for the meaningful status: 400 invalid input, 404 vnet or address
space not found (or nothing of the requested size fits), 500 unexpected failure. On a
deployment with HONOR_HTTP_STATUS enabled the same code also becomes the wire status.
content:
text/plain:
schema:
$ref: '#/components/schemas/CustomError'
examples:
invalidInput:
summary: Invalid input (body code 400)
value:
code: '400'
message: 'BadRequest, Invalid CIDR size requested: 55'
notFound:
summary: Vnet, address space, or free block not found (body code 404)
value:
code: '404'
message: NotFound, VNet rg-example/vnet-example-001 cannot accept a subnet of size 26
unexpected:
summary: Unexpected failure (body code 500)
value:
code: '500'
message: InternalServerError, <the underlying exception message>
CheckCidrResult:
description: >-
The verdict for a parseable CIDR, valid in Azure or not. Served as application/json with
a truthful wire status; this endpoint has no 1.x quirks to preserve.
content:
application/json:
schema:
$ref: '#/components/schemas/CheckCidrResponse'
examples:
valid:
summary: The smallest subnet Azure allows
value:
cidr: 10.0.0.0/29
normalized: 10.0.0.0/29
validAzureSubnet: true
prefixLength: 29
totalAddresses: 8
azureReservedAddresses: 5
usableAddresses: 3
reserved:
networkAddress: 10.0.0.0
defaultGateway: 10.0.0.1
azureDns: ['10.0.0.2', '10.0.0.3']
broadcast: 10.0.0.7
firstUsable: 10.0.0.4
lastUsable: 10.0.0.6
reason: null
tooSmall:
summary: A /30 cannot exist in Azure
value:
cidr: 10.0.0.0/30
normalized: 10.0.0.0/30
validAzureSubnet: false
prefixLength: 30
totalAddresses: 4
azureReservedAddresses: 5
usableAddresses: 0
reserved: null
firstUsable: null
lastUsable: null
reason: >-
An Azure subnet cannot be smaller than /29: Azure reserves 5 addresses in every
subnet (the network address, the default gateway, two addresses for Azure DNS,
and the broadcast address), so a /30 with 4 addresses cannot fit them.
CheckCidrBadInput:
description: The input could not be parsed as an IPv4 CIDR; the body says why.
content:
application/json:
schema:
$ref: '#/components/schemas/CheckCidrError'
example:
error: 'cidr must include a prefix length, for example 10.0.0.0/24'