1 Commits

Author SHA1 Message Date
88e4e12e3d Merge pull request '2026-01-19-nah9' (#6) from 2026-01-19-nah9 into main
Some checks failed
CI/CD Pipeline / Build and Test (push) Failing after 34s
CI/CD Pipeline / Package Module (push) Has been skipped
CI/CD Pipeline / Create Release (push) Has been skipped
Reviewed-on: #6
2026-01-19 16:57:01 +03:00
48 changed files with 1922 additions and 1814 deletions

View File

@@ -26,11 +26,8 @@ jobs:
with: with:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
- name: Restore class library - name: Restore dependencies
run: dotnet restore classlib/classlib.csproj run: dotnet restore
- name: Restore test project
run: dotnet restore classlib.tests/classlib.tests.csproj
- name: Build class library - name: Build class library
run: dotnet build classlib/classlib.csproj --configuration Release --no-restore run: dotnet build classlib/classlib.csproj --configuration Release --no-restore
@@ -42,7 +39,7 @@ jobs:
run: dotnet test classlib.tests/classlib.tests.csproj --configuration Release --no-build --verbosity normal --logger "trx;LogFileName=test-results.trx" --collect:"XPlat Code Coverage" run: dotnet test classlib.tests/classlib.tests.csproj --configuration Release --no-build --verbosity normal --logger "trx;LogFileName=test-results.trx" --collect:"XPlat Code Coverage"
- name: Upload test results - name: Upload test results
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
if: always() if: always()
with: with:
name: test-results name: test-results
@@ -89,7 +86,7 @@ jobs:
cp -r images output/ps.ipam/ cp -r images output/ps.ipam/
- name: Upload module artifact - name: Upload module artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ps.ipam-module name: ps.ipam-module
path: output/ps.ipam/ path: output/ps.ipam/
@@ -102,7 +99,7 @@ jobs:
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
steps: steps:
- name: Download module artifact - name: Download module artifact
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: ps.ipam-module name: ps.ipam-module
path: ps.ipam path: ps.ipam
@@ -113,46 +110,12 @@ jobs:
tar -czvf ps.ipam-${{ github.ref_name }}.tar.gz ps.ipam/ tar -czvf ps.ipam-${{ github.ref_name }}.tar.gz ps.ipam/
- name: Create Gitea Release - name: Create Gitea Release
env: uses: actions/gitea-release-action@v1
GITEA_TOKEN: ${{ secrets.TOKEN }} with:
run: | token: ${{ secrets.TOKEN }}
# Determine if this is a prerelease (contains hyphen like v1.0.0-beta) files: |
PRERELEASE=false ps.ipam-${{ github.ref_name }}.zip
if [[ "${{ github.ref_name }}" == *-* ]]; then ps.ipam-${{ github.ref_name }}.tar.gz
PRERELEASE=true title: Release ${{ github.ref_name }}
fi draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
# Create the release
RELEASE_RESPONSE=$(curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${{ github.ref_name }}\", \"name\": \"Release ${{ github.ref_name }}\", \"body\": \"Release ${{ github.ref_name }}\", \"draft\": false, \"prerelease\": ${PRERELEASE}}" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases")
echo "Release response: ${RELEASE_RESPONSE}"
# Extract release ID
RELEASE_ID=$(echo "${RELEASE_RESPONSE}" | jq -r '.id')
if [ "${RELEASE_ID}" == "null" ] || [ -z "${RELEASE_ID}" ]; then
echo "Failed to create release"
exit 1
fi
echo "Created release with ID: ${RELEASE_ID}"
# Upload ZIP attachment
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: multipart/form-data" \
-F "attachment=@ps.ipam-${{ github.ref_name }}.zip" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${RELEASE_ID}/assets"
# Upload tar.gz attachment
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: multipart/form-data" \
-F "attachment=@ps.ipam-${{ github.ref_name }}.tar.gz" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${RELEASE_ID}/assets"
echo "Release created successfully with attachments"

View File

@@ -109,13 +109,6 @@ public class GetAddressCmdletTests : IDisposable
cmdlet.SubnetCIDR.Should().Be("192.168.1.0/24"); cmdlet.SubnetCIDR.Should().Be("192.168.1.0/24");
} }
[Fact]
public void GetAddressCmdlet_InheritsFromBaseCmdlet()
{
var cmdlet = new GetAddressCmdlet();
cmdlet.Should().BeAssignableTo<BaseCmdlet>();
}
// Test the underlying RequestHelper functionality that the cmdlet uses // Test the underlying RequestHelper functionality that the cmdlet uses
[Fact] [Fact]
@@ -136,8 +129,8 @@ public class GetAddressCmdletTests : IDisposable
// Act // Act
var result = await RequestHelper.InvokeRequest( var result = await RequestHelper.InvokeRequest(
"GET", "GET",
ApiController.Addresses, controllers.addresses,
ModelType.Address, types.Address,
null, null,
null, null,
new[] { "1" } new[] { "1" }
@@ -162,8 +155,8 @@ public class GetAddressCmdletTests : IDisposable
// Act // Act
await RequestHelper.InvokeRequest( await RequestHelper.InvokeRequest(
"GET", "GET",
ApiController.Addresses, controllers.addresses,
ModelType.Address, types.Address,
null, null,
null, null,
new[] { "42" } new[] { "42" }
@@ -184,8 +177,8 @@ public class GetAddressCmdletTests : IDisposable
// Act // Act
await RequestHelper.InvokeRequest( await RequestHelper.InvokeRequest(
"GET", "GET",
ApiController.Addresses, controllers.addresses,
ModelType.Address, types.Address,
null, null,
null, null,
new[] { "search", "192.168.1.50" } new[] { "search", "192.168.1.50" }
@@ -206,8 +199,8 @@ public class GetAddressCmdletTests : IDisposable
// Act // Act
await RequestHelper.InvokeRequest( await RequestHelper.InvokeRequest(
"GET", "GET",
ApiController.Addresses, controllers.addresses,
ModelType.Address, types.Address,
null, null,
null, null,
new[] { "search_hostname", "myserver.example.com" } new[] { "search_hostname", "myserver.example.com" }
@@ -228,8 +221,8 @@ public class GetAddressCmdletTests : IDisposable
// Act // Act
await RequestHelper.InvokeRequest( await RequestHelper.InvokeRequest(
"GET", "GET",
ApiController.Subnets, controllers.subnets,
ModelType.Address, types.Address,
null, null,
null, null,
new[] { "10", "addresses" } new[] { "10", "addresses" }
@@ -250,8 +243,8 @@ public class GetAddressCmdletTests : IDisposable
// Act // Act
await RequestHelper.InvokeRequest( await RequestHelper.InvokeRequest(
"GET", "GET",
ApiController.Addresses, controllers.addresses,
ModelType.Address, types.Address,
null, null,
null, null,
new[] { "tags", "2", "addresses" } new[] { "tags", "2", "addresses" }
@@ -276,8 +269,8 @@ public class GetAddressCmdletTests : IDisposable
// Act // Act
var result = await RequestHelper.InvokeRequest( var result = await RequestHelper.InvokeRequest(
"GET", "GET",
ApiController.Subnets, controllers.subnets,
ModelType.Address, types.Address,
null, null,
null, null,
new[] { "10", "addresses" } new[] { "10", "addresses" }
@@ -301,8 +294,8 @@ public class GetAddressCmdletTests : IDisposable
// Act // Act
var result = await RequestHelper.InvokeRequest( var result = await RequestHelper.InvokeRequest(
"GET", "GET",
ApiController.Addresses, controllers.addresses,
ModelType.Address, types.Address,
null, null,
null, null,
new[] { "999" } new[] { "999" }
@@ -320,11 +313,11 @@ public class GetAddressCmdletTests : IDisposable
// Act // Act
var action = async () => await RequestHelper.InvokeRequest( var action = async () => await RequestHelper.InvokeRequest(
"GET", "GET",
ApiController.Addresses, controllers.addresses,
ModelType.Address types.Address
); );
// Assert // Assert
await action.Should().ThrowAsync<InvalidOperationException>().WithMessage("No session available!"); await action.Should().ThrowAsync<Exception>().WithMessage("No session available!");
} }
} }

View File

@@ -93,18 +93,11 @@ public class NewSessionCmdletTests : IDisposable
session.URL.Should().Be("https://ipam.example.com"); session.URL.Should().Be("https://ipam.example.com");
session.AppID.Should().Be("testapp"); session.AppID.Should().Be("testapp");
session.Token.Should().Be("my-api-token"); session.Token.Should().Be("my-api-token");
session.AuthType.Should().Be(AuthType.Token); session.AuthType.Should().Be(AuthType.token);
session.Expires.Should().BeNull(); session.Expires.Should().BeNull();
session.Credentials.Should().BeNull(); session.Credentials.Should().BeNull();
// Verify it was set as current session // Verify it was set as current session
SessionManager.CurrentSession.Should().BeSameAs(session); SessionManager.CurrentSession.Should().BeSameAs(session);
} }
[Fact]
public void NewSessionCmdlet_InheritsFromBaseCmdlet()
{
var cmdlet = new NewSessionCmdlet();
cmdlet.Should().BeAssignableTo<BaseCmdlet>();
}
} }

View File

@@ -27,9 +27,9 @@ public class RequestHelperTests : IDisposable
_mockHandler?.ForceDispose(); _mockHandler?.ForceDispose();
} }
private void SetupSession(AuthType authType = AuthType.Token) private void SetupSession(AuthType authType = AuthType.token)
{ {
if (authType == AuthType.Token) if (authType == AuthType.token)
{ {
SessionManager.CreateSessionWithToken("https://ipam.example.com", "testapp", "test-token"); SessionManager.CreateSessionWithToken("https://ipam.example.com", "testapp", "test-token");
} }
@@ -37,7 +37,7 @@ public class RequestHelperTests : IDisposable
{ {
// For credentials auth, we need to set up a session manually with future expiry // For credentials auth, we need to set up a session manually with future expiry
var session = new Session( var session = new Session(
AuthType.Credentials, AuthType.credentials,
"cred-token", "cred-token",
"testapp", "testapp",
"https://ipam.example.com", "https://ipam.example.com",
@@ -61,10 +61,10 @@ public class RequestHelperTests : IDisposable
// Arrange - no session set up // Arrange - no session set up
// Act // Act
var action = async () => await RequestHelper.InvokeRequest("GET", ApiController.Addresses); var action = async () => await RequestHelper.InvokeRequest("GET", controllers.addresses);
// Assert // Assert
await action.Should().ThrowAsync<InvalidOperationException>().WithMessage("No session available!"); await action.Should().ThrowAsync<Exception>().WithMessage("No session available!");
} }
[Fact] [Fact]
@@ -76,7 +76,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse("{\"id\":1,\"ip\":\"192.168.1.1\"}"); handler.WithSuccessResponse("{\"id\":1,\"ip\":\"192.168.1.1\"}");
// Act // Act
await RequestHelper.InvokeRequest("GET", ApiController.Addresses, ModelType.Address, null, null, new[] { "1" }); await RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, new[] { "1" });
// Assert // Assert
handler.LastRequest.Should().NotBeNull(); handler.LastRequest.Should().NotBeNull();
@@ -92,7 +92,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse("[]"); handler.WithSuccessResponse("[]");
// Act // Act
await RequestHelper.InvokeRequest("GET", ApiController.Subnets, null, ApiSubController.Tags, null, new[] { "1" }); await RequestHelper.InvokeRequest("GET", controllers.subnets, null, subcontrollers.tags, null, new[] { "1" });
// Assert // Assert
handler.LastRequest!.RequestUri!.ToString().Should().Contain("/subnets/tags/1/"); handler.LastRequest!.RequestUri!.ToString().Should().Contain("/subnets/tags/1/");
@@ -102,12 +102,12 @@ public class RequestHelperTests : IDisposable
public async Task InvokeRequest_WithTokenAuth_AddsPhpipamTokenHeader() public async Task InvokeRequest_WithTokenAuth_AddsPhpipamTokenHeader()
{ {
// Arrange // Arrange
SetupSession(AuthType.Token); SetupSession(AuthType.token);
var handler = SetupMockHandler(); var handler = SetupMockHandler();
handler.WithSuccessResponse("{}"); handler.WithSuccessResponse("{}");
// Act // Act
await RequestHelper.InvokeRequest("GET", ApiController.Addresses); await RequestHelper.InvokeRequest("GET", controllers.addresses);
// Assert // Assert
handler.GetLastRequestHeader("phpipam-token").Should().Be("test-token"); handler.GetLastRequestHeader("phpipam-token").Should().Be("test-token");
@@ -117,12 +117,12 @@ public class RequestHelperTests : IDisposable
public async Task InvokeRequest_WithCredentialsAuth_AddsTokenHeader() public async Task InvokeRequest_WithCredentialsAuth_AddsTokenHeader()
{ {
// Arrange // Arrange
SetupSession(AuthType.Credentials); SetupSession(AuthType.credentials);
var handler = SetupMockHandler(); var handler = SetupMockHandler();
handler.WithSuccessResponse("{}"); handler.WithSuccessResponse("{}");
// Act // Act
await RequestHelper.InvokeRequest("GET", ApiController.Addresses); await RequestHelper.InvokeRequest("GET", controllers.addresses);
// Assert // Assert
handler.GetLastRequestHeader("token").Should().Be("cred-token"); handler.GetLastRequestHeader("token").Should().Be("cred-token");
@@ -137,7 +137,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse("{}"); handler.WithSuccessResponse("{}");
// Act // Act
await RequestHelper.InvokeRequest("GET", ApiController.Addresses); await RequestHelper.InvokeRequest("GET", controllers.addresses);
// Assert // Assert
handler.LastRequest!.Method.Should().Be(HttpMethod.Get); handler.LastRequest!.Method.Should().Be(HttpMethod.Get);
@@ -152,7 +152,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse("{\"id\":1}"); handler.WithSuccessResponse("{\"id\":1}");
// Act // Act
await RequestHelper.InvokeRequest("POST", ApiController.Addresses, null, null, new { ip = "10.0.0.1" }); await RequestHelper.InvokeRequest("POST", controllers.addresses, null, null, new { ip = "10.0.0.1" });
// Assert // Assert
handler.LastRequest!.Method.Should().Be(HttpMethod.Post); handler.LastRequest!.Method.Should().Be(HttpMethod.Post);
@@ -167,7 +167,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse("{\"id\":1}"); handler.WithSuccessResponse("{\"id\":1}");
// Act // Act
await RequestHelper.InvokeRequest("PATCH", ApiController.Addresses, null, null, new { description = "updated" }, new[] { "1" }); await RequestHelper.InvokeRequest("PATCH", controllers.addresses, null, null, new { description = "updated" }, new[] { "1" });
// Assert // Assert
handler.LastRequest!.Method.Should().Be(new HttpMethod("PATCH")); handler.LastRequest!.Method.Should().Be(new HttpMethod("PATCH"));
@@ -182,7 +182,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse("{}"); handler.WithSuccessResponse("{}");
// Act // Act
await RequestHelper.InvokeRequest("DELETE", ApiController.Addresses, null, null, null, new[] { "1" }); await RequestHelper.InvokeRequest("DELETE", controllers.addresses, null, null, null, new[] { "1" });
// Assert // Assert
handler.LastRequest!.Method.Should().Be(HttpMethod.Delete); handler.LastRequest!.Method.Should().Be(HttpMethod.Delete);
@@ -197,7 +197,7 @@ public class RequestHelperTests : IDisposable
handler.WithNotFoundResponse(); handler.WithNotFoundResponse();
// Act // Act
var result = await RequestHelper.InvokeRequest("GET", ApiController.Addresses, ModelType.Address, null, null, new[] { "999" }); var result = await RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, new[] { "999" });
// Assert // Assert
result.Should().BeNull(); result.Should().BeNull();
@@ -212,7 +212,7 @@ public class RequestHelperTests : IDisposable
handler.WithErrorResponse(HttpStatusCode.InternalServerError, "Server error"); handler.WithErrorResponse(HttpStatusCode.InternalServerError, "Server error");
// Act // Act
var action = async () => await RequestHelper.InvokeRequest("GET", ApiController.Addresses); var action = async () => await RequestHelper.InvokeRequest("GET", controllers.addresses);
// Assert // Assert
await action.Should().ThrowAsync<HttpRequestException>(); await action.Should().ThrowAsync<HttpRequestException>();
@@ -249,7 +249,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse(addressJson); handler.WithSuccessResponse(addressJson);
// Act // Act
var result = await RequestHelper.InvokeRequest("GET", ApiController.Addresses, ModelType.Address, null, null, new[] { "1" }); var result = await RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, new[] { "1" });
// Assert // Assert
result.Should().BeOfType<Address>(); result.Should().BeOfType<Address>();
@@ -272,7 +272,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse(addressArrayJson); handler.WithSuccessResponse(addressArrayJson);
// Act // Act
var result = await RequestHelper.InvokeRequest("GET", ApiController.Subnets, ModelType.Address, null, null, new[] { "10", "addresses" }); var result = await RequestHelper.InvokeRequest("GET", controllers.subnets, types.Address, null, null, new[] { "10", "addresses" });
// Assert // Assert
result.Should().BeAssignableTo<IEnumerable<object>>(); result.Should().BeAssignableTo<IEnumerable<object>>();
@@ -300,7 +300,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse(vlanJson); handler.WithSuccessResponse(vlanJson);
// Act // Act
var result = await RequestHelper.InvokeRequest("GET", ApiController.Vlan, ModelType.Vlan, null, null, new[] { "100" }); var result = await RequestHelper.InvokeRequest("GET", controllers.vlan, types.Vlan, null, null, new[] { "100" });
// Assert // Assert
result.Should().BeOfType<Vlan>(); result.Should().BeOfType<Vlan>();
@@ -351,14 +351,14 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse(subnetJson); handler.WithSuccessResponse(subnetJson);
// Act // Act
var result = await RequestHelper.InvokeRequest("GET", ApiController.Subnets, ModelType.Subnetwork, null, null, new[] { "1" }); var result = await RequestHelper.InvokeRequest("GET", controllers.subnets, types.Subnetwork, null, null, new[] { "1" });
// Assert // Assert
result.Should().BeOfType<Subnetwork>(); result.Should().BeOfType<Subnetwork>();
var subnet = (Subnetwork)result!; var subnet = (Subnetwork)result!;
subnet.Subnet.Should().Be("192.168.1.0"); subnet.Subnet.Should().Be("192.168.1.0");
subnet.Mask.Should().Be(24); subnet.Mask.Should().Be(24);
subnet.CIDR.Should().Be("192.168.1.0/24"); subnet.GetCIDR().Should().Be("192.168.1.0/24");
} }
[Fact] [Fact]
@@ -386,7 +386,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse(sectionJson); handler.WithSuccessResponse(sectionJson);
// Act // Act
var result = await RequestHelper.InvokeRequest("GET", ApiController.Sections, ModelType.Section, null, null, new[] { "1" }); var result = await RequestHelper.InvokeRequest("GET", controllers.sections, types.Section, null, null, new[] { "1" });
// Assert // Assert
result.Should().BeOfType<Section>(); result.Should().BeOfType<Section>();
@@ -411,12 +411,12 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse(addressJson); handler.WithSuccessResponse(addressJson);
// Act // Act
var result = await RequestHelper.InvokeRequest("GET", ApiController.Addresses, ModelType.Address, null, null, new[] { "1" }); var result = await RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, new[] { "1" });
// Assert // Assert
var address = (Address)result!; var address = (Address)result!;
address.ExtendedData.Should().NotBeNull(); address.ExtendedData.Should().NotBeNull();
var extendedData = address.ExtendedData!; var extendedData = (Dictionary<string, object>)address.ExtendedData!;
extendedData.Should().ContainKey("custom_environment"); extendedData.Should().ContainKey("custom_environment");
extendedData.Should().ContainKey("custom_owner"); extendedData.Should().ContainKey("custom_owner");
} }
@@ -430,7 +430,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse(@"{""some"": ""data""}"); handler.WithSuccessResponse(@"{""some"": ""data""}");
// Act // Act
var result = await RequestHelper.InvokeRequest("GET", ApiController.Tools); var result = await RequestHelper.InvokeRequest("GET", controllers.tools);
// Assert // Assert
result.Should().NotBeNull(); result.Should().NotBeNull();
@@ -447,7 +447,7 @@ public class RequestHelperTests : IDisposable
var parameters = new { ip = "10.0.0.1", subnetId = 5, description = "New address" }; var parameters = new { ip = "10.0.0.1", subnetId = 5, description = "New address" };
// Act // Act
await RequestHelper.InvokeRequest("POST", ApiController.Addresses, null, null, parameters); await RequestHelper.InvokeRequest("POST", controllers.addresses, null, null, parameters);
// Assert // Assert
handler.LastRequest!.Content.Should().NotBeNull(); handler.LastRequest!.Content.Should().NotBeNull();
@@ -465,7 +465,7 @@ public class RequestHelperTests : IDisposable
handler.WithSuccessResponse("{}"); handler.WithSuccessResponse("{}");
// Act // Act
await RequestHelper.InvokeRequest("GET", ApiController.Addresses); await RequestHelper.InvokeRequest("GET", controllers.addresses);
// Assert // Assert
handler.LastRequest!.Headers.Accept.Should().Contain(h => h.MediaType == "application/json"); handler.LastRequest!.Headers.Accept.Should().Contain(h => h.MediaType == "application/json");
@@ -480,7 +480,7 @@ public class RequestHelperTests : IDisposable
handler.WithResponse(HttpStatusCode.OK, "", "application/json"); handler.WithResponse(HttpStatusCode.OK, "", "application/json");
// Act // Act
var result = await RequestHelper.InvokeRequest("GET", ApiController.Addresses); var result = await RequestHelper.InvokeRequest("GET", controllers.addresses);
// Assert // Assert
result.Should().BeNull(); result.Should().BeNull();
@@ -495,7 +495,7 @@ public class RequestHelperTests : IDisposable
handler.WithJsonResponse(@"{""code"": 200, ""success"": true, ""data"": null}"); handler.WithJsonResponse(@"{""code"": 200, ""success"": true, ""data"": null}");
// Act // Act
var result = await RequestHelper.InvokeRequest("GET", ApiController.Addresses, ModelType.Address); var result = await RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address);
// Assert // Assert
result.Should().BeNull(); result.Should().BeNull();

View File

@@ -21,60 +21,60 @@ public class SessionManagerTests : IDisposable
} }
[Fact] [Fact]
public void GetSessionStatus_WhenNoSession_ReturnsNoSession() public void TestSession_WhenNoSession_ReturnsNoToken()
{ {
// Arrange // Arrange
SessionManager.CurrentSession = null; SessionManager.CurrentSession = null;
// Act // Act
var result = SessionManager.GetSessionStatus(); var result = SessionManager.TestSession();
// Assert // Assert
result.Should().Be(SessionStatus.NoSession); result.Should().Be("NoToken");
} }
[Fact] [Fact]
public void GetSessionStatus_WhenSessionWithNullExpires_ReturnsValid() public void TestSession_WhenSessionWithNullExpires_ReturnsValid()
{ {
// Arrange // Arrange
var session = new Session(AuthType.Token, "test-token", "app", "https://test.com", null, null); var session = new Session(AuthType.token, "test-token", "app", "https://test.com", null, null);
SessionManager.CurrentSession = session; SessionManager.CurrentSession = session;
// Act // Act
var result = SessionManager.GetSessionStatus(); var result = SessionManager.TestSession();
// Assert // Assert
result.Should().Be(SessionStatus.Valid); result.Should().Be("Valid");
} }
[Fact] [Fact]
public void GetSessionStatus_WhenSessionNotExpired_ReturnsValid() public void TestSession_WhenSessionNotExpired_ReturnsValid()
{ {
// Arrange // Arrange
var futureExpiry = DateTime.Now.AddHours(1); var futureExpiry = DateTime.Now.AddHours(1);
var session = new Session(AuthType.Credentials, "test-token", "app", "https://test.com", futureExpiry, null); var session = new Session(AuthType.credentials, "test-token", "app", "https://test.com", futureExpiry, null);
SessionManager.CurrentSession = session; SessionManager.CurrentSession = session;
// Act // Act
var result = SessionManager.GetSessionStatus(); var result = SessionManager.TestSession();
// Assert // Assert
result.Should().Be(SessionStatus.Valid); result.Should().Be("Valid");
} }
[Fact] [Fact]
public void GetSessionStatus_WhenSessionExpired_ReturnsExpired() public void TestSession_WhenSessionExpired_ReturnsExpired()
{ {
// Arrange // Arrange
var pastExpiry = DateTime.Now.AddHours(-1); var pastExpiry = DateTime.Now.AddHours(-1);
var session = new Session(AuthType.Credentials, "test-token", "app", "https://test.com", pastExpiry, null); var session = new Session(AuthType.credentials, "test-token", "app", "https://test.com", pastExpiry, null);
SessionManager.CurrentSession = session; SessionManager.CurrentSession = session;
// Act // Act
var result = SessionManager.GetSessionStatus(); var result = SessionManager.TestSession();
// Assert // Assert
result.Should().Be(SessionStatus.Expired); result.Should().Be("Expired");
} }
[Fact] [Fact]
@@ -93,7 +93,7 @@ public class SessionManagerTests : IDisposable
session.URL.Should().Be(url); session.URL.Should().Be(url);
session.AppID.Should().Be(appId); session.AppID.Should().Be(appId);
session.Token.Should().Be(token); session.Token.Should().Be(token);
session.AuthType.Should().Be(AuthType.Token); session.AuthType.Should().Be(AuthType.token);
session.Expires.Should().BeNull(); session.Expires.Should().BeNull();
session.Credentials.Should().BeNull(); session.Credentials.Should().BeNull();
} }
@@ -144,7 +144,7 @@ public class SessionManagerTests : IDisposable
public void CurrentSession_CanBeSetDirectly() public void CurrentSession_CanBeSetDirectly()
{ {
// Arrange // Arrange
var session = new Session(AuthType.Token, "token", "app", "https://test.com", null, null); var session = new Session(AuthType.token, "token", "app", "https://test.com", null, null);
// Act // Act
SessionManager.CurrentSession = session; SessionManager.CurrentSession = session;

View File

@@ -55,7 +55,7 @@ public class AddressTests
address.Note.Should().Be(note); address.Note.Should().Be(note);
address.LastSeen.Should().Be(lastSeen); address.LastSeen.Should().Be(lastSeen);
address.ExcludePing.Should().Be(excludePing); address.ExcludePing.Should().Be(excludePing);
address.PTRIgnore.Should().Be(ptrIgnore); address.PTRignore.Should().Be(ptrIgnore);
address.PTR.Should().Be(ptr); address.PTR.Should().Be(ptr);
address.FirewallAddressObject.Should().Be(firewallObject); address.FirewallAddressObject.Should().Be(firewallObject);
address.EditDate.Should().Be(editDate); address.EditDate.Should().Be(editDate);
@@ -113,24 +113,4 @@ public class AddressTests
// Act & Assert // Act & Assert
address.ToString().Should().Be(expectedIp); address.ToString().Should().Be(expectedIp);
} }
[Fact]
public void Record_Equality_WorksCorrectly()
{
// Arrange
var address1 = new Address(
1, 10, "192.168.1.1", false, "Test", "host", "", "",
0, 0, "", "", "", null, false,
false, 0, "", null, 0, null
);
var address2 = new Address(
1, 10, "192.168.1.1", false, "Test", "host", "", "",
0, 0, "", "", "", null, false,
false, 0, "", null, 0, null
);
// Assert
address1.Should().Be(address2);
}
} }

View File

@@ -57,7 +57,7 @@ public class NameserverTests
} }
[Fact] [Fact]
public void Constructor_WithEmptyNameservers_ReturnsEmptyArray() public void Constructor_WithEmptyNameservers_ReturnsArrayWithEmptyString()
{ {
// Arrange // Arrange
var nameServersString = ""; var nameServersString = "";
@@ -65,8 +65,9 @@ public class NameserverTests
// Act // Act
var nameserver = new Nameserver(1, "Test", nameServersString, "", "", null); var nameserver = new Nameserver(1, "Test", nameServersString, "", "", null);
// Assert - Empty entries are excluded // Assert
nameserver.NameServers.Should().BeEmpty(); nameserver.NameServers.Should().HaveCount(1);
nameserver.NameServers[0].Should().BeEmpty();
} }
[Fact] [Fact]
@@ -78,27 +79,4 @@ public class NameserverTests
// Assert // Assert
nameserver.EditDate.Should().BeNull(); nameserver.EditDate.Should().BeNull();
} }
[Fact]
public void Constructor_WithSemicolonOnlyString_ReturnsEmptyArray()
{
// Arrange - edge case with just semicolons
var nameServersString = ";;;";
// Act
var nameserver = new Nameserver(1, "Test", nameServersString, "", "", null);
// Assert
nameserver.NameServers.Should().BeEmpty();
}
[Fact]
public void ToString_ReturnsName()
{
// Arrange
var nameserver = new Nameserver(1, "Google DNS", "8.8.8.8", "", "", null);
// Act & Assert
nameserver.ToString().Should().Be("Google DNS");
}
} }

View File

@@ -10,7 +10,7 @@ public class SessionTests
public void Constructor_WithCredentialsAuth_SetsAllProperties() public void Constructor_WithCredentialsAuth_SetsAllProperties()
{ {
// Arrange // Arrange
var authType = AuthType.Credentials; var authType = AuthType.credentials;
var token = "test-token-123"; var token = "test-token-123";
var appId = "myApp"; var appId = "myApp";
var url = "https://ipam.example.com"; var url = "https://ipam.example.com";
@@ -21,7 +21,7 @@ public class SessionTests
var session = new Session(authType, token, appId, url, expires, credentials); var session = new Session(authType, token, appId, url, expires, credentials);
// Assert // Assert
session.AuthType.Should().Be(AuthType.Credentials); session.AuthType.Should().Be(AuthType.credentials);
session.Token.Should().Be(token); session.Token.Should().Be(token);
session.AppID.Should().Be(appId); session.AppID.Should().Be(appId);
session.URL.Should().Be(url); session.URL.Should().Be(url);
@@ -33,7 +33,7 @@ public class SessionTests
public void Constructor_WithTokenAuth_SetsAllProperties() public void Constructor_WithTokenAuth_SetsAllProperties()
{ {
// Arrange // Arrange
var authType = AuthType.Token; var authType = AuthType.token;
var token = "static-api-token"; var token = "static-api-token";
var appId = "apiApp"; var appId = "apiApp";
var url = "https://ipam.test.com"; var url = "https://ipam.test.com";
@@ -42,7 +42,7 @@ public class SessionTests
var session = new Session(authType, token, appId, url, null, null); var session = new Session(authType, token, appId, url, null, null);
// Assert // Assert
session.AuthType.Should().Be(AuthType.Token); session.AuthType.Should().Be(AuthType.token);
session.Token.Should().Be(token); session.Token.Should().Be(token);
session.AppID.Should().Be(appId); session.AppID.Should().Be(appId);
session.URL.Should().Be(url); session.URL.Should().Be(url);
@@ -54,7 +54,7 @@ public class SessionTests
public void Token_CanBeModified() public void Token_CanBeModified()
{ {
// Arrange // Arrange
var session = new Session(AuthType.Credentials, "old-token", "app", "https://test.com", null, null); var session = new Session(AuthType.credentials, "old-token", "app", "https://test.com", null, null);
// Act // Act
session.Token = "new-token"; session.Token = "new-token";
@@ -67,7 +67,7 @@ public class SessionTests
public void Expires_CanBeModified() public void Expires_CanBeModified()
{ {
// Arrange // Arrange
var session = new Session(AuthType.Credentials, "token", "app", "https://test.com", null, null); var session = new Session(AuthType.credentials, "token", "app", "https://test.com", null, null);
var newExpiry = new DateTime(2027, 1, 1); var newExpiry = new DateTime(2027, 1, 1);
// Act // Act
@@ -78,13 +78,15 @@ public class SessionTests
} }
[Fact] [Fact]
public void Record_Equality_WorksForSameValues() public void ToString_ReturnsDefaultObjectString()
{ {
// Arrange // Arrange
var session1 = new Session(AuthType.Token, "token", "app", "https://test.com", null, null); var session = new Session(AuthType.token, "token", "app", "https://test.com", null, null);
var session2 = new Session(AuthType.Token, "token", "app", "https://test.com", null, null);
// Assert - Records use value equality // Act
session1.Should().Be(session2); var result = session.ToString();
// Assert
result.Should().Contain("PS.IPAM.Session");
} }
} }

View File

@@ -93,13 +93,13 @@ public class SubnetworkTests
[InlineData("10.0.0.0", 8, "10.0.0.0/8")] [InlineData("10.0.0.0", 8, "10.0.0.0/8")]
[InlineData("172.16.0.0", 16, "172.16.0.0/16")] [InlineData("172.16.0.0", 16, "172.16.0.0/16")]
[InlineData("192.168.100.0", 30, "192.168.100.0/30")] [InlineData("192.168.100.0", 30, "192.168.100.0/30")]
public void CIDR_ReturnsCidrNotation(string subnet, int mask, string expectedCidr) public void GetCIDR_ReturnsCidrNotation(string subnet, int mask, string expectedCidr)
{ {
// Arrange // Arrange
var subnetwork = CreateSubnetwork(subnet, mask); var subnetwork = CreateSubnetwork(subnet, mask);
// Act // Act
var result = subnetwork.CIDR; var result = subnetwork.GetCIDR();
// Assert // Assert
result.Should().Be(expectedCidr); result.Should().Be(expectedCidr);
@@ -119,31 +119,16 @@ public class SubnetworkTests
} }
[Fact] [Fact]
public void ToString_EqualsCIDRProperty() public void ToString_EqualsGetCIDR()
{ {
// Arrange // Arrange
var subnetwork = CreateSubnetwork("172.20.0.0", 12); var subnetwork = CreateSubnetwork("172.20.0.0", 12);
// Act & Assert // Act & Assert
subnetwork.ToString().Should().Be(subnetwork.CIDR); subnetwork.ToString().Should().Be(subnetwork.GetCIDR());
} }
[Fact] private static Subnetwork CreateSubnetwork(string subnet, int mask)
public void Record_Equality_WorksCorrectly()
{
// Arrange - use same calculation object for equality comparison
var calculation = new { maxhosts = 254 };
var subnet1 = CreateSubnetwork("192.168.1.0", 24, calculation);
var subnet2 = CreateSubnetwork("192.168.1.0", 24, calculation);
// Assert - records use value equality (except for the Calculation object reference)
subnet1.Id.Should().Be(subnet2.Id);
subnet1.Subnet.Should().Be(subnet2.Subnet);
subnet1.Mask.Should().Be(subnet2.Mask);
subnet1.CIDR.Should().Be(subnet2.CIDR);
}
private static Subnetwork CreateSubnetwork(string subnet, int mask, object? calculation = null)
{ {
return new Subnetwork( return new Subnetwork(
1, subnet, mask, 1, "", "", "", 1, subnet, mask, 1, "", "", "",
@@ -151,7 +136,7 @@ public class SubnetworkTests
"", false, false, false, false, "", false, false, false, false,
false, 0, false, false, false, false, false, 0, false, false, false, false,
0, 0, 0, null, null, null, 0, 0, 0, null, null, null,
calculation ?? new object(), null new object(), null
); );
} }
} }

View File

@@ -26,13 +26,28 @@ public class TagTests
tag.Id.Should().Be(id); tag.Id.Should().Be(id);
tag.Type.Should().Be(type); tag.Type.Should().Be(type);
tag.ShowTag.Should().Be(showTag); tag.ShowTag.Should().Be(showTag);
tag.BackgroundColor.Should().Be(bgColor); tag.BGColor.Should().Be(bgColor);
tag.ForegroundColor.Should().Be(fgColor); tag.FGColor.Should().Be(fgColor);
tag.Compress.Should().Be(compress); tag.Compress.Should().BeTrue();
tag.Locked.Should().Be(locked); tag.Locked.Should().BeFalse();
tag.UpdateTag.Should().Be(updateTag); tag.UpdateTag.Should().Be(updateTag);
} }
[Theory]
[InlineData("Yes", true)]
[InlineData("No", false)]
[InlineData("", false)]
[InlineData("yes", false)] // Case sensitive
[InlineData("true", false)] // Only "Yes" is true
public void StringToBool_ConvertsCorrectly(string input, bool expected)
{
// The StringToBool is private, so we test through the constructor
// Using Compress field which uses StringToBool
var tag = new Tag(1, "Test", false, "", "", input, "No", false);
tag.Compress.Should().Be(expected);
}
[Theory] [Theory]
[InlineData("Used")] [InlineData("Used")]
[InlineData("Available")] [InlineData("Available")]
@@ -51,13 +66,12 @@ public class TagTests
} }
[Fact] [Fact]
public void Record_Equality_WorksCorrectly() public void Locked_WithYes_IsTrue()
{ {
// Arrange // Arrange & Act
var tag1 = new Tag(1, "Used", true, "#fff", "#000", "Yes", "No", false); var tag = new Tag(1, "Test", false, "", "", "No", "Yes", false);
var tag2 = new Tag(1, "Used", true, "#fff", "#000", "Yes", "No", false);
// Assert // Assert
tag1.Should().Be(tag2); tag.Locked.Should().BeTrue();
} }
} }

View File

@@ -1,30 +1,24 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary> [Cmdlet("Assign", "Tag")]
/// Assigns a tag to an address in phpIPAM. public class AssignTagCmdlet : PSCmdlet
/// </summary>
[Cmdlet("Assign", "Tag", SupportsShouldProcess = true)]
[OutputType(typeof(Address))]
public class AssignTagCmdlet : BaseCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0)]
HelpMessage = "The address to assign the tag to.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public Address? AddressObject { get; set; } public Address? AddressObject { get; set; }
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
Position = 1, Position = 1)]
HelpMessage = "The tag to assign.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public Tag? Tag { get; set; } public Tag? Tag { get; set; }
@@ -32,25 +26,14 @@ public class AssignTagCmdlet : BaseCmdlet
{ {
try try
{ {
var identifiers = new[] { AddressObject!.Id.ToString() }; var identifiers = new List<string> { AddressObject!.Id.ToString() };
var body = new Dictionary<string, object> var body = new Dictionary<string, object>
{ {
["tag"] = Tag!.Id { "tag", Tag!.Id }
}; };
if (ShouldProcess($"Address {AddressObject.Ip}", $"Assign tag '{Tag.Type}'")) RequestHelper.InvokeRequest("PATCH", controllers.addresses, null, null, body, identifiers.ToArray())
{ .GetAwaiter().GetResult();
RequestHelper.InvokeRequest(
"PATCH", ApiController.Addresses, null, null, body, identifiers
).GetAwaiter().GetResult();
// Return the updated address
var address = RequestHelper.InvokeRequest(
"GET", ApiController.Addresses, ModelType.Address, null, null, identifiers
).GetAwaiter().GetResult();
WriteResult(address);
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,114 +0,0 @@
namespace PS.IPAM.Cmdlets;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Management.Automation;
using PS.IPAM.Helpers;
/// <summary>
/// Base class for all PS.IPAM cmdlets providing common functionality.
/// </summary>
public abstract class BaseCmdlet : PSCmdlet
{
/// <summary>
/// Writes the result to the pipeline, handling both single objects and collections.
/// </summary>
/// <param name="result">The result to write to the pipeline.</param>
protected void WriteResult(object? result)
{
if (result == null)
{
return;
}
if (result is IEnumerable enumerable && result is not string)
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(result);
}
}
/// <summary>
/// Converts custom fields from various formats to a dictionary.
/// </summary>
/// <param name="customFields">The custom fields object to convert.</param>
/// <returns>A dictionary of custom field names and values.</returns>
protected static Dictionary<string, object> ConvertCustomFields(object? customFields)
{
if (customFields == null)
{
return new Dictionary<string, object>();
}
if (customFields is PSObject psobj)
{
var dict = new Dictionary<string, object>();
foreach (var prop in psobj.Properties)
{
dict[prop.Name] = prop.Value ?? string.Empty;
}
return dict;
}
if (customFields is Dictionary<string, object> dictObj)
{
return dictObj;
}
if (customFields is IDictionary genericDict)
{
var result = new Dictionary<string, object>();
foreach (DictionaryEntry entry in genericDict)
{
result[entry.Key?.ToString() ?? string.Empty] = entry.Value ?? string.Empty;
}
return result;
}
return new Dictionary<string, object>();
}
/// <summary>
/// Executes an async operation synchronously and handles errors.
/// </summary>
/// <typeparam name="T">The return type of the operation.</typeparam>
/// <param name="operation">The async operation to execute.</param>
/// <param name="errorId">The error ID for error reporting.</param>
/// <returns>The result of the operation, or default if an error occurred.</returns>
protected T? ExecuteAsync<T>(Func<System.Threading.Tasks.Task<T>> operation, string errorId)
{
try
{
return operation().GetAwaiter().GetResult();
}
catch (Exception ex)
{
WriteError(new ErrorRecord(ex, errorId, ErrorCategory.InvalidOperation, null));
return default;
}
}
/// <summary>
/// Executes an async operation synchronously and handles errors.
/// </summary>
/// <param name="operation">The async operation to execute.</param>
/// <param name="errorId">The error ID for error reporting.</param>
protected void ExecuteAsync(Func<System.Threading.Tasks.Task> operation, string errorId)
{
try
{
operation().GetAwaiter().GetResult();
}
catch (Exception ex)
{
WriteError(new ErrorRecord(ex, errorId, ErrorCategory.InvalidOperation, null));
}
}
}

View File

@@ -1,17 +1,21 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Closes the current phpIPAM API session.
/// </summary>
[Cmdlet(VerbsCommon.Close, "Session")] [Cmdlet(VerbsCommon.Close, "Session")]
public class CloseSessionCmdlet : BaseCmdlet public class CloseSessionCmdlet : PSCmdlet
{ {
protected override void ProcessRecord() protected override void ProcessRecord()
{ {
try
{
RequestHelper.InvokeRequest("DELETE", controllers.user, null, null, null, null)
.GetAwaiter().GetResult();
SessionManager.CloseSession(); SessionManager.CloseSession();
WriteVerbose("Session closed successfully."); }
catch (System.Exception ex)
{
WriteError(new ErrorRecord(ex, "CloseSessionError", ErrorCategory.InvalidOperation, null));
}
} }
} }

View File

@@ -1,25 +1,21 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using System.Net; using System.Net;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Retrieves IP address entries from phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Get, "Address", DefaultParameterSetName = "ByID")] [Cmdlet(VerbsCommon.Get, "Address", DefaultParameterSetName = "ByID")]
[OutputType(typeof(Address))] [OutputType(typeof(Address))]
public class GetAddressCmdlet : BaseCmdlet public class GetAddressCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The address ID to retrieve.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int Id { get; set; } public int Id { get; set; }
@@ -28,8 +24,7 @@ public class GetAddressCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByIP", ParameterSetName = "ByIP")]
HelpMessage = "The IP address to search for.")]
[Parameter( [Parameter(
Mandatory = false, Mandatory = false,
ValueFromPipeline = true, ValueFromPipeline = true,
@@ -44,8 +39,7 @@ public class GetAddressCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByHostName", ParameterSetName = "ByHostName")]
HelpMessage = "The hostname to search for.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? HostName { get; set; } public string? HostName { get; set; }
@@ -54,8 +48,7 @@ public class GetAddressCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByHostBase", ParameterSetName = "ByHostBase")]
HelpMessage = "The hostname base pattern to search for.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? HostBase { get; set; } public string? HostBase { get; set; }
@@ -64,8 +57,7 @@ public class GetAddressCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByTag", ParameterSetName = "ByTag")]
HelpMessage = "The tag ID to filter addresses by.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? TagId { get; set; } public int? TagId { get; set; }
@@ -74,8 +66,7 @@ public class GetAddressCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySubnetId", ParameterSetName = "BySubnetId")]
HelpMessage = "The subnet ID to get addresses from.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? SubnetId { get; set; } public int? SubnetId { get; set; }
@@ -84,8 +75,7 @@ public class GetAddressCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySubnetCIDR", ParameterSetName = "BySubnetCIDR")]
HelpMessage = "The subnet in CIDR notation to get addresses from.")]
[ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")] [ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? SubnetCIDR { get; set; } public string? SubnetCIDR { get; set; }
@@ -94,7 +84,7 @@ public class GetAddressCmdlet : BaseCmdlet
{ {
try try
{ {
var controller = ApiController.Addresses; var controller = controllers.addresses;
var identifiers = new List<string>(); var identifiers = new List<string>();
switch (ParameterSetName) switch (ParameterSetName)
@@ -102,28 +92,23 @@ public class GetAddressCmdlet : BaseCmdlet
case "ByID": case "ByID":
identifiers.Add(Id.ToString()); identifiers.Add(Id.ToString());
break; break;
case "ByIP": case "ByIP":
identifiers.Add("search"); identifiers.Add("search");
identifiers.Add(IP!.ToString()); identifiers.Add(IP!.ToString());
break; break;
case "ByHostName": case "ByHostName":
identifiers.Add("search_hostname"); identifiers.Add("search_hostname");
identifiers.Add(HostName!); identifiers.Add(HostName!);
break; break;
case "ByHostBase": case "ByHostBase":
identifiers.Add("search_hostbase"); identifiers.Add("search_hostbase");
identifiers.Add(HostBase!); identifiers.Add(HostBase!);
break; break;
case "ByTag": case "ByTag":
identifiers.Add("tags"); identifiers.Add("tags");
identifiers.Add(TagId!.Value.ToString()); identifiers.Add(TagId!.Value.ToString());
identifiers.Add("addresses"); identifiers.Add("addresses");
break; break;
case "BySubnetId": case "BySubnetId":
if (IP != null) if (IP != null)
{ {
@@ -132,34 +117,42 @@ public class GetAddressCmdlet : BaseCmdlet
} }
else else
{ {
controller = ApiController.Subnets; controller = controllers.subnets;
identifiers.Add(SubnetId!.Value.ToString()); identifiers.Add(SubnetId!.Value.ToString());
identifiers.Add("addresses"); identifiers.Add("addresses");
} }
break; break;
case "BySubnetCIDR": case "BySubnetCIDR":
controller = ApiController.Subnets; controller = controllers.subnets;
var subnet = RequestHelper.InvokeRequest( var subnet = RequestHelper.InvokeRequest("GET", controllers.subnets, types.Subnetwork, null, null, new[] { "cidr", SubnetCIDR! })
"GET", ApiController.Subnets, ModelType.Subnetwork, null, null, .GetAwaiter().GetResult();
new[] { "cidr", SubnetCIDR! }
).GetAwaiter().GetResult() as Subnetwork;
if (subnet == null) if (subnet == null)
{ {
throw new ItemNotFoundException($"Subnet '{SubnetCIDR}' not found."); throw new Exception("Cannot find subnet!");
} }
var subnetObj = subnet as Subnetwork;
identifiers.Add(subnet.Id.ToString()); identifiers.Add(subnetObj!.Id.ToString());
identifiers.Add("addresses"); identifiers.Add("addresses");
break; break;
} }
var result = RequestHelper.InvokeRequest( var result = RequestHelper.InvokeRequest("GET", controller, types.Address, null, null, identifiers.ToArray())
"GET", controller, ModelType.Address, null, null, identifiers.ToArray() .GetAwaiter().GetResult();
).GetAwaiter().GetResult();
WriteResult(result); if (result != null)
{
if (result is System.Collections.IEnumerable enumerable && !(result is string))
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(result);
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,23 +1,20 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using System.Net;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Gets the first available IP address in a subnet.
/// </summary>
[Cmdlet(VerbsCommon.Get, "FirstFreeIP", DefaultParameterSetName = "ByID")] [Cmdlet(VerbsCommon.Get, "FirstFreeIP", DefaultParameterSetName = "ByID")]
public class GetFirstFreeIPCmdlet : BaseCmdlet public class GetFirstFreeIPCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByCIDR", ParameterSetName = "ByCIDR")]
HelpMessage = "The subnet in CIDR notation.")]
[ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")] [ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? CIDR { get; set; } public string? CIDR { get; set; }
@@ -27,8 +24,7 @@ public class GetFirstFreeIPCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The subnet ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Id { get; set; } public int? Id { get; set; }
@@ -37,8 +33,7 @@ public class GetFirstFreeIPCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySubnetObject", ParameterSetName = "BySubnetObject")]
HelpMessage = "The subnet object.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public Subnetwork? SubnetObject { get; set; } public Subnetwork? SubnetObject { get; set; }
@@ -46,12 +41,30 @@ public class GetFirstFreeIPCmdlet : BaseCmdlet
{ {
try try
{ {
var subnetId = GetSubnetId(); int subnetId;
var identifiers = new List<string> { subnetId.ToString(), "first_free" }; if (ParameterSetName == "ByCIDR")
{
var subnet = RequestHelper.InvokeRequest("GET", controllers.subnets, types.Subnetwork, null, null, new[] { "cidr", CIDR! })
.GetAwaiter().GetResult();
if (subnet == null)
{
throw new Exception("Cannot find subnet!");
}
var subnetObj = subnet as Subnetwork;
subnetId = subnetObj!.Id;
}
else if (ParameterSetName == "BySubnetObject")
{
subnetId = SubnetObject!.Id;
}
else
{
subnetId = Id!.Value;
}
var result = RequestHelper.InvokeRequest( var identifiers = new List<string> { subnetId.ToString(), "first_free" };
"GET", ApiController.Subnets, null, null, null, identifiers.ToArray() var result = RequestHelper.InvokeRequest("GET", controllers.subnets, null, null, null, identifiers.ToArray())
).GetAwaiter().GetResult(); .GetAwaiter().GetResult();
if (result != null) if (result != null)
{ {
@@ -63,28 +76,4 @@ public class GetFirstFreeIPCmdlet : BaseCmdlet
WriteError(new ErrorRecord(ex, "GetFirstFreeIPError", ErrorCategory.InvalidOperation, null)); WriteError(new ErrorRecord(ex, "GetFirstFreeIPError", ErrorCategory.InvalidOperation, null));
} }
} }
private int GetSubnetId()
{
switch (ParameterSetName)
{
case "ByCIDR":
var subnet = RequestHelper.InvokeRequest(
"GET", ApiController.Subnets, ModelType.Subnetwork, null, null,
new[] { "cidr", CIDR! }
).GetAwaiter().GetResult() as Subnetwork;
if (subnet == null)
{
throw new ItemNotFoundException($"Subnet '{CIDR}' not found.");
}
return subnet.Id;
case "BySubnetObject":
return SubnetObject!.Id;
default:
return Id!.Value;
}
}
} }

View File

@@ -1,24 +1,20 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Retrieves L2 domain information from phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Get, "L2Domain", DefaultParameterSetName = "ByID")] [Cmdlet(VerbsCommon.Get, "L2Domain", DefaultParameterSetName = "ByID")]
[OutputType(typeof(Domain))] [OutputType(typeof(Domain))]
public class GetL2DomainCmdlet : BaseCmdlet public class GetL2DomainCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = false, Mandatory = false,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The L2 domain ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Id { get; set; } public int? Id { get; set; }
@@ -27,18 +23,28 @@ public class GetL2DomainCmdlet : BaseCmdlet
try try
{ {
var identifiers = new List<string>(); var identifiers = new List<string>();
if (Id.HasValue) if (Id.HasValue)
{ {
identifiers.Add(Id.Value.ToString()); identifiers.Add(Id.Value.ToString());
} }
var result = RequestHelper.InvokeRequest( var result = RequestHelper.InvokeRequest("GET", controllers.l2domains, types.Domain, null, null, identifiers.Count > 0 ? identifiers.ToArray() : null)
"GET", ApiController.L2Domains, ModelType.Domain, null, null, .GetAwaiter().GetResult();
identifiers.Count > 0 ? identifiers.ToArray() : null
).GetAwaiter().GetResult();
WriteResult(result); if (result != null)
{
if (result is System.Collections.IEnumerable enumerable && !(result is string))
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(result);
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,24 +1,20 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Retrieves nameserver information from phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Get, "Nameserver", DefaultParameterSetName = "NoParams")] [Cmdlet(VerbsCommon.Get, "Nameserver", DefaultParameterSetName = "NoParams")]
[OutputType(typeof(Nameserver))] [OutputType(typeof(Nameserver))]
public class GetNameserverCmdlet : BaseCmdlet public class GetNameserverCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The nameserver ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Id { get; set; } public int? Id { get; set; }
@@ -27,18 +23,28 @@ public class GetNameserverCmdlet : BaseCmdlet
try try
{ {
var identifiers = new List<string>(); var identifiers = new List<string>();
if (Id.HasValue) if (Id.HasValue)
{ {
identifiers.Add(Id.Value.ToString()); identifiers.Add(Id.Value.ToString());
} }
var result = RequestHelper.InvokeRequest( var result = RequestHelper.InvokeRequest("GET", controllers.tools, types.Nameserver, subcontrollers.nameservers, null, identifiers.Count > 0 ? identifiers.ToArray() : null)
"GET", ApiController.Tools, ModelType.Nameserver, ApiSubController.Nameservers, .GetAwaiter().GetResult();
null, identifiers.Count > 0 ? identifiers.ToArray() : null
).GetAwaiter().GetResult();
WriteResult(result); if (result != null)
{
if (result is System.Collections.IEnumerable enumerable && !(result is string))
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(result);
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,24 +1,20 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using System.Net; using System.Net;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Retrieves permission information from phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Get, "Permissions")] [Cmdlet(VerbsCommon.Get, "Permissions")]
public class GetPermissionsCmdlet : BaseCmdlet public class GetPermissionsCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The address ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? Id { get; set; } public string? Id { get; set; }
@@ -27,8 +23,7 @@ public class GetPermissionsCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByIP", ParameterSetName = "ByIP")]
HelpMessage = "The IP address to search for.")]
[Parameter( [Parameter(
Mandatory = false, Mandatory = false,
ValueFromPipeline = true, ValueFromPipeline = true,
@@ -43,8 +38,7 @@ public class GetPermissionsCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByHostName", ParameterSetName = "ByHostName")]
HelpMessage = "The hostname to search for.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? HostName { get; set; } public string? HostName { get; set; }
@@ -53,8 +47,7 @@ public class GetPermissionsCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByTag", ParameterSetName = "ByTag")]
HelpMessage = "The tag ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? TagId { get; set; } public string? TagId { get; set; }
@@ -63,8 +56,7 @@ public class GetPermissionsCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySubnetId", ParameterSetName = "BySubnetId")]
HelpMessage = "The subnet ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? SubnetId { get; set; } public string? SubnetId { get; set; }
@@ -73,8 +65,7 @@ public class GetPermissionsCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySubnetCIDR", ParameterSetName = "BySubnetCIDR")]
HelpMessage = "The subnet in CIDR notation.")]
[ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")] [ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? SubnetCIDR { get; set; } public string? SubnetCIDR { get; set; }
@@ -83,7 +74,7 @@ public class GetPermissionsCmdlet : BaseCmdlet
{ {
try try
{ {
var controller = ApiController.Addresses; var controller = controllers.addresses;
var identifiers = new List<string>(); var identifiers = new List<string>();
switch (ParameterSetName) switch (ParameterSetName)
@@ -91,23 +82,19 @@ public class GetPermissionsCmdlet : BaseCmdlet
case "ByID": case "ByID":
identifiers.Add(Id!); identifiers.Add(Id!);
break; break;
case "ByIP": case "ByIP":
identifiers.Add("search"); identifiers.Add("search");
identifiers.Add(IP!.ToString()); identifiers.Add(IP!.ToString());
break; break;
case "ByHostName": case "ByHostName":
identifiers.Add("search_hostname"); identifiers.Add("search_hostname");
identifiers.Add(HostName!); identifiers.Add(HostName!);
break; break;
case "ByTag": case "ByTag":
identifiers.Add("tags"); identifiers.Add("tags");
identifiers.Add(TagId!); identifiers.Add(TagId!);
identifiers.Add("addresses"); identifiers.Add("addresses");
break; break;
case "BySubnetId": case "BySubnetId":
if (IP != null) if (IP != null)
{ {
@@ -116,34 +103,42 @@ public class GetPermissionsCmdlet : BaseCmdlet
} }
else else
{ {
controller = ApiController.Subnets; controller = controllers.subnets;
identifiers.Add(SubnetId!); identifiers.Add(SubnetId!);
identifiers.Add("addresses"); identifiers.Add("addresses");
} }
break; break;
case "BySubnetCIDR": case "BySubnetCIDR":
controller = ApiController.Subnets; controller = controllers.subnets;
var subnet = RequestHelper.InvokeRequest( var subnet = RequestHelper.InvokeRequest("GET", controllers.subnets, types.Subnetwork, null, null, new[] { "cidr", SubnetCIDR! })
"GET", ApiController.Subnets, ModelType.Subnetwork, null, null, .GetAwaiter().GetResult();
new[] { "cidr", SubnetCIDR! }
).GetAwaiter().GetResult() as Subnetwork;
if (subnet == null) if (subnet == null)
{ {
throw new ItemNotFoundException($"Subnet '{SubnetCIDR}' not found."); throw new Exception("Cannot find subnet!");
} }
var subnetObj = subnet as Subnetwork;
identifiers.Add(subnet.Id.ToString()); identifiers.Add(subnetObj!.Id.ToString());
identifiers.Add("addresses"); identifiers.Add("addresses");
break; break;
} }
var result = RequestHelper.InvokeRequest( var result = RequestHelper.InvokeRequest("GET", controller, null, null, null, identifiers.ToArray())
"GET", controller, null, null, null, identifiers.ToArray() .GetAwaiter().GetResult();
).GetAwaiter().GetResult();
WriteResult(result); if (result != null)
{
if (result is System.Collections.IEnumerable enumerable && !(result is string))
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(result);
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,23 +1,19 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Retrieves section information from phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Get, "Section", DefaultParameterSetName = "NoParams")] [Cmdlet(VerbsCommon.Get, "Section", DefaultParameterSetName = "NoParams")]
[OutputType(typeof(Section))] [OutputType(typeof(Section))]
public class GetSectionCmdlet : BaseCmdlet public class GetSectionCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = false, Mandatory = false,
ValueFromPipeline = true, ValueFromPipeline = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The section ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Id { get; set; } public int? Id { get; set; }
@@ -25,8 +21,7 @@ public class GetSectionCmdlet : BaseCmdlet
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
Position = 0, Position = 0,
ParameterSetName = "ByName", ParameterSetName = "ByName")]
HelpMessage = "The section name.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? Name { get; set; } public string? Name { get; set; }
@@ -35,7 +30,6 @@ public class GetSectionCmdlet : BaseCmdlet
try try
{ {
var identifiers = new List<string>(); var identifiers = new List<string>();
if (ParameterSetName == "ByID" && Id.HasValue) if (ParameterSetName == "ByID" && Id.HasValue)
{ {
identifiers.Add(Id.Value.ToString()); identifiers.Add(Id.Value.ToString());
@@ -45,12 +39,23 @@ public class GetSectionCmdlet : BaseCmdlet
identifiers.Add(Name!); identifiers.Add(Name!);
} }
var result = RequestHelper.InvokeRequest( var result = RequestHelper.InvokeRequest("GET", controllers.sections, types.Section, null, null, identifiers.Count > 0 ? identifiers.ToArray() : null)
"GET", ApiController.Sections, ModelType.Section, null, null, .GetAwaiter().GetResult();
identifiers.Count > 0 ? identifiers.ToArray() : null
).GetAwaiter().GetResult();
WriteResult(result); if (result != null)
{
if (result is System.Collections.IEnumerable enumerable && !(result is string))
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(result);
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,24 +1,22 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Management.Automation; using System.Management.Automation;
using System.Net;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Retrieves subnet information from phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Get, "Subnet", DefaultParameterSetName = "NoParams")] [Cmdlet(VerbsCommon.Get, "Subnet", DefaultParameterSetName = "NoParams")]
[OutputType(typeof(Subnetwork))] [OutputType(typeof(Subnetwork))]
public class GetSubnetCmdlet : BaseCmdlet public class GetSubnetCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByCIDR", ParameterSetName = "ByCIDR")]
HelpMessage = "The subnet in CIDR notation (e.g., 192.168.1.0/24).")]
[ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")] [ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? CIDR { get; set; } public string? CIDR { get; set; }
@@ -28,8 +26,7 @@ public class GetSubnetCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The subnet ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Id { get; set; } public int? Id { get; set; }
@@ -38,8 +35,7 @@ public class GetSubnetCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySectionId", ParameterSetName = "BySectionId")]
HelpMessage = "The section ID to get subnets from.")]
[Parameter( [Parameter(
Mandatory = false, Mandatory = false,
ValueFromPipeline = true, ValueFromPipeline = true,
@@ -60,8 +56,7 @@ public class GetSubnetCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySectionName", ParameterSetName = "BySectionName")]
HelpMessage = "The section name to get subnets from.")]
[Parameter( [Parameter(
Mandatory = false, Mandatory = false,
ValueFromPipeline = true, ValueFromPipeline = true,
@@ -82,8 +77,7 @@ public class GetSubnetCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByVrfId", ParameterSetName = "ByVrfId")]
HelpMessage = "The VRF ID to get subnets from.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? VrfId { get; set; } public int? VrfId { get; set; }
@@ -92,8 +86,7 @@ public class GetSubnetCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByVlanId", ParameterSetName = "ByVlanId")]
HelpMessage = "The VLAN ID to get subnets from.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? VlanId { get; set; } public int? VlanId { get; set; }
@@ -102,8 +95,7 @@ public class GetSubnetCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByVlanNumber", ParameterSetName = "ByVlanNumber")]
HelpMessage = "The VLAN number to get subnets from.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? VlanNumber { get; set; } public int? VlanNumber { get; set; }
@@ -112,8 +104,7 @@ public class GetSubnetCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 1, Position = 1,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "Get child subnets (slaves).")]
public SwitchParameter Slaves { get; set; } public SwitchParameter Slaves { get; set; }
[Parameter( [Parameter(
@@ -121,8 +112,7 @@ public class GetSubnetCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 2, Position = 2,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "Get child subnets recursively.")]
public SwitchParameter Recurse { get; set; } public SwitchParameter Recurse { get; set; }
[Parameter( [Parameter(
@@ -130,16 +120,16 @@ public class GetSubnetCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 1, Position = 1,
ParameterSetName = "ByVlanNumber", ParameterSetName = "ByVlanNumber")]
HelpMessage = "The L2 domain ID to narrow VLAN search.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? VlanDomainId { get; set; } public int? VlanDomainId { get; set; }
protected override void ProcessRecord() protected override void ProcessRecord()
{ {
try try
{ {
var controller = ApiController.Subnets; var controller = controllers.subnets;
var identifiers = new List<string>(); var identifiers = new List<string>();
switch (ParameterSetName) switch (ParameterSetName)
@@ -148,7 +138,6 @@ public class GetSubnetCmdlet : BaseCmdlet
identifiers.Add("cidr"); identifiers.Add("cidr");
identifiers.Add(CIDR!); identifiers.Add(CIDR!);
break; break;
case "ByID": case "ByID":
identifiers.Add(Id!.Value.ToString()); identifiers.Add(Id!.Value.ToString());
if (Slaves.IsPresent) if (Slaves.IsPresent)
@@ -156,122 +145,117 @@ public class GetSubnetCmdlet : BaseCmdlet
identifiers.Add(Recurse.IsPresent ? "slaves_recursive" : "slaves"); identifiers.Add(Recurse.IsPresent ? "slaves_recursive" : "slaves");
} }
break; break;
case "BySectionId": case "BySectionId":
controller = ApiController.Sections; controller = controllers.sections;
identifiers.Add(SectionId!.Value.ToString()); identifiers.Add(SectionId!.Value.ToString());
identifiers.Add("subnets"); identifiers.Add("subnets");
break; break;
case "BySectionName": case "BySectionName":
controller = ApiController.Sections; controller = controllers.sections;
var section = GetSectionByName(SectionName!); var section = RequestHelper.InvokeRequest("GET", controllers.sections, types.Section, null, null, new[] { SectionName! })
identifiers.Add(section.Id.ToString()); .GetAwaiter().GetResult();
if (section == null)
{
throw new Exception("Cannot find section!");
}
var sectionObj = section as Section;
identifiers.Add(sectionObj!.Id.ToString());
identifiers.Add("subnets"); identifiers.Add("subnets");
break; break;
case "ByVrfId": case "ByVrfId":
controller = ApiController.Vrf; controller = controllers.vrf;
identifiers.Add(VrfId!.Value.ToString()); identifiers.Add(VrfId!.Value.ToString());
identifiers.Add("subnets"); identifiers.Add("subnets");
break; break;
case "ByVlanId": case "ByVlanId":
controller = ApiController.Vlan; controller = controllers.vlan;
identifiers.Add(VlanId!.Value.ToString()); identifiers.Add(VlanId!.Value.ToString());
identifiers.Add("subnets"); identifiers.Add("subnets");
AddSectionIdentifier(identifiers);
break;
case "ByVlanNumber":
controller = ApiController.Vlan;
var vlan = FindVlanByNumber(VlanNumber!.Value, VlanDomainId);
identifiers.Add(vlan.Id.ToString());
identifiers.Add("subnets");
AddSectionIdentifier(identifiers);
break;
}
var result = RequestHelper.InvokeRequest(
"GET", controller, ModelType.Subnetwork, null, null, identifiers.ToArray()
).GetAwaiter().GetResult();
WriteResult(result);
}
catch (Exception ex)
{
WriteError(new ErrorRecord(ex, "GetSubnetError", ErrorCategory.InvalidOperation, null));
}
}
private Section GetSectionByName(string name)
{
var section = RequestHelper.InvokeRequest(
"GET", ApiController.Sections, ModelType.Section, null, null, new[] { name }
).GetAwaiter().GetResult() as Section;
if (section == null)
{
throw new ItemNotFoundException($"Section '{name}' not found.");
}
return section;
}
private Vlan FindVlanByNumber(int number, int? domainId)
{
var vlans = RequestHelper.InvokeRequest(
"GET", ApiController.Vlan, ModelType.Vlan, null, null,
new[] { "search", number.ToString() }
).GetAwaiter().GetResult();
if (vlans == null)
{
throw new ItemNotFoundException($"VLAN {number} not found.");
}
Vlan? foundVlan = null;
if (vlans is System.Collections.IEnumerable enumerable)
{
foreach (var v in enumerable)
{
if (v is Vlan vlan)
{
if (domainId.HasValue && vlan.DomainId != domainId.Value)
{
continue;
}
if (foundVlan != null)
{
throw new InvalidOperationException(
$"Multiple VLANs with number {number} exist. Specify VlanDomainId to narrow the search.");
}
foundVlan = vlan;
}
}
}
if (foundVlan == null)
{
throw new ItemNotFoundException($"VLAN {number} not found.");
}
return foundVlan;
}
private void AddSectionIdentifier(List<string> identifiers)
{
if (SectionId.HasValue) if (SectionId.HasValue)
{ {
identifiers.Add(SectionId.Value.ToString()); identifiers.Add(SectionId.Value.ToString());
} }
else if (!string.IsNullOrEmpty(SectionName)) else if (!string.IsNullOrEmpty(SectionName))
{ {
var section = GetSectionByName(SectionName); var section2 = RequestHelper.InvokeRequest("GET", controllers.sections, types.Section, null, null, new[] { SectionName })
identifiers.Add(section.Id.ToString()); .GetAwaiter().GetResult();
if (section2 != null)
{
var sectionObj2 = section2 as Section;
identifiers.Add(sectionObj2!.Id.ToString());
}
}
break;
case "ByVlanNumber":
controller = controllers.vlan;
var vlans = RequestHelper.InvokeRequest("GET", controllers.vlan, types.Vlan, null, null, new[] { "search", VlanNumber!.Value.ToString() })
.GetAwaiter().GetResult();
if (vlans == null)
{
throw new Exception("Cannot find Vlan!");
}
var vlanList = vlans as System.Collections.IEnumerable;
Vlan? foundVlan = null;
if (vlanList != null)
{
foreach (var v in vlanList)
{
if (v is Vlan vlan)
{
if (VlanDomainId.HasValue && vlan.DomainId != VlanDomainId.Value)
continue;
if (foundVlan != null)
{
throw new Exception($"More than one vLan with {VlanNumber} number is present!");
}
foundVlan = vlan;
}
}
}
if (foundVlan == null)
{
throw new Exception("Cannot find Vlan!");
}
identifiers.Add(foundVlan.Id.ToString());
identifiers.Add("subnets");
if (SectionId.HasValue)
{
identifiers.Add(SectionId.Value.ToString());
}
else if (!string.IsNullOrEmpty(SectionName))
{
var section3 = RequestHelper.InvokeRequest("GET", controllers.sections, types.Section, null, null, new[] { SectionName })
.GetAwaiter().GetResult();
if (section3 != null)
{
var sectionObj3 = section3 as Section;
identifiers.Add(sectionObj3!.Id.ToString());
}
}
break;
}
var result = RequestHelper.InvokeRequest("GET", controller, types.Subnetwork, null, null, identifiers.ToArray())
.GetAwaiter().GetResult();
if (result != null)
{
if (result is System.Collections.IEnumerable enumerable && !(result is string))
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(result);
}
}
}
catch (Exception ex)
{
WriteError(new ErrorRecord(ex, "GetSubnetError", ErrorCategory.InvalidOperation, null));
} }
} }
} }

View File

@@ -1,23 +1,20 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using System.Net;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Retrieves subnet usage statistics from phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Get, "SubnetUsage", DefaultParameterSetName = "ByID")] [Cmdlet(VerbsCommon.Get, "SubnetUsage", DefaultParameterSetName = "ByID")]
public class GetSubnetUsageCmdlet : BaseCmdlet public class GetSubnetUsageCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByCIDR", ParameterSetName = "ByCIDR")]
HelpMessage = "The subnet in CIDR notation.")]
[ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")] [ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? CIDR { get; set; } public string? CIDR { get; set; }
@@ -27,60 +24,43 @@ public class GetSubnetUsageCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The subnet ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Id { get; set; } public int? Id { get; set; }
[Parameter(
Mandatory = true,
ValueFromPipeline = true,
Position = 0,
ParameterSetName = "BySubnetObject",
HelpMessage = "The subnet object.")]
[ValidateNotNullOrEmpty]
public Subnetwork? SubnetObject { get; set; }
protected override void ProcessRecord() protected override void ProcessRecord()
{ {
try try
{ {
var subnetId = GetSubnetId(); int subnetId;
if (ParameterSetName == "ByCIDR")
{
var subnet = RequestHelper.InvokeRequest("GET", controllers.subnets, types.Subnetwork, null, null, new[] { "cidr", CIDR! })
.GetAwaiter().GetResult();
if (subnet == null)
{
throw new Exception("Cannot find subnet!");
}
var subnetObj = subnet as Subnetwork;
subnetId = subnetObj!.Id;
}
else
{
subnetId = Id!.Value;
}
var identifiers = new List<string> { subnetId.ToString(), "usage" }; var identifiers = new List<string> { subnetId.ToString(), "usage" };
var result = RequestHelper.InvokeRequest("GET", controllers.subnets, null, null, null, identifiers.ToArray())
.GetAwaiter().GetResult();
var result = RequestHelper.InvokeRequest( if (result != null)
"GET", ApiController.Subnets, null, null, null, identifiers.ToArray() {
).GetAwaiter().GetResult(); WriteObject(result);
}
WriteResult(result);
} }
catch (Exception ex) catch (Exception ex)
{ {
WriteError(new ErrorRecord(ex, "GetSubnetUsageError", ErrorCategory.InvalidOperation, null)); WriteError(new ErrorRecord(ex, "GetSubnetUsageError", ErrorCategory.InvalidOperation, null));
} }
} }
private int GetSubnetId()
{
switch (ParameterSetName)
{
case "ByCIDR":
var subnet = RequestHelper.InvokeRequest(
"GET", ApiController.Subnets, ModelType.Subnetwork, null, null,
new[] { "cidr", CIDR! }
).GetAwaiter().GetResult() as Subnetwork;
if (subnet == null)
{
throw new ItemNotFoundException($"Subnet '{CIDR}' not found.");
}
return subnet.Id;
case "BySubnetObject":
return SubnetObject!.Id;
default:
return Id!.Value;
}
}
} }

View File

@@ -1,24 +1,20 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Retrieves tag information from phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Get, "Tag", DefaultParameterSetName = "NoParams")] [Cmdlet(VerbsCommon.Get, "Tag", DefaultParameterSetName = "NoParams")]
[OutputType(typeof(Tag))] [OutputType(typeof(Tag))]
public class GetTagCmdlet : BaseCmdlet public class GetTagCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = false, Mandatory = false,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The tag ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Id { get; set; } public int? Id { get; set; }
@@ -27,8 +23,7 @@ public class GetTagCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByAddressObject", ParameterSetName = "ByAddressObject")]
HelpMessage = "Get the tag associated with this address.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public Address? AddressObject { get; set; } public Address? AddressObject { get; set; }
@@ -37,8 +32,7 @@ public class GetTagCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySubnetObject", ParameterSetName = "BySubnetObject")]
HelpMessage = "Get the tag associated with this subnet.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public Subnetwork? SubnetObject { get; set; } public Subnetwork? SubnetObject { get; set; }
@@ -56,7 +50,6 @@ public class GetTagCmdlet : BaseCmdlet
identifiers.Add(Id.Value.ToString()); identifiers.Add(Id.Value.ToString());
} }
break; break;
case "ByAddressObject": case "ByAddressObject":
if (AddressObject?.TagId > 0) if (AddressObject?.TagId > 0)
{ {
@@ -67,7 +60,6 @@ public class GetTagCmdlet : BaseCmdlet
return; return;
} }
break; break;
case "BySubnetObject": case "BySubnetObject":
if (SubnetObject?.TagId > 0) if (SubnetObject?.TagId > 0)
{ {
@@ -80,11 +72,23 @@ public class GetTagCmdlet : BaseCmdlet
break; break;
} }
var result = RequestHelper.InvokeRequest( var result = RequestHelper.InvokeRequest("GET", controllers.addresses, types.Tag, null, null, identifiers.ToArray())
"GET", ApiController.Addresses, ModelType.Tag, null, null, identifiers.ToArray() .GetAwaiter().GetResult();
).GetAwaiter().GetResult();
WriteResult(result); if (result != null)
{
if (result is System.Collections.IEnumerable enumerable && !(result is string))
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(result);
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,24 +1,20 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Retrieves VLAN information from phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Get, "Vlan", DefaultParameterSetName = "NoParams")] [Cmdlet(VerbsCommon.Get, "Vlan", DefaultParameterSetName = "NoParams")]
[OutputType(typeof(Vlan))] [OutputType(typeof(Vlan))]
public class GetVlanCmdlet : BaseCmdlet public class GetVlanCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The VLAN ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Id { get; set; } public int? Id { get; set; }
@@ -27,8 +23,7 @@ public class GetVlanCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByNumber", ParameterSetName = "ByNumber")]
HelpMessage = "The VLAN number.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Number { get; set; } public int? Number { get; set; }
@@ -37,8 +32,7 @@ public class GetVlanCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByL2Domain", ParameterSetName = "ByL2Domain")]
HelpMessage = "The L2 domain ID to get VLANs from.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? L2DomainId { get; set; } public int? L2DomainId { get; set; }
@@ -47,8 +41,7 @@ public class GetVlanCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySubnetObject", ParameterSetName = "BySubnetObject")]
HelpMessage = "Get the VLAN associated with this subnet.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public Subnetwork? SubnetObject { get; set; } public Subnetwork? SubnetObject { get; set; }
@@ -57,8 +50,7 @@ public class GetVlanCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByDomainObject", ParameterSetName = "ByDomainObject")]
HelpMessage = "Get VLANs in this L2 domain.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public Domain? DomainObject { get; set; } public Domain? DomainObject { get; set; }
@@ -66,7 +58,7 @@ public class GetVlanCmdlet : BaseCmdlet
{ {
try try
{ {
var controller = ApiController.Vlan; var controller = controllers.vlan;
var identifiers = new List<string>(); var identifiers = new List<string>();
switch (ParameterSetName) switch (ParameterSetName)
@@ -74,44 +66,65 @@ public class GetVlanCmdlet : BaseCmdlet
case "ByID": case "ByID":
identifiers.Add(Id!.Value.ToString()); identifiers.Add(Id!.Value.ToString());
break; break;
case "ByNumber": case "ByNumber":
identifiers.Add("search"); identifiers.Add("search");
identifiers.Add(Number!.Value.ToString()); identifiers.Add(Number!.Value.ToString());
break; break;
case "ByL2Domain": case "ByL2Domain":
controller = ApiController.L2Domains; controller = controllers.l2domains;
identifiers.Add(L2DomainId!.Value.ToString()); identifiers.Add(L2DomainId!.Value.ToString());
identifiers.Add(ApiSubController.Vlans.ToString().ToLowerInvariant()); identifiers.Add(subcontrollers.vlans.ToString());
break; break;
case "BySubnetObject": case "BySubnetObject":
if (SubnetObject == null || SubnetObject.VlanId <= 0) if (SubnetObject != null && SubnetObject.VlanId > 0)
{
identifiers.Add(SubnetObject.VlanId.ToString());
}
else
{ {
return; return;
} }
identifiers.Add(SubnetObject.VlanId.ToString());
break; break;
case "ByDomainObject": case "ByDomainObject":
if (DomainObject != null) if (DomainObject != null)
{ {
var result = RequestHelper.InvokeRequest( var result = RequestHelper.InvokeRequest("GET", controllers.l2domains, types.Vlan, subcontrollers.vlans, null, new[] { DomainObject.Id.ToString() })
"GET", ApiController.L2Domains, ModelType.Vlan, ApiSubController.Vlans, .GetAwaiter().GetResult();
null, new[] { DomainObject.Id.ToString() } if (result != null)
).GetAwaiter().GetResult(); {
WriteResult(result); if (result is System.Collections.IEnumerable enumerable && !(result is string))
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(result);
}
}
} }
return; return;
} }
var vlanResult = RequestHelper.InvokeRequest( var vlanResult = RequestHelper.InvokeRequest("GET", controller, types.Vlan, null, null, identifiers.Count > 0 ? identifiers.ToArray() : null)
"GET", controller, ModelType.Vlan, null, null, .GetAwaiter().GetResult();
identifiers.Count > 0 ? identifiers.ToArray() : null
).GetAwaiter().GetResult();
WriteResult(vlanResult); if (vlanResult != null)
{
if (vlanResult is System.Collections.IEnumerable enumerable && !(vlanResult is string))
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(vlanResult);
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,24 +1,20 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Retrieves VRF information from phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Get, "Vrf", DefaultParameterSetName = "NoParams")] [Cmdlet(VerbsCommon.Get, "Vrf", DefaultParameterSetName = "NoParams")]
[OutputType(typeof(Vrf))] [OutputType(typeof(Vrf))]
public class GetVrfCmdlet : BaseCmdlet public class GetVrfCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByID", ParameterSetName = "ByID")]
HelpMessage = "The VRF ID.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Id { get; set; } public int? Id { get; set; }
@@ -27,18 +23,28 @@ public class GetVrfCmdlet : BaseCmdlet
try try
{ {
var identifiers = new List<string>(); var identifiers = new List<string>();
if (Id.HasValue) if (Id.HasValue)
{ {
identifiers.Add(Id.Value.ToString()); identifiers.Add(Id.Value.ToString());
} }
var result = RequestHelper.InvokeRequest( var result = RequestHelper.InvokeRequest("GET", controllers.vrf, types.Vrf, null, null, identifiers.Count > 0 ? identifiers.ToArray() : null)
"GET", ApiController.Vrf, ModelType.Vrf, null, null, .GetAwaiter().GetResult();
identifiers.Count > 0 ? identifiers.ToArray() : null
).GetAwaiter().GetResult();
WriteResult(result); if (result != null)
{
if (result is System.Collections.IEnumerable enumerable && !(result is string))
{
foreach (var item in enumerable)
{
WriteObject(item);
}
}
else
{
WriteObject(result);
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,24 +1,21 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using System.Net;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Creates a new IP address entry in phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.New, "Address", DefaultParameterSetName = "BySubnetId")] [Cmdlet(VerbsCommon.New, "Address", DefaultParameterSetName = "BySubnetId")]
[OutputType(typeof(Address))] [OutputType(typeof(Address))]
public class NewAddressCmdlet : BaseCmdlet public class NewAddressCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySubnetId", ParameterSetName = "BySubnetId")]
HelpMessage = "The subnet ID to create the address in.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? SubnetId { get; set; } public int? SubnetId { get; set; }
@@ -27,8 +24,7 @@ public class NewAddressCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "BySubnetObject", ParameterSetName = "BySubnetObject")]
HelpMessage = "The subnet object to create the address in.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public Subnetwork? SubnetObject { get; set; } public Subnetwork? SubnetObject { get; set; }
@@ -36,73 +32,166 @@ public class NewAddressCmdlet : BaseCmdlet
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 1, Position = 1)]
HelpMessage = "The IP address to create.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string IP { get; set; } = string.Empty; public string IP { get; set; } = string.Empty;
[Parameter(Mandatory = false, Position = 2, HelpMessage = "Mark this address as a gateway.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 2)]
public SwitchParameter Gateway { get; set; } public SwitchParameter Gateway { get; set; }
[Parameter(Mandatory = false, Position = 3, HelpMessage = "Description for the address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 3)]
public string? Description { get; set; } public string? Description { get; set; }
[Parameter(Mandatory = false, Position = 4, HelpMessage = "Hostname for the address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 4)]
public string? Hostname { get; set; } public string? Hostname { get; set; }
[Parameter(Mandatory = false, Position = 5, HelpMessage = "MAC address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 5)]
public string? MAC { get; set; } public string? MAC { get; set; }
[Parameter(Mandatory = false, Position = 6, HelpMessage = "Owner of the address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 6)]
public string? Owner { get; set; } public string? Owner { get; set; }
[Parameter(Mandatory = false, Position = 7, HelpMessage = "Tag ID for the address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 7)]
public int? TagId { get; set; } public int? TagId { get; set; }
[Parameter(Mandatory = false, Position = 8, HelpMessage = "Ignore PTR record generation.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 8)]
public SwitchParameter PTRIgnore { get; set; } public SwitchParameter PTRIgnore { get; set; }
[Parameter(Mandatory = false, Position = 9, HelpMessage = "PTR record ID.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 7)]
public int? PTRId { get; set; } public int? PTRId { get; set; }
[Parameter(Mandatory = false, Position = 10, HelpMessage = "Note for the address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 10)]
public string? Note { get; set; } public string? Note { get; set; }
[Parameter(Mandatory = false, Position = 11, HelpMessage = "Exclude from ping scanning.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 11)]
public SwitchParameter ExcludePing { get; set; } public SwitchParameter ExcludePing { get; set; }
[Parameter(Mandatory = false, Position = 12, HelpMessage = "Associated device ID.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 12)]
public int? DeviceId { get; set; } public int? DeviceId { get; set; }
[Parameter(Mandatory = false, Position = 13, HelpMessage = "Port information.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 13)]
public string? Port { get; set; } public string? Port { get; set; }
[Parameter(Mandatory = false, Position = 14, HelpMessage = "Custom fields as a hashtable or PSObject.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 14)]
public object? CustomFields { get; set; } public object? CustomFields { get; set; }
protected override void ProcessRecord() protected override void ProcessRecord()
{ {
try try
{ {
var actualSubnetId = ParameterSetName == "BySubnetObject" int actualSubnetId;
? SubnetObject!.Id if (ParameterSetName == "BySubnetObject")
: SubnetId!.Value; {
actualSubnetId = SubnetObject!.Id;
}
else
{
actualSubnetId = SubnetId!.Value;
}
var body = BuildRequestBody(actualSubnetId); var body = new Dictionary<string, object>
{
{ "subnetId", actualSubnetId },
{ "ip", IP }
};
var result = RequestHelper.InvokeRequest( if (Gateway.IsPresent)
"POST", ApiController.Addresses, null, null, body body["is_gateway"] = "1";
).GetAwaiter().GetResult(); if (!string.IsNullOrEmpty(Description))
body["description"] = Description;
if (!string.IsNullOrEmpty(Hostname))
body["hostname"] = Hostname;
if (!string.IsNullOrEmpty(MAC))
body["mac"] = MAC;
if (!string.IsNullOrEmpty(Owner))
body["owner"] = Owner;
if (TagId.HasValue)
body["tag"] = TagId.Value;
if (PTRIgnore.IsPresent)
body["PTRignore"] = "1";
if (PTRId.HasValue)
body["PTR"] = PTRId.Value;
if (!string.IsNullOrEmpty(Note))
body["note"] = Note;
if (ExcludePing.IsPresent)
body["excludePing"] = "1";
if (DeviceId.HasValue)
body["deviceId"] = DeviceId.Value;
if (!string.IsNullOrEmpty(Port))
body["port"] = Port;
if (CustomFields != null)
{
var customDict = ConvertCustomFields(CustomFields);
foreach (var kvp in customDict)
{
body[kvp.Key] = kvp.Value;
}
}
var result = RequestHelper.InvokeRequest("POST", controllers.addresses, null, null, body, null)
.GetAwaiter().GetResult();
if (result != null) if (result != null)
{ {
// Fetch the created address to return it var address = RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, new[] { "search", IP })
var address = RequestHelper.InvokeRequest( .GetAwaiter().GetResult();
"GET", ApiController.Addresses, ModelType.Address, null, null, if (address != null)
new[] { "search", IP } {
).GetAwaiter().GetResult(); WriteObject(address);
}
WriteResult(address);
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -111,32 +200,20 @@ public class NewAddressCmdlet : BaseCmdlet
} }
} }
private Dictionary<string, object> BuildRequestBody(int subnetId) private Dictionary<string, object> ConvertCustomFields(object customFields)
{ {
var body = new Dictionary<string, object> var dict = new Dictionary<string, object>();
if (customFields is PSObject psobj)
{ {
["subnetId"] = subnetId, foreach (var prop in psobj.Properties)
["ip"] = IP
};
if (Gateway.IsPresent) body["is_gateway"] = "1";
if (!string.IsNullOrEmpty(Description)) body["description"] = Description;
if (!string.IsNullOrEmpty(Hostname)) body["hostname"] = Hostname;
if (!string.IsNullOrEmpty(MAC)) body["mac"] = MAC;
if (!string.IsNullOrEmpty(Owner)) body["owner"] = Owner;
if (TagId.HasValue) body["tag"] = TagId.Value;
if (PTRIgnore.IsPresent) body["PTRignore"] = "1";
if (PTRId.HasValue) body["PTR"] = PTRId.Value;
if (!string.IsNullOrEmpty(Note)) body["note"] = Note;
if (ExcludePing.IsPresent) body["excludePing"] = "1";
if (DeviceId.HasValue) body["deviceId"] = DeviceId.Value;
if (!string.IsNullOrEmpty(Port)) body["port"] = Port;
foreach (var kvp in ConvertCustomFields(CustomFields))
{ {
body[kvp.Key] = kvp.Value; dict[prop.Name] = prop.Value ?? new object();
} }
}
return body; else if (customFields is Dictionary<string, object> dictObj)
{
return dictObj;
}
return dict;
} }
} }

View File

@@ -1,85 +1,168 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Creates a new address using the first available IP in a subnet.
/// </summary>
[Cmdlet(VerbsCommon.New, "FirstFreeIP")] [Cmdlet(VerbsCommon.New, "FirstFreeIP")]
[OutputType(typeof(Address))] [OutputType(typeof(Address))]
public class NewFirstFreeIPCmdlet : BaseCmdlet public class NewFirstFreeIPCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0)]
HelpMessage = "The subnet ID to create the address in.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string SubnetId { get; set; } = string.Empty; public string SubnetId { get; set; } = string.Empty;
[Parameter(Mandatory = false, Position = 1, HelpMessage = "Mark this address as a gateway.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 2)]
public SwitchParameter Gateway { get; set; } public SwitchParameter Gateway { get; set; }
[Parameter(Mandatory = false, Position = 2, HelpMessage = "Description for the address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 3)]
public string? Description { get; set; } public string? Description { get; set; }
[Parameter(Mandatory = false, Position = 3, HelpMessage = "Hostname for the address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 4)]
public string? Hostname { get; set; } public string? Hostname { get; set; }
[Parameter(Mandatory = false, Position = 4, HelpMessage = "MAC address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 5)]
public string? MAC { get; set; } public string? MAC { get; set; }
[Parameter(Mandatory = false, Position = 5, HelpMessage = "Owner of the address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 6)]
public string? Owner { get; set; } public string? Owner { get; set; }
[Parameter(Mandatory = false, Position = 6, HelpMessage = "Tag ID for the address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 7)]
public string? TagId { get; set; } public string? TagId { get; set; }
[Parameter(Mandatory = false, Position = 7, HelpMessage = "Ignore PTR record generation.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 8)]
public SwitchParameter PTRIgnore { get; set; } public SwitchParameter PTRIgnore { get; set; }
[Parameter(Mandatory = false, Position = 8, HelpMessage = "PTR record ID.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 9)]
public string? PTRId { get; set; } public string? PTRId { get; set; }
[Parameter(Mandatory = false, Position = 9, HelpMessage = "Note for the address.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 10)]
public string? Note { get; set; } public string? Note { get; set; }
[Parameter(Mandatory = false, Position = 10, HelpMessage = "Exclude from ping scanning.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 11)]
public SwitchParameter ExcludePing { get; set; } public SwitchParameter ExcludePing { get; set; }
[Parameter(Mandatory = false, Position = 11, HelpMessage = "Associated device ID.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 12)]
public string? DeviceId { get; set; } public string? DeviceId { get; set; }
[Parameter(Mandatory = false, Position = 12, HelpMessage = "Port information.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 13)]
public string? Port { get; set; } public string? Port { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Custom fields as a hashtable or PSObject.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true)]
public object? CustomFields { get; set; } public object? CustomFields { get; set; }
protected override void ProcessRecord() protected override void ProcessRecord()
{ {
try try
{ {
var identifiers = new[] { "first_free" }; var identifiers = new List<string> { "first_free" };
var body = BuildRequestBody(); var body = new Dictionary<string, object>
{
{ "subnetId", SubnetId }
};
var result = RequestHelper.InvokeRequest( if (Gateway.IsPresent)
"POST", ApiController.Addresses, null, null, body, identifiers body["is_gateway"] = "1";
).GetAwaiter().GetResult(); if (!string.IsNullOrEmpty(Description))
body["description"] = Description;
if (!string.IsNullOrEmpty(Hostname))
body["hostname"] = Hostname;
if (!string.IsNullOrEmpty(MAC))
body["mac"] = MAC;
if (!string.IsNullOrEmpty(Owner))
body["owner"] = Owner;
if (!string.IsNullOrEmpty(TagId))
body["tag"] = TagId;
if (PTRIgnore.IsPresent)
body["PTRignore"] = "1";
if (!string.IsNullOrEmpty(PTRId))
body["PTR"] = PTRId;
if (!string.IsNullOrEmpty(Note))
body["note"] = Note;
if (ExcludePing.IsPresent)
body["excludePing"] = "1";
if (!string.IsNullOrEmpty(DeviceId))
body["deviceId"] = DeviceId;
if (!string.IsNullOrEmpty(Port))
body["port"] = Port;
if (CustomFields != null)
{
var customDict = ConvertCustomFields(CustomFields);
foreach (var kvp in customDict)
{
body[kvp.Key] = kvp.Value;
}
}
var result = RequestHelper.InvokeRequest("POST", controllers.addresses, null, null, body, identifiers.ToArray())
.GetAwaiter().GetResult();
if (result != null) if (result != null)
{ {
var ip = result.ToString(); var ip = result.ToString();
var address = RequestHelper.InvokeRequest( var address = RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, new[] { "search", ip })
"GET", ApiController.Addresses, ModelType.Address, null, null, .GetAwaiter().GetResult();
new[] { "search", ip! } if (address != null)
).GetAwaiter().GetResult(); {
WriteObject(address);
WriteResult(address); }
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -88,31 +171,20 @@ public class NewFirstFreeIPCmdlet : BaseCmdlet
} }
} }
private Dictionary<string, object> BuildRequestBody() private Dictionary<string, object> ConvertCustomFields(object customFields)
{ {
var body = new Dictionary<string, object> var dict = new Dictionary<string, object>();
if (customFields is PSObject psobj)
{ {
["subnetId"] = SubnetId foreach (var prop in psobj.Properties)
};
if (Gateway.IsPresent) body["is_gateway"] = "1";
if (!string.IsNullOrEmpty(Description)) body["description"] = Description;
if (!string.IsNullOrEmpty(Hostname)) body["hostname"] = Hostname;
if (!string.IsNullOrEmpty(MAC)) body["mac"] = MAC;
if (!string.IsNullOrEmpty(Owner)) body["owner"] = Owner;
if (!string.IsNullOrEmpty(TagId)) body["tag"] = TagId;
if (PTRIgnore.IsPresent) body["PTRignore"] = "1";
if (!string.IsNullOrEmpty(PTRId)) body["PTR"] = PTRId;
if (!string.IsNullOrEmpty(Note)) body["note"] = Note;
if (ExcludePing.IsPresent) body["excludePing"] = "1";
if (!string.IsNullOrEmpty(DeviceId)) body["deviceId"] = DeviceId;
if (!string.IsNullOrEmpty(Port)) body["port"] = Port;
foreach (var kvp in ConvertCustomFields(CustomFields))
{ {
body[kvp.Key] = kvp.Value; dict[prop.Name] = prop.Value ?? new object();
} }
}
return body; else if (customFields is Dictionary<string, object> dictObj)
{
return dictObj;
}
return dict;
} }
} }

View File

@@ -1,22 +1,18 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Management.Automation; using System.Management.Automation;
using System.Threading.Tasks;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Creates a new phpIPAM API session.
/// </summary>
[Cmdlet(VerbsCommon.New, "Session", DefaultParameterSetName = "Credentials")] [Cmdlet(VerbsCommon.New, "Session", DefaultParameterSetName = "Credentials")]
[OutputType(typeof(Session))] public class NewSessionCmdlet : PSCmdlet
public class NewSessionCmdlet : BaseCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0)]
HelpMessage = "The phpIPAM server URL (must start with http:// or https://).")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
[ValidatePattern("^https?://")] [ValidatePattern("^https?://")]
public string URL { get; set; } = string.Empty; public string URL { get; set; } = string.Empty;
@@ -25,8 +21,7 @@ public class NewSessionCmdlet : BaseCmdlet
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 1, Position = 1)]
HelpMessage = "The API application ID configured in phpIPAM.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string AppID { get; set; } = string.Empty; public string AppID { get; set; } = string.Empty;
@@ -35,8 +30,7 @@ public class NewSessionCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 2, Position = 2,
ParameterSetName = "Credentials", ParameterSetName = "Credentials")]
HelpMessage = "The credentials (username and password) for authentication.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public PSCredential? Credentials { get; set; } public PSCredential? Credentials { get; set; }
@@ -45,8 +39,7 @@ public class NewSessionCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 2, Position = 2,
ParameterSetName = "Token", ParameterSetName = "Token")]
HelpMessage = "The static API token for authentication.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string? Token { get; set; } public string? Token { get; set; }
@@ -54,8 +47,7 @@ public class NewSessionCmdlet : BaseCmdlet
Mandatory = false, Mandatory = false,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 3, Position = 3)]
HelpMessage = "If specified, SSL certificate errors will be ignored.")]
public SwitchParameter IgnoreSSL { get; set; } public SwitchParameter IgnoreSSL { get; set; }
protected override void ProcessRecord() protected override void ProcessRecord()
@@ -63,10 +55,9 @@ public class NewSessionCmdlet : BaseCmdlet
try try
{ {
Session session; Session session;
if (ParameterSetName == "Credentials" && Credentials != null) if (ParameterSetName == "Credentials" && Credentials != null)
{ {
session = SessionManager.CreateSessionWithCredentialsAsync( session = SessionManager.CreateSessionWithCredentials(
URL, URL,
AppID, AppID,
Credentials, Credentials,
@@ -79,14 +70,12 @@ public class NewSessionCmdlet : BaseCmdlet
} }
else else
{ {
throw new ArgumentException("Invalid parameter set. Provide either Credentials or Token."); throw new ArgumentException("Invalid parameter set");
} }
WriteObject(session);
} }
catch (Exception ex) catch (Exception ex)
{ {
WriteError(new ErrorRecord(ex, "NewSessionError", ErrorCategory.AuthenticationError, null)); WriteError(new ErrorRecord(ex, "NewSessionError", ErrorCategory.InvalidOperation, null));
} }
} }
} }

View File

@@ -1,23 +1,20 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using System.Net;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Creates a new subnet in phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.New, "Subnet")] [Cmdlet(VerbsCommon.New, "Subnet")]
[OutputType(typeof(Subnetwork))] [OutputType(typeof(Subnetwork))]
public class NewSubnetCmdlet : BaseCmdlet public class NewSubnetCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0)]
HelpMessage = "The subnet in CIDR notation (e.g., 192.168.1.0/24).")]
[ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")] [ValidatePattern(@"^\d+\.\d+\.\d+\.\d+/\d{1,2}$")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public string CIDR { get; set; } = string.Empty; public string CIDR { get; set; } = string.Empty;
@@ -26,79 +23,187 @@ public class NewSubnetCmdlet : BaseCmdlet
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 1, Position = 1)]
HelpMessage = "The section ID to create the subnet in.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int SectionId { get; set; } public int SectionId { get; set; }
[Parameter(Mandatory = false, Position = 2, HelpMessage = "Description for the subnet.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 2)]
public string? Description { get; set; } public string? Description { get; set; }
[Parameter(Mandatory = false, Position = 3, HelpMessage = "VLAN ID to associate.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 3)]
public int? VlanId { get; set; } public int? VlanId { get; set; }
[Parameter(Mandatory = false, Position = 4, HelpMessage = "VRF ID to associate.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 4)]
public int? VrfId { get; set; } public int? VrfId { get; set; }
[Parameter(Mandatory = false, Position = 5, HelpMessage = "Master subnet ID for hierarchy.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 5)]
public int? MasterSubnetId { get; set; } public int? MasterSubnetId { get; set; }
[Parameter(Mandatory = false, Position = 6, HelpMessage = "Nameserver ID.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 6)]
public int? NameserverId { get; set; } public int? NameserverId { get; set; }
[Parameter(Mandatory = false, Position = 7, HelpMessage = "Show subnet name.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 7)]
public SwitchParameter ShowName { get; set; } public SwitchParameter ShowName { get; set; }
[Parameter(Mandatory = false, Position = 8, HelpMessage = "Enable recursive DNS.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 8)]
public SwitchParameter DNSRecursive { get; set; } public SwitchParameter DNSRecursive { get; set; }
[Parameter(Mandatory = false, Position = 9, HelpMessage = "Enable DNS records.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 9)]
public SwitchParameter DNSRecords { get; set; } public SwitchParameter DNSRecords { get; set; }
[Parameter(Mandatory = false, Position = 10, HelpMessage = "Allow IP requests.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 10)]
public SwitchParameter AllowRequests { get; set; } public SwitchParameter AllowRequests { get; set; }
[Parameter(Mandatory = false, Position = 11, HelpMessage = "Scan agent ID.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 11)]
public int? ScanAgentId { get; set; } public int? ScanAgentId { get; set; }
[Parameter(Mandatory = false, Position = 12, HelpMessage = "Enable subnet discovery.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 12)]
public SwitchParameter DiscoverSubnet { get; set; } public SwitchParameter DiscoverSubnet { get; set; }
[Parameter(Mandatory = false, Position = 13, HelpMessage = "Mark subnet as full.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 12)]
public SwitchParameter IsFull { get; set; } public SwitchParameter IsFull { get; set; }
[Parameter(Mandatory = false, Position = 14, HelpMessage = "Tag ID for the subnet.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 12)]
public int? TagId { get; set; } public int? TagId { get; set; }
[Parameter(Mandatory = false, Position = 15, HelpMessage = "Usage threshold percentage (1-100).")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 13)]
[ValidateRange(1, 100)] [ValidateRange(1, 100)]
public int? Threshold { get; set; } public int? Threshold { get; set; }
[Parameter(Mandatory = false, Position = 16, HelpMessage = "Location ID.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 14)]
public int? LocationId { get; set; } public int? LocationId { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Custom fields as a hashtable or PSObject.")] [Parameter(
Mandatory = false,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
Position = 15)]
public object? CustomFields { get; set; } public object? CustomFields { get; set; }
protected override void ProcessRecord() protected override void ProcessRecord()
{ {
try try
{ {
var body = BuildRequestBody(); var parts = CIDR.Split('/');
var body = new Dictionary<string, object>
{
{ "subnet", parts[0] },
{ "mask", parts[1] },
{ "sectionId", SectionId }
};
var result = RequestHelper.InvokeRequest( if (!string.IsNullOrEmpty(Description))
"POST", ApiController.Subnets, null, null, body body["description"] = Description;
).GetAwaiter().GetResult(); if (VlanId.HasValue)
body["vlanId"] = VlanId.Value;
if (VrfId.HasValue)
body["vrfId"] = VrfId.Value;
if (MasterSubnetId.HasValue)
body["masterSubnetId"] = MasterSubnetId.Value;
if (NameserverId.HasValue)
body["nameserverId"] = NameserverId.Value;
if (ShowName.IsPresent)
body["showName"] = "1";
if (DNSRecursive.IsPresent)
body["DNSrecursive"] = "1";
if (DNSRecords.IsPresent)
body["DNSrecords"] = "1";
if (AllowRequests.IsPresent)
body["allowRequests"] = "1";
if (ScanAgentId.HasValue)
body["scanAgent"] = ScanAgentId.Value;
if (DiscoverSubnet.IsPresent)
body["discoverSubnet"] = "1";
if (IsFull.IsPresent)
body["isFull"] = "1";
if (TagId.HasValue)
body["state"] = TagId.Value;
if (Threshold.HasValue)
body["threshold"] = Threshold.Value;
if (LocationId.HasValue)
body["location"] = LocationId.Value;
if (CustomFields != null)
{
var customDict = ConvertCustomFields(CustomFields);
foreach (var kvp in customDict)
{
body[kvp.Key] = kvp.Value;
}
}
var result = RequestHelper.InvokeRequest("POST", controllers.subnets, null, null, body, null)
.GetAwaiter().GetResult();
if (result != null) if (result != null)
{ {
// Get the created subnet // Get the created subnet
var subnet = RequestHelper.InvokeRequest( var subnet = RequestHelper.InvokeRequest("GET", controllers.subnets, types.Subnetwork, null, null, new[] { "cidr", CIDR })
"GET", ApiController.Subnets, ModelType.Subnetwork, null, null, .GetAwaiter().GetResult();
new[] { "cidr", CIDR } if (subnet != null)
).GetAwaiter().GetResult(); {
WriteObject(subnet);
WriteResult(subnet); }
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -107,37 +212,20 @@ public class NewSubnetCmdlet : BaseCmdlet
} }
} }
private Dictionary<string, object> BuildRequestBody() private Dictionary<string, object> ConvertCustomFields(object customFields)
{ {
var parts = CIDR.Split('/'); var dict = new Dictionary<string, object>();
var body = new Dictionary<string, object> if (customFields is PSObject psobj)
{ {
["subnet"] = parts[0], foreach (var prop in psobj.Properties)
["mask"] = parts[1],
["sectionId"] = SectionId
};
if (!string.IsNullOrEmpty(Description)) body["description"] = Description;
if (VlanId.HasValue) body["vlanId"] = VlanId.Value;
if (VrfId.HasValue) body["vrfId"] = VrfId.Value;
if (MasterSubnetId.HasValue) body["masterSubnetId"] = MasterSubnetId.Value;
if (NameserverId.HasValue) body["nameserverId"] = NameserverId.Value;
if (ShowName.IsPresent) body["showName"] = "1";
if (DNSRecursive.IsPresent) body["DNSrecursive"] = "1";
if (DNSRecords.IsPresent) body["DNSrecords"] = "1";
if (AllowRequests.IsPresent) body["allowRequests"] = "1";
if (ScanAgentId.HasValue) body["scanAgent"] = ScanAgentId.Value;
if (DiscoverSubnet.IsPresent) body["discoverSubnet"] = "1";
if (IsFull.IsPresent) body["isFull"] = "1";
if (TagId.HasValue) body["state"] = TagId.Value;
if (Threshold.HasValue) body["threshold"] = Threshold.Value;
if (LocationId.HasValue) body["location"] = LocationId.Value;
foreach (var kvp in ConvertCustomFields(CustomFields))
{ {
body[kvp.Key] = kvp.Value; dict[prop.Name] = prop.Value ?? new object();
} }
}
return body; else if (customFields is Dictionary<string, object> dictObj)
{
return dictObj;
}
return dict;
} }
} }

View File

@@ -1,31 +1,27 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary> [Cmdlet(VerbsCommon.Remove, "Address", DefaultParameterSetName = "ByID")]
/// Removes an IP address entry from phpIPAM. public class RemoveAddressCmdlet : PSCmdlet
/// </summary>
[Cmdlet(VerbsCommon.Remove, "Address", DefaultParameterSetName = "ById", SupportsShouldProcess = true)]
public class RemoveAddressCmdlet : BaseCmdlet
{ {
[Parameter(
Mandatory = true,
ValueFromPipeline = true,
Position = 0,
ParameterSetName = "ByID")]
[ValidateNotNullOrEmpty]
public int? Id { get; set; }
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ById", ParameterSetName = "ByAddressObject")]
HelpMessage = "The address ID to remove.")]
[ValidateNotNullOrEmpty]
public int Id { get; set; }
[Parameter(
Mandatory = true,
ValueFromPipeline = true,
Position = 0,
ParameterSetName = "ByAddressObject",
HelpMessage = "The address object to remove.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public Address? AddressObject { get; set; } public Address? AddressObject { get; set; }
@@ -33,17 +29,19 @@ public class RemoveAddressCmdlet : BaseCmdlet
{ {
try try
{ {
var addressId = ParameterSetName == "ById" ? Id : AddressObject!.Id; int addressId;
var identifiers = new[] { addressId.ToString() }; if (ParameterSetName == "ByID")
if (ShouldProcess($"Address ID: {addressId}", "Remove"))
{ {
RequestHelper.InvokeRequest( addressId = Id!.Value;
"DELETE", ApiController.Addresses, null, null, null, identifiers
).GetAwaiter().GetResult();
WriteVerbose($"Address {addressId} removed successfully.");
} }
else
{
addressId = AddressObject!.Id;
}
var identifiers = new List<string> { addressId.ToString() };
RequestHelper.InvokeRequest("DELETE", controllers.addresses, null, null, null, identifiers.ToArray())
.GetAwaiter().GetResult();
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,24 +1,20 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Updates an existing IP address entry in phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Set, "Address", DefaultParameterSetName = "ById")] [Cmdlet(VerbsCommon.Set, "Address", DefaultParameterSetName = "ById")]
[OutputType(typeof(Address))] [OutputType(typeof(Address))]
public class SetAddressCmdlet : BaseCmdlet public class SetAddressCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ById", ParameterSetName = "ById")]
HelpMessage = "The address ID to update.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int? Id { get; set; } public int? Id { get; set; }
@@ -27,72 +23,142 @@ public class SetAddressCmdlet : BaseCmdlet
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0,
ParameterSetName = "ByAddressObject", ParameterSetName = "ByAddressObject")]
HelpMessage = "The address object to update.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public Address? AddressObject { get; set; } public Address? AddressObject { get; set; }
[Parameter(Position = 1, HelpMessage = "Set as gateway address.")] [Parameter(
Position = 1,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
ParameterSetName = "ById")]
public bool? Gateway { get; set; } public bool? Gateway { get; set; }
[Parameter(Position = 2, HelpMessage = "Description for the address.")] [Parameter(
Position = 2,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
ParameterSetName = "ById")]
public string? Description { get; set; } public string? Description { get; set; }
[Parameter(Position = 3, HelpMessage = "Hostname for the address.")] [Parameter(
Mandatory = false,
Position = 3)]
public string? Hostname { get; set; } public string? Hostname { get; set; }
[Parameter(Position = 4, HelpMessage = "MAC address.")] [Parameter(
Mandatory = false,
Position = 4)]
public string? MAC { get; set; } public string? MAC { get; set; }
[Parameter(Position = 5, HelpMessage = "Owner of the address.")] [Parameter(
Mandatory = false,
Position = 5)]
public string? Owner { get; set; } public string? Owner { get; set; }
[Parameter(Position = 6, HelpMessage = "Tag ID for the address.")] [Parameter(
Mandatory = false,
Position = 6)]
public int? TagId { get; set; } public int? TagId { get; set; }
[Parameter(Position = 7, HelpMessage = "Ignore PTR record generation.")] [Parameter(
Mandatory = false,
Position = 7)]
public bool? PTRIgnore { get; set; } public bool? PTRIgnore { get; set; }
[Parameter(Position = 8, HelpMessage = "PTR record ID.")] [Parameter(
Mandatory = false,
Position = 8)]
public int? PTRId { get; set; } public int? PTRId { get; set; }
[Parameter(Position = 9, HelpMessage = "Note for the address.")] [Parameter(
Mandatory = false,
Position = 9)]
public string? Note { get; set; } public string? Note { get; set; }
[Parameter(Position = 10, HelpMessage = "Exclude from ping scanning.")] [Parameter(
Mandatory = false,
Position = 10)]
public bool? ExcludePing { get; set; } public bool? ExcludePing { get; set; }
[Parameter(Position = 11, HelpMessage = "Associated device ID.")] [Parameter(
Mandatory = false,
Position = 11)]
public int? DeviceId { get; set; } public int? DeviceId { get; set; }
[Parameter(Position = 12, HelpMessage = "Port information.")] [Parameter(
Mandatory = false,
Position = 12)]
public string? Port { get; set; } public string? Port { get; set; }
[Parameter(HelpMessage = "Custom fields as a hashtable or PSObject.")] [Parameter(
Mandatory = false)]
public object? CustomFields { get; set; } public object? CustomFields { get; set; }
protected override void ProcessRecord() protected override void ProcessRecord()
{ {
try try
{ {
var addressId = ParameterSetName == "ById" ? Id!.Value : AddressObject!.Id; int addressId;
var identifiers = new[] { addressId.ToString() }; if (ParameterSetName == "ById")
var body = BuildRequestBody(); {
addressId = Id!.Value;
}
else
{
addressId = AddressObject!.Id;
}
var identifiers = new List<string> { addressId.ToString() };
var body = new Dictionary<string, object>();
if (Gateway.HasValue)
body["is_gateway"] = Gateway.Value;
if (!string.IsNullOrEmpty(Description))
body["description"] = Description;
if (!string.IsNullOrEmpty(Hostname))
body["hostname"] = Hostname;
if (!string.IsNullOrEmpty(MAC))
body["mac"] = MAC;
if (!string.IsNullOrEmpty(Owner))
body["owner"] = Owner;
if (TagId.HasValue)
body["tag"] = TagId.Value;
if (PTRIgnore.HasValue)
body["PTRignore"] = PTRIgnore.Value;
if (PTRId.HasValue)
body["PTR"] = PTRId.Value;
if (!string.IsNullOrEmpty(Note))
body["note"] = Note;
if (ExcludePing.HasValue)
body["excludePing"] = ExcludePing.Value;
if (DeviceId.HasValue)
body["deviceId"] = DeviceId.Value;
if (!string.IsNullOrEmpty(Port))
body["port"] = Port;
if (CustomFields != null)
{
var customDict = ConvertCustomFields(CustomFields);
foreach (var kvp in customDict)
{
body[kvp.Key] = kvp.Value;
}
}
try try
{ {
RequestHelper.InvokeRequest( RequestHelper.InvokeRequest("PATCH", controllers.addresses, null, null, body, identifiers.ToArray())
"PATCH", ApiController.Addresses, null, null, body, identifiers .GetAwaiter().GetResult();
).GetAwaiter().GetResult();
} }
finally finally
{ {
// Always return the updated address var address = RequestHelper.InvokeRequest("GET", controllers.addresses, types.Address, null, null, identifiers.ToArray())
var address = RequestHelper.InvokeRequest( .GetAwaiter().GetResult();
"GET", ApiController.Addresses, ModelType.Address, null, null, identifiers if (address != null)
).GetAwaiter().GetResult(); {
WriteObject(address);
WriteResult(address); }
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -101,28 +167,20 @@ public class SetAddressCmdlet : BaseCmdlet
} }
} }
private Dictionary<string, object> BuildRequestBody() private Dictionary<string, object> ConvertCustomFields(object customFields)
{ {
var body = new Dictionary<string, object>(); var dict = new Dictionary<string, object>();
if (customFields is PSObject psobj)
if (Gateway.HasValue) body["is_gateway"] = Gateway.Value;
if (!string.IsNullOrEmpty(Description)) body["description"] = Description;
if (!string.IsNullOrEmpty(Hostname)) body["hostname"] = Hostname;
if (!string.IsNullOrEmpty(MAC)) body["mac"] = MAC;
if (!string.IsNullOrEmpty(Owner)) body["owner"] = Owner;
if (TagId.HasValue) body["tag"] = TagId.Value;
if (PTRIgnore.HasValue) body["PTRignore"] = PTRIgnore.Value;
if (PTRId.HasValue) body["PTR"] = PTRId.Value;
if (!string.IsNullOrEmpty(Note)) body["note"] = Note;
if (ExcludePing.HasValue) body["excludePing"] = ExcludePing.Value;
if (DeviceId.HasValue) body["deviceId"] = DeviceId.Value;
if (!string.IsNullOrEmpty(Port)) body["port"] = Port;
foreach (var kvp in ConvertCustomFields(CustomFields))
{ {
body[kvp.Key] = kvp.Value; foreach (var prop in psobj.Properties)
} {
dict[prop.Name] = prop.Value ?? new object();
return body; }
}
else if (customFields is Dictionary<string, object> dictObj)
{
return dictObj;
}
return dict;
} }
} }

View File

@@ -1,91 +1,143 @@
namespace PS.IPAM.Cmdlets; namespace PS.IPAM.Cmdlets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Management.Automation; using System.Management.Automation;
using PS.IPAM;
using PS.IPAM.Helpers; using PS.IPAM.Helpers;
/// <summary>
/// Updates an existing subnet in phpIPAM.
/// </summary>
[Cmdlet(VerbsCommon.Set, "Subnet")] [Cmdlet(VerbsCommon.Set, "Subnet")]
[OutputType(typeof(Subnetwork))] [OutputType(typeof(Subnetwork))]
public class SetSubnetCmdlet : BaseCmdlet public class SetSubnetCmdlet : PSCmdlet
{ {
[Parameter( [Parameter(
Mandatory = true, Mandatory = true,
ValueFromPipeline = true, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true, ValueFromPipelineByPropertyName = true,
Position = 0, Position = 0)]
HelpMessage = "The subnet ID to update.")]
[ValidateNotNullOrEmpty] [ValidateNotNullOrEmpty]
public int Id { get; set; } public int Id { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Description for the subnet.")] [Parameter(
Mandatory = false)]
public string? Description { get; set; } public string? Description { get; set; }
[Parameter(Mandatory = false, HelpMessage = "VLAN ID to associate.")] [Parameter(
Mandatory = false)]
public int? VlanId { get; set; } public int? VlanId { get; set; }
[Parameter(Mandatory = false, HelpMessage = "VRF ID to associate.")] [Parameter(
Mandatory = false)]
public int? VrfId { get; set; } public int? VrfId { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Master subnet ID for hierarchy.")] [Parameter(
Mandatory = false)]
public int? MasterSubnetId { get; set; } public int? MasterSubnetId { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Nameserver ID.")] [Parameter(
Mandatory = false)]
public int? NameserverId { get; set; } public int? NameserverId { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Show subnet name.")] [Parameter(
Mandatory = false)]
public SwitchParameter ShowName { get; set; } public SwitchParameter ShowName { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Enable recursive DNS.")] [Parameter(
Mandatory = false)]
public SwitchParameter DNSRecursive { get; set; } public SwitchParameter DNSRecursive { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Enable DNS records.")] [Parameter(
Mandatory = false)]
public SwitchParameter DNSRecords { get; set; } public SwitchParameter DNSRecords { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Allow IP requests.")] [Parameter(
Mandatory = false)]
public SwitchParameter AllowRequests { get; set; } public SwitchParameter AllowRequests { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Scan agent ID.")] [Parameter(
Mandatory = false)]
public int? ScanAgentId { get; set; } public int? ScanAgentId { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Enable subnet discovery.")] [Parameter(
Mandatory = false)]
public SwitchParameter DiscoverSubnet { get; set; } public SwitchParameter DiscoverSubnet { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Mark subnet as full.")] [Parameter(
Mandatory = false)]
public SwitchParameter IsFull { get; set; } public SwitchParameter IsFull { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Tag ID for the subnet.")] [Parameter(
Mandatory = false)]
public int? TagId { get; set; } public int? TagId { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Usage threshold percentage (1-100).")] [Parameter(
Mandatory = false)]
[ValidateRange(1, 100)] [ValidateRange(1, 100)]
public int? Threshold { get; set; } public int? Threshold { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Location ID.")] [Parameter(
Mandatory = false)]
public int? LocationId { get; set; } public int? LocationId { get; set; }
[Parameter(Mandatory = false, HelpMessage = "Custom fields as a hashtable or PSObject.")] [Parameter(
Mandatory = false)]
public object? CustomFields { get; set; } public object? CustomFields { get; set; }
protected override void ProcessRecord() protected override void ProcessRecord()
{ {
try try
{ {
var identifiers = new[] { Id.ToString() }; var identifiers = new List<string> { Id.ToString() };
var body = BuildRequestBody(); var body = new Dictionary<string, object>();
RequestHelper.InvokeRequest( if (!string.IsNullOrEmpty(Description))
"PATCH", ApiController.Subnets, null, null, body, identifiers body["description"] = Description;
).GetAwaiter().GetResult(); if (VlanId.HasValue)
body["vlanId"] = VlanId.Value;
if (VrfId.HasValue)
body["vrfId"] = VrfId.Value;
if (MasterSubnetId.HasValue)
body["masterSubnetId"] = MasterSubnetId.Value;
if (NameserverId.HasValue)
body["nameserverId"] = NameserverId.Value;
if (ShowName.IsPresent)
body["showName"] = "1";
if (DNSRecursive.IsPresent)
body["DNSrecursive"] = "1";
if (DNSRecords.IsPresent)
body["DNSrecords"] = "1";
if (AllowRequests.IsPresent)
body["allowRequests"] = "1";
if (ScanAgentId.HasValue)
body["scanAgent"] = ScanAgentId.Value;
if (DiscoverSubnet.IsPresent)
body["discoverSubnet"] = "1";
if (IsFull.IsPresent)
body["isFull"] = "1";
if (TagId.HasValue)
body["state"] = TagId.Value;
if (Threshold.HasValue)
body["threshold"] = Threshold.Value;
if (LocationId.HasValue)
body["location"] = LocationId.Value;
var subnet = RequestHelper.InvokeRequest( if (CustomFields != null)
"GET", ApiController.Subnets, ModelType.Subnetwork, null, null, identifiers {
).GetAwaiter().GetResult(); var customDict = ConvertCustomFields(CustomFields);
foreach (var kvp in customDict)
{
body[kvp.Key] = kvp.Value;
}
}
WriteResult(subnet); RequestHelper.InvokeRequest("PATCH", controllers.subnets, null, null, body, identifiers.ToArray())
.GetAwaiter().GetResult();
var subnet = RequestHelper.InvokeRequest("GET", controllers.subnets, types.Subnetwork, null, null, identifiers.ToArray())
.GetAwaiter().GetResult();
if (subnet != null)
{
WriteObject(subnet);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -93,31 +145,20 @@ public class SetSubnetCmdlet : BaseCmdlet
} }
} }
private Dictionary<string, object> BuildRequestBody() private Dictionary<string, object> ConvertCustomFields(object customFields)
{ {
var body = new Dictionary<string, object>(); var dict = new Dictionary<string, object>();
if (customFields is PSObject psobj)
if (!string.IsNullOrEmpty(Description)) body["description"] = Description;
if (VlanId.HasValue) body["vlanId"] = VlanId.Value;
if (VrfId.HasValue) body["vrfId"] = VrfId.Value;
if (MasterSubnetId.HasValue) body["masterSubnetId"] = MasterSubnetId.Value;
if (NameserverId.HasValue) body["nameserverId"] = NameserverId.Value;
if (ShowName.IsPresent) body["showName"] = "1";
if (DNSRecursive.IsPresent) body["DNSrecursive"] = "1";
if (DNSRecords.IsPresent) body["DNSrecords"] = "1";
if (AllowRequests.IsPresent) body["allowRequests"] = "1";
if (ScanAgentId.HasValue) body["scanAgent"] = ScanAgentId.Value;
if (DiscoverSubnet.IsPresent) body["discoverSubnet"] = "1";
if (IsFull.IsPresent) body["isFull"] = "1";
if (TagId.HasValue) body["state"] = TagId.Value;
if (Threshold.HasValue) body["threshold"] = Threshold.Value;
if (LocationId.HasValue) body["location"] = LocationId.Value;
foreach (var kvp in ConvertCustomFields(CustomFields))
{ {
body[kvp.Key] = kvp.Value; foreach (var prop in psobj.Properties)
} {
dict[prop.Name] = prop.Value ?? new object();
return body; }
}
else if (customFields is Dictionary<string, object> dictObj)
{
return dictObj;
}
return dict;
} }
} }

View File

@@ -1,5 +1,4 @@
namespace PS.IPAM.Helpers; namespace PS.IPAM.Helpers;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -10,46 +9,89 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PS.IPAM;
/// <summary>
/// Helper class for making HTTP requests to the phpIPAM API.
/// </summary>
public static class RequestHelper public static class RequestHelper
{ {
/// <summary> // Handler for testing - allows injecting a mock HTTP handler
/// Handler for testing - allows injecting a mock HTTP handler.
/// </summary>
public static HttpMessageHandler? TestHttpHandler { get; set; } public static HttpMessageHandler? TestHttpHandler { get; set; }
/// <summary> public static async Task<object?> InvokeRequest(
/// Invokes an HTTP request to the phpIPAM API. string method,
/// </summary> controllers controller,
/// <param name="method">The HTTP method (GET, POST, PATCH, DELETE).</param> types? type = null,
/// <param name="controller">The API controller to call.</param> subcontrollers? subController = null,
/// <param name="modelType">The expected model type for response conversion.</param>
/// <param name="subController">Optional sub-controller for nested endpoints.</param>
/// <param name="parameters">Optional request body parameters.</param>
/// <param name="identifiers">Optional path identifiers.</param>
/// <param name="ignoreSsl">Whether to ignore SSL certificate errors.</param>
/// <returns>The deserialized response data, or null if not found.</returns>
public static async Task<object?> InvokeRequestAsync(
HttpMethod method,
ApiController controller,
ModelType? modelType = null,
ApiSubController? subController = null,
object? parameters = null, object? parameters = null,
string[]? identifiers = null, string[]? identifiers = null,
bool ignoreSsl = false) bool ignoreSsl = false)
{ {
EnsureValidSession(); var tokenStatus = SessionManager.TestSession();
if (tokenStatus == "NoToken")
{
throw new Exception("No session available!");
}
var session = SessionManager.CurrentSession!; if (tokenStatus == "Expired")
var uri = BuildUri(session, controller, subController, identifiers); {
await UpdateSession();
}
var session = SessionManager.CurrentSession;
if (session == null)
{
throw new Exception("No session available!");
}
var uri = $"{session.URL}/api/{session.AppID}/{controller}";
if (subController != null)
{
uri += $"/{subController}";
}
if (identifiers != null && identifiers.Length > 0)
{
uri += $"/{string.Join("/", identifiers)}/";
}
using var client = SessionManager.CreateHttpClient(ignoreSsl, TestHttpHandler); using var client = SessionManager.CreateHttpClient(ignoreSsl, TestHttpHandler);
ConfigureClient(client, session); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await SendRequestAsync(client, method, uri, parameters); switch (session.AuthType)
{
case AuthType.credentials:
client.DefaultRequestHeaders.Add("token", session.Token);
break;
case AuthType.token:
client.DefaultRequestHeaders.Add("phpipam-token", session.Token);
break;
}
HttpResponseMessage? response = null;
try
{
if (method == "GET")
{
response = await client.GetAsync(uri);
}
else if (method == "POST")
{
var jsonContent = parameters != null ? JsonConvert.SerializeObject(parameters) : "{}";
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
response = await client.PostAsync(uri, content);
}
else if (method == "PATCH")
{
var jsonContent = parameters != null ? JsonConvert.SerializeObject(parameters) : "{}";
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(new HttpMethod("PATCH"), uri)
{
Content = content
};
response = await client.SendAsync(request);
}
else if (method == "DELETE")
{
response = await client.DeleteAsync(uri);
}
if (response == null) if (response == null)
{ {
@@ -72,197 +114,30 @@ public static class RequestHelper
return null; return null;
} }
return ParseResponse(responseContent, modelType);
}
/// <summary>
/// Overload for backward compatibility using string method names.
/// </summary>
public static Task<object?> InvokeRequest(
string method,
ApiController controller,
ModelType? modelType = null,
ApiSubController? subController = null,
object? parameters = null,
string[]? identifiers = null,
bool ignoreSsl = false)
{
var httpMethod = method.ToUpperInvariant() switch
{
"GET" => HttpMethod.Get,
"POST" => HttpMethod.Post,
"PATCH" => new HttpMethod("PATCH"),
"DELETE" => HttpMethod.Delete,
"PUT" => HttpMethod.Put,
_ => throw new ArgumentException($"Unsupported HTTP method: {method}", nameof(method))
};
return InvokeRequestAsync(httpMethod, controller, modelType, subController, parameters, identifiers, ignoreSsl);
}
/// <summary>
/// Refreshes an expired session.
/// </summary>
public static async Task RefreshSessionAsync()
{
var session = SessionManager.CurrentSession;
if (session == null)
{
throw new InvalidOperationException("No session available!");
}
var status = SessionManager.GetSessionStatus();
if (status == SessionStatus.Valid)
{
// Just refresh the token
await InvokeRequestAsync(new HttpMethod("PATCH"), ApiController.User);
return;
}
if (status == SessionStatus.Expired && session.Credentials is PSCredential creds)
{
await SessionManager.CreateSessionWithCredentialsAsync(
session.URL,
session.AppID,
creds,
false
);
}
}
private static void EnsureValidSession()
{
var status = SessionManager.GetSessionStatus();
switch (status)
{
case SessionStatus.NoSession:
throw new InvalidOperationException("No session available!");
case SessionStatus.Expired:
RefreshSessionAsync().GetAwaiter().GetResult();
break;
}
}
private static string BuildUri(
Session session,
ApiController controller,
ApiSubController? subController,
string[]? identifiers)
{
var controllerName = GetControllerName(controller);
var uri = $"{session.URL}/api/{session.AppID}/{controllerName}";
if (subController.HasValue)
{
uri += $"/{GetSubControllerName(subController.Value)}";
}
if (identifiers is { Length: > 0 })
{
uri += $"/{string.Join("/", identifiers)}/";
}
return uri;
}
private static string GetControllerName(ApiController controller) => controller switch
{
ApiController.User => "user",
ApiController.Vlan => "vlan",
ApiController.Subnets => "subnets",
ApiController.Addresses => "addresses",
ApiController.Sections => "sections",
ApiController.Vrf => "vrf",
ApiController.L2Domains => "l2domains",
ApiController.Tools => "tools",
_ => throw new ArgumentOutOfRangeException(nameof(controller))
};
private static string GetSubControllerName(ApiSubController subController) => subController switch
{
ApiSubController.Nameservers => "nameservers",
ApiSubController.Tags => "tags",
ApiSubController.Devices => "devices",
ApiSubController.DeviceTypes => "device_types",
ApiSubController.Vlans => "vlans",
ApiSubController.Vrfs => "vrfs",
ApiSubController.ScanAgents => "scanagents",
ApiSubController.Locations => "locations",
ApiSubController.Nat => "nat",
ApiSubController.Racks => "racks",
_ => throw new ArgumentOutOfRangeException(nameof(subController))
};
private static void ConfigureClient(HttpClient client, Session session)
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
switch (session.AuthType)
{
case AuthType.Credentials:
client.DefaultRequestHeaders.Add("token", session.Token);
break;
case AuthType.Token:
client.DefaultRequestHeaders.Add("phpipam-token", session.Token);
break;
}
}
private static async Task<HttpResponseMessage?> SendRequestAsync(
HttpClient client,
HttpMethod method,
string uri,
object? parameters)
{
try
{
if (method == HttpMethod.Get)
{
return await client.GetAsync(uri);
}
if (method == HttpMethod.Delete)
{
return await client.DeleteAsync(uri);
}
var jsonContent = parameters != null ? JsonConvert.SerializeObject(parameters) : "{}";
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
if (method == HttpMethod.Post)
{
return await client.PostAsync(uri, content);
}
// PATCH, PUT, etc.
var request = new HttpRequestMessage(method, uri) { Content = content };
return await client.SendAsync(request);
}
catch (HttpRequestException ex) when (ex.Message.Contains("404"))
{
return null;
}
}
private static object? ParseResponse(string responseContent, ModelType? modelType)
{
var jsonResponse = JsonConvert.DeserializeObject<dynamic>(responseContent); var jsonResponse = JsonConvert.DeserializeObject<dynamic>(responseContent);
if (jsonResponse == null) if (jsonResponse == null)
{ {
return null; return null;
} }
if (!modelType.HasValue) if (type.HasValue)
{ {
return ConvertToTypedObjects(jsonResponse, type.Value);
}
return jsonResponse.data; return jsonResponse.data;
} }
catch (HttpRequestException ex)
return ConvertToTypedObjects(jsonResponse, modelType.Value); {
if (ex.Message.Contains("404"))
{
return null;
}
throw;
}
} }
private static object? ConvertToTypedObjects(dynamic jsonResponse, ModelType modelType) private static object? ConvertToTypedObjects(dynamic jsonResponse, types type)
{ {
if (jsonResponse?.data == null) if (jsonResponse?.data == null)
{ {
@@ -272,170 +147,228 @@ public static class RequestHelper
var data = jsonResponse.data; var data = jsonResponse.data;
if (data is JArray array) if (data is JArray array)
{ {
return array.Select(item => ConvertSingleObject(item, modelType)).ToList(); return array.Select(item => ConvertSingleObject(item, type)).ToList();
} }
else
return ConvertSingleObject(data, modelType);
}
private static object? ConvertSingleObject(dynamic item, ModelType modelType)
{ {
if (item is not JObject jobject) return ConvertSingleObject(data, type);
}
}
private static object? ConvertSingleObject(dynamic item, types type)
{
var jobject = item as JObject;
if (jobject == null)
{ {
return null; return null;
} }
var customFields = ExtractCustomFields(jobject);
return modelType switch
{
ModelType.Address => CreateAddress(jobject, customFields),
ModelType.Vlan => CreateVlan(jobject, customFields),
ModelType.Subnetwork => CreateSubnetwork(jobject, customFields),
ModelType.Vrf => CreateVrf(jobject, customFields),
ModelType.Section => CreateSection(jobject),
ModelType.Tag => CreateTag(jobject),
ModelType.Nameserver => CreateNameserver(jobject),
ModelType.Domain => CreateDomain(jobject),
_ => jobject.ToObject<object>()
};
}
private static Dictionary<string, object>? ExtractCustomFields(JObject jobject)
{
var customFields = new Dictionary<string, object>(); var customFields = new Dictionary<string, object>();
foreach (var prop in jobject.Properties().Where(p => p.Name.StartsWith("custom_"))) foreach (var prop in jobject.Properties())
{ {
customFields[prop.Name] = prop.Value?.ToObject<object>() ?? string.Empty; if (prop.Name.StartsWith("custom_"))
{
customFields[prop.Name] = prop.Value?.ToObject<object>() ?? new object();
} }
return customFields.Count > 0 ? customFields : null;
} }
#region Model Factory Methods switch (type)
{
private static Address CreateAddress(JObject obj, Dictionary<string, object>? customFields) => new( case types.Address:
obj["id"]?.ToObject<int>() ?? 0, return CreateAddress(jobject, customFields);
obj["subnetId"]?.ToObject<int>() ?? 0, case types.Vlan:
obj["ip"]?.ToString() ?? "", return CreateVlan(jobject, customFields);
obj["is_gateway"]?.ToObject<bool>() ?? false, case types.Subnetwork:
obj["description"]?.ToString() ?? "", return CreateSubnetwork(jobject, customFields);
obj["hostname"]?.ToString() ?? "", case types.Vrf:
obj["mac"]?.ToString() ?? "", return CreateVrf(jobject, customFields);
obj["owner"]?.ToString() ?? "", case types.Section:
obj["tag"]?.ToObject<int>() ?? 0, return CreateSection(jobject);
obj["deviceId"]?.ToObject<int>() ?? 0, case types.Tag:
obj["location"]?.ToString() ?? "", return CreateTag(jobject);
obj["port"]?.ToString() ?? "", case types.Nameserver:
obj["note"]?.ToString() ?? "", return CreateNameserver(jobject);
obj["lastSeen"]?.ToObject<DateTime?>(), case types.Domain:
obj["excludePing"]?.ToObject<bool>() ?? false, return CreateDomain(jobject);
obj["PTRignore"]?.ToObject<bool>() ?? false, default:
obj["PTR"]?.ToObject<int>() ?? 0, return jobject.ToObject<object>();
obj["firewallAddressObject"]?.ToString() ?? "", }
obj["editDate"]?.ToObject<DateTime?>(), }
obj["customer_id"]?.ToObject<int>() ?? 0,
customFields private static Address CreateAddress(JObject jobject, Dictionary<string, object> customFields)
); {
return new Address(
private static Vlan CreateVlan(JObject obj, Dictionary<string, object>? customFields) => new( jobject["id"]?.ToObject<int>() ?? 0,
obj["vlanId"]?.ToObject<int>() ?? 0, jobject["subnetId"]?.ToObject<int>() ?? 0,
obj["domainId"]?.ToObject<int>() ?? 0, jobject["ip"]?.ToString() ?? "",
obj["name"]?.ToString() ?? "", jobject["is_gateway"]?.ToObject<bool>() ?? false,
obj["number"]?.ToObject<int>() ?? 0, jobject["description"]?.ToString() ?? "",
obj["description"]?.ToString() ?? "", jobject["hostname"]?.ToString() ?? "",
obj["editDate"]?.ToObject<DateTime?>(), jobject["mac"]?.ToString() ?? "",
obj["customer_id"]?.ToObject<int>() ?? 0, jobject["owner"]?.ToString() ?? "",
customFields jobject["tag"]?.ToObject<int>() ?? 0,
); jobject["deviceId"]?.ToObject<int>() ?? 0,
jobject["location"]?.ToString() ?? "",
private static Subnetwork CreateSubnetwork(JObject obj, Dictionary<string, object>? customFields) => new( jobject["port"]?.ToString() ?? "",
obj["id"]?.ToObject<int>() ?? 0, jobject["note"]?.ToString() ?? "",
obj["subnet"]?.ToString() ?? "", jobject["lastSeen"]?.ToObject<DateTime?>(),
obj["mask"]?.ToObject<int>() ?? 0, jobject["excludePing"]?.ToObject<bool>() ?? false,
obj["sectionId"]?.ToObject<int>() ?? 0, jobject["PTRignore"]?.ToObject<bool>() ?? false,
obj["description"]?.ToString() ?? "", jobject["PTR"]?.ToObject<int>() ?? 0,
obj["linked_subnet"]?.ToString() ?? "", jobject["firewallAddressObject"]?.ToString() ?? "",
obj["firewallAddressObject"]?.ToString() ?? "", jobject["editDate"]?.ToObject<DateTime?>(),
obj["vrfId"]?.ToObject<int>() ?? 0, jobject["customer_id"]?.ToObject<int>() ?? 0,
obj["masterSubnetId"]?.ToObject<int>() ?? 0, customFields.Count > 0 ? customFields : null
obj["allowRequests"]?.ToObject<bool>() ?? false, );
obj["vlanId"]?.ToObject<int>() ?? 0, }
obj["showName"]?.ToObject<bool>() ?? false,
obj["deviceId"]?.ToObject<int>() ?? 0, private static Vlan CreateVlan(JObject jobject, Dictionary<string, object> customFields)
obj["permissions"]?.ToString() ?? "", {
obj["pingSubnet"]?.ToObject<bool>() ?? false, return new Vlan(
obj["discoverSubnet"]?.ToObject<bool>() ?? false, jobject["vlanId"]?.ToObject<int>() ?? 0,
obj["resolveDNS"]?.ToObject<bool>() ?? false, jobject["domainId"]?.ToObject<int>() ?? 0,
obj["DNSrecursive"]?.ToObject<bool>() ?? false, jobject["name"]?.ToString() ?? "",
obj["DNSrecords"]?.ToObject<bool>() ?? false, jobject["number"]?.ToObject<int>() ?? 0,
obj["nameserverId"]?.ToObject<int>() ?? 0, jobject["description"]?.ToString() ?? "",
obj["scanAgent"]?.ToObject<bool>() ?? false, jobject["editDate"]?.ToObject<DateTime?>(),
obj["isFolder"]?.ToObject<bool>() ?? false, jobject["customer_id"]?.ToObject<int>() ?? 0,
obj["isFull"]?.ToObject<bool>() ?? false, customFields.Count > 0 ? customFields : null
obj["isPool"]?.ToObject<bool>() ?? false, );
obj["state"]?.ToObject<int>() ?? 0, }
obj["threshold"]?.ToObject<int>() ?? 0,
obj["location"]?.ToObject<int>() ?? 0, private static Subnetwork CreateSubnetwork(JObject jobject, Dictionary<string, object> customFields)
obj["editDate"]?.ToObject<DateTime?>(), {
obj["lastScan"]?.ToObject<DateTime?>(), var props = jobject.Properties().ToList();
obj["lastDiscovery"]?.ToObject<DateTime?>(), return new Subnetwork(
obj["calculation"]?.ToObject<object>() ?? new object(), jobject["id"]?.ToObject<int>() ?? 0,
customFields jobject["subnet"]?.ToString() ?? "",
); jobject["mask"]?.ToObject<int>() ?? 0,
jobject["sectionId"]?.ToObject<int>() ?? 0,
private static Vrf CreateVrf(JObject obj, Dictionary<string, object>? customFields) => new( jobject["description"]?.ToString() ?? "",
obj["id"]?.ToObject<int>() ?? 0, jobject["linked_subnet"]?.ToString() ?? "",
obj["name"]?.ToString() ?? "", jobject["firewallAddressObject"]?.ToString() ?? "",
obj["rd"]?.ToString() ?? "", jobject["vrfId"]?.ToObject<int>() ?? 0,
obj["description"]?.ToString() ?? "", jobject["masterSubnetId"]?.ToObject<int>() ?? 0,
obj["sections"]?.ToString() ?? "", jobject["allowRequests"]?.ToObject<bool>() ?? false,
obj["editDate"]?.ToObject<DateTime?>(), jobject["vlanId"]?.ToObject<int>() ?? 0,
customFields jobject["showName"]?.ToObject<bool>() ?? false,
); jobject["deviceId"]?.ToObject<int>() ?? 0,
jobject["permissions"]?.ToString() ?? "",
private static Section CreateSection(JObject obj) => new( jobject["pingSubnet"]?.ToObject<bool>() ?? false,
obj["id"]?.ToObject<int>() ?? 0, jobject["discoverSubnet"]?.ToObject<bool>() ?? false,
obj["name"]?.ToString() ?? "", jobject["resolveDNS"]?.ToObject<bool>() ?? false,
obj["description"]?.ToString() ?? "", jobject["DNSrecursive"]?.ToObject<bool>() ?? false,
obj["masterSection"]?.ToObject<int>() ?? 0, jobject["DNSrecords"]?.ToObject<bool>() ?? false,
obj["permissions"]?.ToString() ?? "", jobject["nameserverId"]?.ToObject<int>() ?? 0,
obj["strictMode"]?.ToObject<bool>() ?? false, jobject["scanAgent"]?.ToObject<bool>() ?? false,
obj["subnetOrdering"]?.ToString() ?? "", jobject["isFolder"]?.ToObject<bool>() ?? false,
obj["order"]?.ToObject<int>() ?? 0, jobject["isFull"]?.ToObject<bool>() ?? false,
obj["editDate"]?.ToObject<DateTime?>(), jobject["isPool"]?.ToObject<bool>() ?? false,
obj["showSubnet"]?.ToObject<bool>() ?? false, jobject["state"]?.ToObject<int>() ?? 0,
obj["showVlan"]?.ToObject<bool>() ?? false, jobject["threshold"]?.ToObject<int>() ?? 0,
obj["showVRF"]?.ToObject<bool>() ?? false, jobject["location"]?.ToObject<int>() ?? 0,
obj["showSupernetOnly"]?.ToObject<bool>() ?? false, jobject["editDate"]?.ToObject<DateTime?>(),
obj["DNS"]?.ToObject<int>() ?? 0 jobject["lastScan"]?.ToObject<DateTime?>(),
); jobject["lastDiscovery"]?.ToObject<DateTime?>(),
jobject["calculation"]?.ToObject<object>() ?? new object(),
private static Tag CreateTag(JObject obj) => new( customFields.Count > 0 ? customFields : null
obj["id"]?.ToObject<int>() ?? 0, );
obj["type"]?.ToString() ?? "", }
obj["showtag"]?.ToObject<bool>() ?? false,
obj["bgcolor"]?.ToString() ?? "", private static Vrf CreateVrf(JObject jobject, Dictionary<string, object> customFields)
obj["fgcolor"]?.ToString() ?? "", {
obj["compress"]?.ToString() ?? "", return new Vrf(
obj["locked"]?.ToString() ?? "", jobject["id"]?.ToObject<int>() ?? 0,
obj["updateTag"]?.ToObject<bool>() ?? false jobject["name"]?.ToString() ?? "",
); jobject["rd"]?.ToString() ?? "",
jobject["description"]?.ToString() ?? "",
private static Nameserver CreateNameserver(JObject obj) => new( jobject["sections"]?.ToString() ?? "",
obj["id"]?.ToObject<int>() ?? 0, jobject["editDate"]?.ToObject<DateTime?>(),
obj["name"]?.ToString() ?? "", customFields.Count > 0 ? customFields : null
obj["nameservers"]?.ToString() ?? "", );
obj["description"]?.ToString() ?? "", }
obj["permissions"]?.ToString() ?? "",
obj["editDate"]?.ToObject<DateTime?>() private static Section CreateSection(JObject jobject)
); {
return new Section(
private static Domain CreateDomain(JObject obj) => new( jobject["id"]?.ToObject<int>() ?? 0,
obj["id"]?.ToObject<int>() ?? 0, jobject["name"]?.ToString() ?? "",
obj["name"]?.ToString() ?? "", jobject["description"]?.ToString() ?? "",
obj["description"]?.ToString() ?? "", jobject["masterSection"]?.ToObject<int>() ?? 0,
obj["sections"]?.ToString() ?? "" jobject["permissions"]?.ToString() ?? "",
); jobject["strictMode"]?.ToObject<bool>() ?? false,
jobject["subnetOrdering"]?.ToString() ?? "",
#endregion jobject["order"]?.ToObject<int>() ?? 0,
jobject["editDate"]?.ToObject<DateTime?>(),
jobject["showSubnet"]?.ToObject<bool>() ?? false,
jobject["showVlan"]?.ToObject<bool>() ?? false,
jobject["showVRF"]?.ToObject<bool>() ?? false,
jobject["showSupernetOnly"]?.ToObject<bool>() ?? false,
jobject["DNS"]?.ToObject<int>() ?? 0
);
}
private static Tag CreateTag(JObject jobject)
{
return new Tag(
jobject["id"]?.ToObject<int>() ?? 0,
jobject["type"]?.ToString() ?? "",
jobject["showtag"]?.ToObject<bool>() ?? false,
jobject["bgcolor"]?.ToString() ?? "",
jobject["fgcolor"]?.ToString() ?? "",
jobject["compress"]?.ToString() ?? "",
jobject["locked"]?.ToString() ?? "",
jobject["updateTag"]?.ToObject<bool>() ?? false
);
}
private static Nameserver CreateNameserver(JObject jobject)
{
return new Nameserver(
jobject["id"]?.ToObject<int>() ?? 0,
jobject["name"]?.ToString() ?? "",
jobject["nameservers"]?.ToString() ?? "",
jobject["description"]?.ToString() ?? "",
jobject["permissions"]?.ToString() ?? "",
jobject["editDate"]?.ToObject<DateTime?>()
);
}
private static Domain CreateDomain(JObject jobject)
{
return new Domain(
jobject["id"]?.ToObject<int>() ?? 0,
jobject["name"]?.ToString() ?? "",
jobject["description"]?.ToString() ?? "",
jobject["sections"]?.ToString() ?? ""
);
}
private static async Task UpdateSession()
{
var session = SessionManager.CurrentSession;
if (session == null)
{
throw new Exception("No session available!");
}
var tokenStatus = SessionManager.TestSession();
if (tokenStatus == "Valid")
{
// Just refresh the token
var result = await InvokeRequest("PATCH", controllers.user, null, null, null, null);
// Token refresh doesn't return expires in the same format, so we'll skip updating expires
return;
}
if (tokenStatus == "Expired" && session.Credentials is PSCredential creds)
{
await SessionManager.CreateSessionWithCredentials(
session.URL,
session.AppID,
creds,
false
);
}
}
} }

View File

@@ -1,60 +1,44 @@
namespace PS.IPAM.Helpers; namespace PS.IPAM.Helpers;
using System; using System;
using System.Management.Automation; using System.Management.Automation;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using PS.IPAM;
/// <summary>
/// Manages phpIPAM API sessions including creation, validation, and lifecycle.
/// </summary>
public static class SessionManager public static class SessionManager
{ {
private static Session? _currentSession; private static Session? _currentSession;
/// <summary>
/// Gets or sets the current active session.
/// </summary>
public static Session? CurrentSession public static Session? CurrentSession
{ {
get => _currentSession; get => _currentSession;
set => _currentSession = value; set => _currentSession = value;
} }
/// <summary> public static string TestSession()
/// Tests the current session status.
/// </summary>
/// <returns>The session status indicating validity or issues.</returns>
public static SessionStatus GetSessionStatus()
{ {
if (_currentSession == null) if (_currentSession == null)
{ {
return SessionStatus.NoSession; return "NoToken";
} }
if (_currentSession.Expires == null) if (_currentSession.Expires == null)
{ {
return SessionStatus.Valid; return "Valid";
} }
return _currentSession.Expires < DateTime.Now if (_currentSession.Expires < DateTime.Now)
? SessionStatus.Expired {
: SessionStatus.Valid; return "Expired";
} }
/// <summary> return "Valid";
/// Creates a new session using username/password credentials. }
/// </summary>
/// <param name="url">The phpIPAM server URL.</param> public static async Task<Session> CreateSessionWithCredentials(
/// <param name="appId">The API application ID.</param>
/// <param name="credentials">The PowerShell credential object.</param>
/// <param name="ignoreSsl">Whether to ignore SSL certificate errors.</param>
/// <returns>The created session.</returns>
public static async Task<Session> CreateSessionWithCredentialsAsync(
string url, string url,
string appId, string appId,
PSCredential credentials, PSCredential credentials,
@@ -62,7 +46,7 @@ public static class SessionManager
{ {
var uri = $"{url}/api/{appId}/user"; var uri = $"{url}/api/{appId}/user";
var auth = Convert.ToBase64String( var auth = Convert.ToBase64String(
Encoding.UTF8.GetBytes($"{credentials.UserName}:{GetPasswordString(credentials)}")); Encoding.UTF8.GetBytes($"{credentials.UserName}:{GetPassword(credentials)}"));
using var client = CreateHttpClient(ignoreSsl); using var client = CreateHttpClient(ignoreSsl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
@@ -74,15 +58,14 @@ public static class SessionManager
if (jsonResponse?.success != true) if (jsonResponse?.success != true)
{ {
throw new InvalidOperationException( throw new Exception(jsonResponse?.error?.ToString() ?? "Failed to create session");
jsonResponse?.error?.ToString() ?? "Failed to create session");
} }
var token = jsonResponse.data.token.ToString(); var token = jsonResponse.data.token.ToString();
var expires = DateTime.Parse(jsonResponse.data.expires.ToString()); var expires = DateTime.Parse(jsonResponse.data.expires.ToString());
_currentSession = new Session( _currentSession = new Session(
AuthType.Credentials, AuthType.credentials,
token, token,
appId, appId,
url, url,
@@ -93,17 +76,13 @@ public static class SessionManager
return _currentSession; return _currentSession;
} }
/// <summary> public static Session CreateSessionWithToken(
/// Creates a new session using a static API token. string url,
/// </summary> string appId,
/// <param name="url">The phpIPAM server URL.</param> string token)
/// <param name="appId">The API application ID.</param>
/// <param name="token">The API token.</param>
/// <returns>The created session.</returns>
public static Session CreateSessionWithToken(string url, string appId, string token)
{ {
_currentSession = new Session( _currentSession = new Session(
AuthType.Token, AuthType.token,
token, token,
appId, appId,
url, url,
@@ -114,20 +93,24 @@ public static class SessionManager
return _currentSession; return _currentSession;
} }
/// <summary>
/// Closes the current session and clears session data.
/// </summary>
public static void CloseSession() public static void CloseSession()
{ {
_currentSession = null; _currentSession = null;
} }
/// <summary> private static string GetPassword(PSCredential credential)
/// Creates an HttpClient with optional SSL bypass. {
/// </summary> var ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(credential.Password);
/// <param name="ignoreSsl">Whether to ignore SSL certificate errors.</param> try
/// <param name="handler">Optional custom message handler for testing.</param> {
/// <returns>A configured HttpClient instance.</returns> return System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr);
}
finally
{
System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr);
}
}
public static HttpClient CreateHttpClient(bool ignoreSsl = false, HttpMessageHandler? handler = null) public static HttpClient CreateHttpClient(bool ignoreSsl = false, HttpMessageHandler? handler = null)
{ {
if (handler != null) if (handler != null)
@@ -139,27 +122,10 @@ public static class SessionManager
{ {
var sslHandler = new HttpClientHandler var sslHandler = new HttpClientHandler
{ {
ServerCertificateCustomValidationCallback = (_, _, _, _) => true ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
}; };
return new HttpClient(sslHandler); return new HttpClient(sslHandler);
} }
return new HttpClient(); return new HttpClient();
} }
/// <summary>
/// Extracts the plain text password from a PSCredential object.
/// </summary>
private static string GetPasswordString(PSCredential credential)
{
var ptr = Marshal.SecureStringToBSTR(credential.Password);
try
{
return Marshal.PtrToStringBSTR(ptr);
}
finally
{
Marshal.ZeroFreeBSTR(ptr);
}
}
} }

View File

@@ -1,23 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// This file provides the IsExternalInit type required for using C# 9 records
// in projects targeting .NET Standard 2.1 or earlier.
#if !NET5_0_OR_GREATER
namespace System.Runtime.CompilerServices
{
using System.ComponentModel;
/// <summary>
/// Reserved to be used by the compiler for tracking metadata.
/// This class should not be used by developers in source code.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
internal static class IsExternalInit
{
}
}
#endif

View File

@@ -1,35 +1,77 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
using System.Collections.Generic;
/// <summary>
/// Represents an IP address entry in phpIPAM.
/// </summary>
[Serializable] [Serializable]
public sealed record Address( public class Address {
int Id, public int Id { get; }
int SubnetId, public int SubnetId { get; }
string Ip, public string Ip { get; }
bool IsGateway, public bool IsGateway { get; }
string Description, public string Description { get; }
string Hostname, public string Hostname { get; }
string MAC, public string MAC { get; }
string Owner, public string Owner { get; }
int TagId, public int TagId { get; }
int DeviceId, public int DeviceId { get; }
string Location, public string Location { get; }
string Port, public string Port { get; }
string Note, public string Note { get; }
DateTime? LastSeen, public DateTime? LastSeen { get; }
bool ExcludePing, public bool ExcludePing { get; }
bool PTRIgnore, public bool PTRignore { get; }
public int PTR { get; }
public string FirewallAddressObject { get; }
public DateTime? EditDate { get; }
public int CustomerId { get; }
public Object? ExtendedData { get; }
public Address(
int id,
int subnetId,
string ip,
bool is_gateway,
string description,
string hostname,
string mac,
string owner,
int tag,
int deviceId,
string location,
string port,
string note,
DateTime? lastSeen,
bool excludePing,
bool PTRignore,
int PTR, int PTR,
string FirewallAddressObject, string firewallAddressObject,
DateTime? EditDate, DateTime? editDate,
int CustomerId, int customer_id,
Dictionary<string, object>? ExtendedData = null Object? extendedData
) ) {
{ this.Id = id;
public override string ToString() => Ip; this.SubnetId = subnetId;
this.Ip = ip;
this.IsGateway = is_gateway;
this.Description = description;
this.Hostname = hostname;
this.MAC = mac;
this.Owner = owner;
this.TagId = tag;
this.DeviceId = deviceId;
this.Location = location;
this.Port = port;
this.Note = note;
this.LastSeen = lastSeen;
this.ExcludePing = excludePing;
this.PTRignore = PTRignore;
this.PTR = PTR;
this.FirewallAddressObject = firewallAddressObject;
this.EditDate = editDate;
this.CustomerId = customer_id;
this.ExtendedData = extendedData;
}
public override string ToString() {
return this.Ip;
}
} }

View File

@@ -1,17 +1,27 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
/// <summary>
/// Represents an L2 domain in phpIPAM.
/// </summary>
[Serializable] [Serializable]
public sealed record Domain( public class Domain {
int Id, public int Id { get; }
string Name, public string Name { get; }
string Description, public string Description { get; }
string Sections public string Sections { get; }
)
{ public Domain (
public override string ToString() => Name; int id,
string name,
string description,
string sections
) {
this.Id = id;
this.Name = name;
this.Description = description;
this.Sections = sections;
}
public override string ToString()
{
return this.Name;
}
} }

View File

@@ -1,13 +1,8 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
/// <summary>
/// Represents a nameserver configuration in phpIPAM.
/// </summary>
[Serializable] [Serializable]
public sealed record Nameserver public class Nameserver {
{
public int Id { get; } public int Id { get; }
public string Name { get; } public string Name { get; }
public string[] NameServers { get; } public string[] NameServers { get; }
@@ -21,15 +16,13 @@ public sealed record Nameserver
string nameServers, string nameServers,
string description, string description,
string permissions, string permissions,
DateTime? editDate) DateTime? editDate
{ ) {
Id = id; this.Id = id;
Name = name; this.Name = name;
NameServers = nameServers?.Split(';', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>(); this.NameServers = nameServers.Split(new char[] {';'});
Description = description; this.Description = description;
Permissions = permissions; this.Permissions = permissions;
EditDate = editDate; this.EditDate = editDate;
} }
public override string ToString() => Name;
} }

View File

@@ -1,27 +1,57 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
/// <summary>
/// Represents a section in phpIPAM that organizes subnets.
/// </summary>
[Serializable] [Serializable]
public sealed record Section( public class Section {
int Id, public int Id { get; }
string Name, public string Name { get; }
string Description, public string Description { get; }
int MasterSectionId, public int MasterSectionId { get; }
string Permissions, public string Permissions { get; }
bool StrictMode, public bool StrictMode { get; }
string SubnetOrdering, public string SubnetOrdering { get; }
int Order, public int Order { get; }
DateTime? EditDate, public DateTime? EditDate { get; }
bool ShowSubnet, public bool ShowSubnet { get; }
bool ShowVlan, public bool ShowVlan { get; }
bool ShowVRF, public bool ShowVRF { get; }
bool ShowSupernetOnly, public bool ShowSupernetOnly { get; }
int DNSId public int DNSId { get; }
)
{ public Section (
public override string ToString() => Name; int id,
string name,
string description,
int masterSectionId,
string permissions,
bool strictMode,
string subnetOrdering,
int order,
DateTime? editDate,
bool showSubnet,
bool showVlan,
bool showVRF,
bool showSupernetOnly,
int dnsId
) {
this.Id = id;
this.Name = name;
this.Description = description;
this.MasterSectionId = masterSectionId;
this.Permissions = permissions;
this.StrictMode = strictMode;
this.SubnetOrdering = subnetOrdering;
this.Order = order;
this.EditDate = editDate;
this.ShowSubnet = showSubnet;
this.ShowVlan = showVlan;
this.ShowVRF = showVRF;
this.ShowSupernetOnly = showSupernetOnly;
this.DNSId = dnsId;
}
public override string ToString()
{
return this.Name;
}
} }

View File

@@ -1,27 +1,33 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
using System.Management.Automation;
/// <summary>
/// Represents an authenticated session with phpIPAM API.
/// </summary>
[Serializable] [Serializable]
public sealed record Session( public class Session {
AuthType AuthType, public AuthType AuthType { get; }
string Token, public string Token { get; set; }
string AppID, public string AppID { get; }
string URL, public string URL { get; }
DateTime? Expires, public DateTime? Expires { get; set; }
object? Credentials public object? Credentials { get; }
) public Session(
{ AuthType authType,
/// <summary> string token,
/// Gets or sets the current authentication token. string appId,
/// </summary> string url,
public string Token { get; set; } = Token; DateTime? expires,
object? credentials
/// <summary> ) {
/// Gets or sets the token expiration time. AuthType = authType;
/// </summary> Token = token;
public DateTime? Expires { get; set; } = Expires; AppID = appId;
URL = url;
Expires = expires;
Credentials = credentials;
}
public override string ToString()
{
return base.ToString();
}
} }

View File

@@ -1,51 +1,114 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
using System.Collections.Generic;
/// <summary>
/// Represents a subnet/network in phpIPAM.
/// </summary>
[Serializable] [Serializable]
public sealed record Subnetwork( public class Subnetwork {
int Id, public int Id { get; }
string Subnet, public string Subnet { get; }
int Mask, public int Mask { get; }
int SectionId, public int SectionId { get; }
string Description, public string Description { get; }
string LinkedSubnet, public string LinkedSubnet { get; }
string FirewallAddressObject, public string FirewallAddressObject { get; }
int VrfId, public int VrfId { get; }
int MasterSubnetId, public int MasterSubnetId { get; }
bool AllowRequests, public bool AllowRequests { get; }
int VlanId, public int VlanId { get; }
bool ShowName, public bool ShowName { get; }
int DeviceId, public int DeviceId { get; }
string Permissions, public string Permissions { get; }
bool PingSubnet, public bool PingSubnet { get; }
bool DiscoverSubnet, public bool DiscoverSubnet { get; }
bool ResolveDNS, public bool ResolveDNS { get; }
bool DNSRecursive, public bool DNSRecursive { get; }
bool DNSRecords, public bool DNSRecords { get; }
int NameserverId, public int NameserverId { get; }
bool ScanAgent, public bool ScanAgent { get; }
bool IsFolder, public bool IsFolder { get; }
bool IsFull, public bool IsFull { get; }
bool IsPool, public bool IsPool { get; }
int TagId, public int TagId { get; }
int Threshold, public int Threshold { get; }
int LocationId, public int LocationId { get; }
DateTime? EditDate, public DateTime? EditDate { get; }
DateTime? LastScan, public DateTime? LastScan { get; }
DateTime? LastDiscovery, public DateTime? LastDiscovery { get; }
object Calculation, public Object Calculation { get; }
Dictionary<string, object>? ExtendedData = null public Object? ExtendedData { get; }
) public Subnetwork(
{ int id,
/// <summary> string subnet,
/// Gets the subnet in CIDR notation (e.g., "192.168.1.0/24"). int mask,
/// </summary> int sectionId,
public string CIDR => $"{Subnet}/{Mask}"; string description,
string linkedSubnet,
public override string ToString() => CIDR; string firewallAddressObject,
int vrfId,
int masterSubnetId,
bool allowRequests,
int vlanId,
bool showName,
int deviceId,
string permissions,
bool pingSubnet,
bool discoverSubnet,
bool resolveDNS,
bool dnsRecursive,
bool dnsRecords,
int nameserverId,
bool scanAgent,
bool isFolder,
bool isFull,
bool isPool,
int tagId,
int threshold,
int locationId,
DateTime? editDate,
DateTime? lastScan,
DateTime? lastDiscovery,
Object calculation,
Object? custom_fields
) {
this.Id = id;
this.Subnet = subnet;
this.Mask = mask;
this.SectionId = sectionId;
this.Description = description;
this.LinkedSubnet = linkedSubnet;
this.FirewallAddressObject = firewallAddressObject;
this.VrfId = vrfId;
this.MasterSubnetId = masterSubnetId;
this.AllowRequests = allowRequests;
this.VlanId = vlanId;
this.ShowName = showName;
this.DeviceId = deviceId;
this.Permissions = permissions;
this.PingSubnet = pingSubnet;
this.DiscoverSubnet = discoverSubnet;
this.ResolveDNS = resolveDNS;
this.DNSRecursive = dnsRecursive;
this.DNSRecords = dnsRecords;
this.NameserverId = nameserverId;
this.ScanAgent = scanAgent;
this.IsFolder = isFolder;
this.IsFull = isFull;
this.IsPool = isPool;
this.TagId = tagId;
this.Threshold = threshold;
this.LocationId = locationId;
this.EditDate = editDate;
this.LastScan = lastScan;
this.LastDiscovery = lastDiscovery;
this.Calculation = calculation;
this.ExtendedData = custom_fields;
}
public string GetCIDR() {
return $"{this.Subnet}/{this.Mask}";
}
public override string ToString()
{
return this.GetCIDR();
}
} }

View File

@@ -1,21 +1,45 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
/// <summary>
/// Represents an address tag in phpIPAM.
/// </summary>
[Serializable] [Serializable]
public sealed record Tag( public class Tag {
int Id, public int Id { get; }
string Type, public string Type { get; }
bool ShowTag, public bool ShowTag { get; }
string BackgroundColor, public string BGColor { get; }
string ForegroundColor, public string FGColor { get; }
string Compress, public bool Compress { get; }
string Locked, public bool Locked { get; }
bool UpdateTag public bool UpdateTag { get; }
)
{ public Tag(
public override string ToString() => Type; int id,
string type,
bool showTag,
string BGColor,
string FGColor,
string compress,
string locked,
bool updateTag
) {
this.Id = id;
this.Type = type;
this.ShowTag = showTag;
this.BGColor = BGColor;
this.FGColor = FGColor;
this.Compress = this.StringToBool(compress);
this.Locked = this.StringToBool(locked);
this.UpdateTag = updateTag;
}
public override string ToString() {
return this.Type;
}
private bool StringToBool(string str) {
if (str == "Yes") {
return true;
}
return false;
}
} }

View File

@@ -1,27 +1,41 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
using System.Collections.Generic; using System.Dynamic;
/// <summary>
/// Represents a VLAN in phpIPAM.
/// </summary>
[Serializable] [Serializable]
public sealed record Vlan( public class Vlan : DynamicObject {
int Id, public int Id { get; }
int DomainId, public int VlanId { get; }
string Name, public int DomainId { get; }
int Number, public string Name { get; }
string Description, public int Number { get; }
DateTime? EditDate, public string Description { get; }
int CustomerId, public DateTime? EditDate { get; }
Dictionary<string, object>? ExtendedData = null public int CustomerId { get; }
) public Object? ExtendedData { get; }
{ public Vlan (
/// <summary> int vlanId,
/// Alias for Id to maintain API compatibility. int domainId,
/// </summary> string name,
public int VlanId => Id; int number,
string description,
public override string ToString() => Number.ToString(); DateTime? editDate,
int customer_id,
Object? custom_fields
) {
this.Id = vlanId;
this.VlanId = vlanId;
this.DomainId = domainId;
this.Name = name;
this.Number = number;
this.Description = description;
this.EditDate = editDate;
this.CustomerId = customer_id;
this.ExtendedData = custom_fields;
}
public override string ToString()
{
return $"{this.Number}";
}
} }

View File

@@ -1,21 +1,35 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
using System.Collections.Generic;
/// <summary>
/// Represents a VRF (Virtual Routing and Forwarding) instance in phpIPAM.
/// </summary>
[Serializable] [Serializable]
public sealed record Vrf( public class Vrf {
int Id, public int Id { get; }
string Name, public string Name { get; }
string RouteDistinguisher, public string RouteDistinguisher { get; }
string Description, public string Description { get; }
string Sections, public string Sections { get; }
DateTime? EditDate, public DateTime? EditDate { get; }
Dictionary<string, object>? ExtendedData = null public Object? ExtendedData { get; }
)
{ public Vrf(
public override string ToString() => Name; int id,
string name,
string rd,
string description,
string sections,
DateTime? editDate,
Object? custom_fields
) {
this.Id = id;
this.Name = name;
this.RouteDistinguisher = rd;
this.Description = description;
this.Sections = sections;
this.EditDate = editDate;
this.ExtendedData = custom_fields;
}
public override string ToString()
{
return this.Name;
}
} }

View File

@@ -1,22 +0,0 @@
namespace PS.IPAM;
/// <summary>
/// Represents the current status of an API session.
/// </summary>
public enum SessionStatus
{
/// <summary>
/// No session exists or no token is available.
/// </summary>
NoSession,
/// <summary>
/// The session token has expired.
/// </summary>
Expired,
/// <summary>
/// The session is valid and ready for use.
/// </summary>
Valid
}

View File

@@ -1,20 +1,8 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
/// <summary>
/// Specifies the authentication method for phpIPAM API.
/// </summary>
[Serializable] [Serializable]
public enum AuthType public enum AuthType {
{ credentials,
/// <summary> token
/// Authentication using username and password credentials.
/// </summary>
Credentials,
/// <summary>
/// Authentication using a static API token.
/// </summary>
Token
} }

View File

@@ -1,19 +1,14 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
/// <summary>
/// Represents the main API controllers in phpIPAM.
/// </summary>
[Serializable] [Serializable]
public enum ApiController public enum controllers {
{ user,
User, vlan,
Vlan, subnets,
Subnets, addresses,
Addresses, sections,
Sections, vrf,
Vrf, l2domains,
L2Domains, tools
Tools
} }

View File

@@ -1,21 +1,16 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
/// <summary>
/// Represents sub-controllers/endpoints within main API controllers.
/// </summary>
[Serializable] [Serializable]
public enum ApiSubController public enum subcontrollers {
{ nameservers,
Nameservers, tags,
Tags, devices,
Devices, device_types,
DeviceTypes, vlans,
Vlans, vrfs,
Vrfs, scanagents,
ScanAgents, locations,
Locations, nat,
Nat, racks
Racks
} }

View File

@@ -1,13 +1,8 @@
namespace PS.IPAM; namespace PS.IPAM;
using System; using System;
/// <summary>
/// Represents the model types returned by the phpIPAM API.
/// </summary>
[Serializable] [Serializable]
public enum ModelType public enum types {
{
Address, Address,
Domain, Domain,
Section, Section,