diff --git a/README.md b/README.md
index b13e653..7ffc816 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,14 @@ should align to these tenets, or propose an adjustment to the tenets.
- Azure Reserved - DNS Mapping
- Broadcast Address (last network address)
+### OCI mode ([docs](https://docs.oracle.com/en-us/iaas/Content/Network/Concepts/overview.htm#Reserved__reserved_subnet)):
+
+- Smallest subnet: /30
+- Three reserved addresses per subnet:
+ - Network Address (network + 0)
+ - OCI Reserved - Default Gateway Address (network + 1)
+ - Broadcast Address (last network address)
+
## Building From Source
If you have a more opinionated best-practice way to lay out this repository please open an issue.
diff --git a/dist/index.html b/dist/index.html
index 79ea7ce..fefeb57 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -107,6 +107,7 @@
Visual Subnet Calculator
Mode - Standard
Mode - AWS
Mode - Azure
+ Mode - OCI
Import / Export
diff --git a/dist/js/main.js b/dist/js/main.js
index c2d4ed7..93607d3 100644
--- a/dist/js/main.js
+++ b/dist/js/main.js
@@ -23,6 +23,12 @@ let infoColumnCount = 5
// - Net+2 = Reserved - DNS Mapping
// - Net+3 = Reserved - DNS Mapping
// - Last = Broadcast Address
+// OCI mode:
+// - Smallest subnet: /30
+// - Three reserved addresses per subnet:
+// - Net+0 = Network Address
+// - Net+1 = OCI Reserved - Default Gateway Address
+// - Last = Broadcast Address
let noteTimeout;
let operatingMode = 'Standard'
let previousOperatingMode = 'Standard'
@@ -34,12 +40,14 @@ const netsizePatterns = {
Standard: '^([12]?[0-9]|3[0-2])$',
AZURE: '^([12]?[0-9])$',
AWS: '^(1?[0-9]|2[0-8])$',
+ OCI: '^([12]?[0-9]|{30})$',
};
const minSubnetSizes = {
Standard: 32,
AZURE: 29,
AWS: 28,
+ OCI: 30,
};
$('input#network').on('paste', function (e) {
@@ -123,6 +131,15 @@ $('#dropdown_aws').click(function() {
}
});
+$('#dropdown_oci').click(function() {
+ previousOperatingMode = operatingMode;
+ operatingMode = 'OCI';
+
+ if(!switchMode(operatingMode)) {
+ operatingMode = previousOperatingMode;
+ $('#dropdown_'+ operatingMode.toLowerCase()).addClass('active');
+ }
+});
$('#importBtn').on('click', function() {
importConfig(JSON.parse($('#importExportArea').val()))
@@ -415,7 +432,21 @@ function subnet_usable_first(network, netSize, operatingMode) {
// AWS reserves 3 additional IPs
// https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-faq#are-there-any-restrictions-on-using-ip-addresses-within-these-subnets
// Azure reserves 3 additional IPs
- return network + (operatingMode == 'Standard' ? 1 : 4);
+ // https://docs.oracle.com/en-us/iaas/Content/Network/Concepts/overview.htm#Reserved__reserved_subnet
+ // OCI reserves 2 additional IPs
+ //return network + (operatingMode == 'Standard' ? 1 : 4);
+ switch (operatingMode) {
+ case 'AWS':
+ case 'AZURE':
+ return network + 4;
+ break;
+ case 'OCI':
+ return network + 2;
+ break;
+ default:
+ return network + 1;
+ break;
+ }
} else {
return network;
}
@@ -587,6 +618,9 @@ function mutate_subnet_map(verb, network, subnetTree, propValue = '') {
case 'AZURE':
var modal_error_message = 'The minimum IPv4 subnet size for Azure is /' + minSubnetSizes[operatingMode] + '.
More Information:
Azure Virtual Network FAQ > How small and how large can virtual networks and subnets be?'
break;
+ case 'OCI':
+ var modal_error_message = 'The minimum IPv4 subnet size for OCI is /' + minSubnetSizes[operatingMode] + '.
More Information:
Infrastructure Services>Networking>Networking Overview>Three IP Addresses in Each Subnet'
+ break;
default:
var modal_error_message = 'The minimum size for an IPv4 subnet is /' + minSubnetSizes[operatingMode] + '.
More Information:
Wikipedia - Classless Inter-Domain Routing'
break;
@@ -635,6 +669,9 @@ function switchMode(operatingMode) {
case 'AZURE':
var validate_error_message = 'Azure Mode - Smallest size is /' + minSubnetSizes[operatingMode]
break;
+ case 'OCI':
+ var validate_error_message = 'OCI Mode - Smallest size is /' + minSubnetSizes[operatingMode]
+ break;
default:
var validate_error_message = 'Smallest size is /' + minSubnetSizes[operatingMode]
break;
@@ -651,7 +688,7 @@ function switchMode(operatingMode) {
}
});
// Remove active class from all buttons if needed
- $('#dropdown_standard, #dropdown_azure, #dropdown_aws').removeClass('active');
+ $('#dropdown_standard, #dropdown_azure, #dropdown_aws, #dropdown_oci').removeClass('active');
$('#dropdown_' + operatingMode.toLowerCase()).addClass('active');
isSwitched = true;
} else {
@@ -662,6 +699,9 @@ function switchMode(operatingMode) {
case 'AZURE':
var modal_error_message = 'One or more subnets are smaller than the minimum allowed for Azure.
The smallest size allowed is /' + minSubnetSizes[operatingMode] + '.
See: Azure Virtual Network FAQ > How small and how large can virtual networks and subnets be?'
break;
+ case 'OCI':
+ var modal_error_message = 'One or more subnets are smaller than the minimum allowed for OCI.
The smallest size allowed is /' + minSubnetSizes[operatingMode] + '.
See: Infrastructure Services>Networking>Networking Overview>Three IP Addresses in Each Subnet'
+ break;
default:
var validate_error_message = 'Unknown Error'
break;
@@ -707,6 +747,9 @@ function set_usable_ips_title(operatingMode) {
case 'AZURE':
$('#useableHeader').html('Usable IPs (Azure)')
break;
+ case 'OCI':
+ $('#useableHeader').html('Usable IPs (OCI)')
+ break;
default:
$('#useableHeader').html('Usable IPs')
break;
diff --git a/src/tests/import-export.spec.ts b/src/tests/import-export.spec.ts
index b3a1207..34dd46f 100644
--- a/src/tests/import-export.spec.ts
+++ b/src/tests/import-export.spec.ts
@@ -29,6 +29,16 @@ test('Default (Azure) Export Content', async ({ page }) => {
await page.getByLabel('Import/Export', { exact: true }).getByText('Close').click();
});
+test('Default (OCI) Export Content', async ({ page }) => {
+ await page.goto('/');
+ await page.getByRole('button', { name: 'Tools' }).click();
+ await page.getByRole('link', { name: 'Mode - OCI' }).click();
+ await page.getByRole('button', { name: 'Tools' }).click();
+ await page.getByRole('link', { name: 'Import / Export' }).click();
+ await expect(page.getByLabel('Import/Export Content')).toHaveValue('{\n "config_version": "2",\n "operating_mode": "OCI",\n "base_network": "10.0.0.0/16",\n "subnets": {\n "10.0.0.0/16": {}\n }\n}');
+ //await page.getByLabel('Import/Export', { exact: true }).getByText('Close').click();
+});
+
test('Import 192.168.0.0/24', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Tools' }).click();
diff --git a/src/tests/subnet-basic.spec.ts b/src/tests/subnet-basic.spec.ts
index bcdea33..e638238 100644
--- a/src/tests/subnet-basic.spec.ts
+++ b/src/tests/subnet-basic.spec.ts
@@ -125,6 +125,13 @@ test('Usable IPs - Azure', async ({ page }) => {
await expect(page.getByLabel('10.0.0.0/16', { exact: true }).getByLabel('Usable IPs')).toContainText('10.0.0.4 - 10.0.255.254');
});
+test('Usable IPs - OCI', async ({ page }) => {
+ await page.goto('/');
+ await page.getByRole('button', { name: 'Tools' }).click();
+ await page.getByRole('link', { name: 'Mode - OCI' }).click();
+ await expect(page.getByLabel('10.0.0.0/16', { exact: true }).getByLabel('Usable IPs')).toContainText('10.0.0.2 - 10.0.255.254');
+});
+
test('Note Splitting', async ({ page }) => {
await page.goto('/');
await page.getByLabel('Note Split/Join').click();
diff --git a/src/tests/ui-error-handling.spec.ts b/src/tests/ui-error-handling.spec.ts
index a24c345..ddb7254 100644
--- a/src/tests/ui-error-handling.spec.ts
+++ b/src/tests/ui-error-handling.spec.ts
@@ -74,3 +74,15 @@ test('Subnet Too Small for Azure Mode', async ({ page }) => {
await expect(page.getByText('Azure Mode - Smallest size is /29')).toBeVisible();
});
+test('Subnet Too Small for OCI Mode', async ({ page }) => {
+ await page.goto('/');
+ await expect(page.locator('#useableHeader')).toContainText('Usable IPs');
+ await page.getByRole('button', { name: 'Tools' }).click();
+ await page.getByRole('link', { name: 'Mode - OCI' }).click();
+ await page.getByLabel('Network Size').click();
+ await page.getByLabel('Network Size').fill('31');
+ await page.getByRole('button', { name: 'Go' }).click();
+ await expect(page.locator('#notifyModalLabel')).toContainText('Warning!');
+ await expect(page.locator('#notifyModalDescription')).toContainText('Please correct the errors in the form!');
+ await expect(page.getByText('OCI Mode - Smallest size is /30')).toBeVisible();
+});
diff --git a/src/tests/ui-usage.spec.ts b/src/tests/ui-usage.spec.ts
index 76af731..7c862f8 100644
--- a/src/tests/ui-usage.spec.ts
+++ b/src/tests/ui-usage.spec.ts
@@ -76,6 +76,15 @@ test('Table Header Azure Mode', async ({ page }) => {
await expect(page.getByText('Azure reserves 5 addresses in')).toBeVisible();
});
+test('Table Header OCI Mode', async ({ page }) => {
+ await page.goto('/');
+ await expect(page.locator('#useableHeader')).toContainText('Usable IPs');
+ await page.getByRole('button', { name: 'Tools' }).click();
+ await page.getByRole('link', { name: 'Mode - OCI' }).click();
+ await expect(page.getByRole('cell', { name: 'Usable IPs', exact: true })).toContainText('Usable IPs (OCI)');
+ await page.getByRole('link', { name: 'OCI' }).hover()
+ await expect(page.getByText('OCI reserves 3 addresses in')).toBeVisible();
+});
test('Table Header AWS then Standard', async ({ page }) => {
await page.goto('/');