Skip to content

OSClient API Reference

This is the API reference for OSClient. If you're looking for how-to guidance, check the integration guide first. This covers the complete OpenStackClient library API, service clients, and data models.

Package Overview

The OSClient library provides a comprehensive Swift API for interacting with OpenStack services with:

  • Type-safe API using Swift's strong type system
  • Actor-based concurrency for thread safety
  • Intelligent caching designed for up to 60-80% API call reduction
  • Comprehensive error handling with configurable retry policies
  • Cross-platform compatibility (macOS and Linux)
  • SSL verification control for development environments

Architecture Overview

graph TB
    subgraph "OSClient Library"
        Client[OpenStackClient]

        subgraph "Service Clients"
            Nova[NovaService<br/>Compute]
            Neutron[NeutronService<br/>Networking]
            Cinder[CinderService<br/>Block Storage]
            Glance[GlanceService<br/>Images]
            Keystone[KeystoneService<br/>Identity]
            Barbican[BarbicanService<br/>Key Manager]
            Swift[SwiftService<br/>Object Storage]
            Magnum[MagnumService<br/>Containers]
        end

        subgraph "Support Layer"
            Auth[Authentication]
            Cache[Data Managers]
            Retry[Retry Policy]
            Log[Logger]
        end
    end

    subgraph "OpenStack APIs"
        API[REST APIs]
    end

    Client --> Nova
    Client --> Neutron
    Client --> Cinder
    Client --> Glance
    Client --> Keystone
    Client --> Barbican
    Client --> Swift
    Client --> Magnum

    Nova --> Auth
    Neutron --> Auth
    Cinder --> Auth
    Glance --> Auth

    Auth --> API
    Cache --> API

    style Client fill:#4a9eff,stroke:#333,color:#fff
    style Nova fill:#ff6b6b,stroke:#333,color:#fff
    style Neutron fill:#51cf66,stroke:#333,color:#fff
    style Cinder fill:#ffd43b,stroke:#333,color:#000
    style Glance fill:#be4bdb,stroke:#333,color:#fff

Class Hierarchy

classDiagram
    class OpenStackClient {
        +nova: NovaService
        +neutron: NeutronService
        +cinder: CinderService
        +glance: GlanceService
        +keystone: KeystoneService
        +barbican: BarbicanService
        +swift: SwiftService
        +magnum: MagnumService
        +isAuthenticated: Bool
        +connect()
        +disconnect()
    }

    class OpenStackService {
        <<protocol>>
        +endpoint: URL
        +authToken: String
    }

    class NovaService {
        +listServers()
        +getServer(id)
        +createServer(request)
        +deleteServer(id)
    }

    class NeutronService {
        +listNetworks()
        +listSubnets()
        +listRouters()
        +listFloatingIPs()
    }

    class DataManager {
        <<protocol>>
        +loadData()
        +refreshData()
        +invalidateCache()
    }

    OpenStackClient --> NovaService
    OpenStackClient --> NeutronService
    NovaService ..|> OpenStackService
    NeutronService ..|> OpenStackService

OpenStackClient

The main entry point for all OpenStack operations.

Initialization

@MainActor
public final class OpenStackClient: @unchecked Sendable {
    /// Connect to OpenStack with configuration and credentials
    public static func connect(
        config: OpenStackConfig,
        credentials: OpenStackCredentials,
        logger: any OpenStackClientLogger = ConsoleLogger(),
        enablePerformanceEnhancements: Bool = true
    ) async throws -> OpenStackClient

    /// Convenience initializer for logger-only initialization
    public convenience init(logger: any OpenStackClientLogger)
}

Example:

import OSClient

let config = OpenStackConfig(
    authURL: URL(string: "https://keystone.example.com:5000/v3")!,
    region: "RegionOne",
    verifySSL: true  // Set to false for self-signed certificates
)

let credentials = OpenStackCredentials.password(
    username: "operator",
    password: "secret",
    projectName: "myproject",
    userDomainName: "default",
    projectDomainName: "default"
)

let client = try await OpenStackClient.connect(
    config: config,
    credentials: credentials
)

Connection Management

Authentication Flow

sequenceDiagram
    participant App as Application
    participant Client as OpenStackClient
    participant Keystone as Keystone API
    participant Service as Service API

    App->>Client: connect(config, credentials)
    Client->>Keystone: POST /v3/auth/tokens
    Keystone-->>Client: Token + Service Catalog

    Note over Client: Store token and endpoints

    App->>Client: listServers()
    Client->>Client: Check token validity

    alt Token Valid
        Client->>Service: GET /servers (with token)
        Service-->>Client: Server list
    else Token Expired
        Client->>Keystone: POST /v3/auth/tokens
        Keystone-->>Client: New token
        Client->>Service: GET /servers (with new token)
        Service-->>Client: Server list
    end

    Client-->>App: [Server]
extension OpenStackClient {
    /// Manually trigger connection/authentication
    public func connect() async

    /// Disconnect and clear authentication state
    public func disconnect()

    /// Clear authentication cache (forces re-authentication)
    public func clearAuthCache() async
}

UI State Properties

extension OpenStackClient {
    /// Whether the client is currently authenticated
    public private(set) var isAuthenticated: Bool

    /// Any authentication error that occurred
    public private(set) var authenticationError: (any Error)?

    /// Whether a connection attempt is in progress
    public private(set) var isConnecting: Bool

    /// Time until the current token expires
    public private(set) var timeUntilTokenExpiration: TimeInterval?

    /// Add an observer for state changes
    public func addObserver(_ observer: @escaping () -> Void)
}

Service Access

Service properties are async to support lazy initialization:

extension OpenStackClient {
    /// Access to Nova (Compute) service
    public var nova: NovaService { get async }

    /// Access to Neutron (Network) service
    public var neutron: NeutronService { get async }

    /// Access to Cinder (Block Storage) service
    public var cinder: CinderService { get async }

    /// Access to Glance (Image) service
    public var glance: GlanceService { get async }

    /// Access to Keystone (Identity) service
    public var keystone: KeystoneService { get async }

    /// Access to Barbican (Key Management) service
    public var barbican: BarbicanService { get async }

    /// Access to Swift (Object Storage) service
    public var swift: SwiftService { get async }

    /// Access to Magnum (Container Infrastructure) service
    public var magnum: MagnumService { get async }
}

Example:

// Access a service (async)
let nova = await client.nova
let servers = try await nova.listServers()

// Or use convenience methods directly on client
let servers = try await client.listServers()

Data Manager Access

Data managers provide caching and incremental loading for frequently accessed resources:

Data Manager Caching Flow

flowchart TD
    Request[Data Request] --> CacheCheck{Cache Valid?}

    CacheCheck --> |Yes| CacheHit[Return Cached Data]
    CacheCheck --> |No| FetchAPI[Fetch from API]

    FetchAPI --> Store[Store in Cache]
    Store --> Return[Return Fresh Data]

    subgraph "Cache Invalidation"
        Timer[TTL Timer] --> Invalidate[Invalidate Cache]
        Modify[Create/Update/Delete] --> Invalidate
        Force[forceRefresh=true] --> Invalidate
    end

    Invalidate --> CacheCheck

    style CacheHit fill:#51cf66,stroke:#333,color:#fff
    style FetchAPI fill:#4a9eff,stroke:#333,color:#fff
extension OpenStackClient {
    /// Access to the data manager factory
    public var dataManagerFactory: ServiceDataManagerFactory { get async }

    /// Access to server data manager with caching
    public var serverDataManager: ServerDataManager { get async }

    /// Access to network data manager with caching
    public var networkDataManager: NetworkDataManager { get async }

    /// Access to volume data manager with caching
    public var volumeDataManager: VolumeDataManager { get async }

    /// Access to image data manager with caching
    public var imageDataManager: ImageDataManager { get async }
}

Configuration Properties

extension OpenStackClient {
    /// The configured region for this client
    public var region: String { get async }

    /// The configured project domain name
    public var projectDomainName: String { get async }

    /// The project name (from credentials or token)
    public var projectName: String? { get async }

    /// The project ID (if available from token)
    public var projectID: String? { get async }

    /// The project identifier (name or ID)
    public var project: String { get async }
}

Configuration

/// OpenStack configuration containing connection settings
public struct OpenStackConfig: Sendable {
    /// The authentication URL for the OpenStack Identity (Keystone) service
    public let authURL: URL

    /// The region name ("auto-detect" for automatic detection)
    public let region: String

    /// The interface type ("public", "internal", "admin")
    public let interface: String

    /// The domain name for user authentication
    public let userDomainName: String

    /// The domain name for project scope
    public let projectDomainName: String

    /// The timeout interval for API requests in seconds
    public let timeout: TimeInterval

    /// The retry policy for failed API requests
    public let retryPolicy: RetryPolicy

    /// Whether to verify SSL/TLS certificates
    /// Set to false for self-signed certificates (development only)
    public let verifySSL: Bool

    public init(
        authURL: URL,
        region: String = "auto-detect",
        userDomainName: String = "default",
        projectDomainName: String = "default",
        timeout: TimeInterval = 30.0,
        retryPolicy: RetryPolicy = RetryPolicy(),
        verifySSL: Bool = true,
        interface: String = "public"
    )
}

Credentials

Authentication Methods

graph TB
    subgraph "Password Authentication"
        PW[Password Credentials]
        PW --> User[Username]
        PW --> Pass[Password]
        PW --> Project[Project Name/ID]
        PW --> Domain[Domain Name/ID]

        User --> KeystoneP[Keystone Auth]
        Pass --> KeystoneP
        Project --> KeystoneP
        Domain --> KeystoneP
    end

    subgraph "Application Credential Authentication"
        AC[App Credential]
        AC --> AppID[Credential ID]
        AC --> Secret[Secret]

        AppID --> KeystoneA[Keystone Auth]
        Secret --> KeystoneA
    end

    KeystoneP --> Token[Auth Token]
    KeystoneA --> Token

    style PW fill:#4a9eff,stroke:#333,color:#fff
    style AC fill:#51cf66,stroke:#333,color:#fff
    style Token fill:#ffd43b,stroke:#333,color:#000
/// OpenStack authentication credentials
public enum OpenStackCredentials: Sendable {
    /// Username/password authentication
    case password(
        username: String,
        password: String,
        projectName: String?,
        projectID: String? = nil,
        userDomainName: String? = nil,
        userDomainID: String? = nil,
        projectDomainName: String? = nil,
        projectDomainID: String? = nil
    )

    /// Application credential authentication
    case applicationCredential(
        id: String,
        secret: String,
        projectName: String?,
        projectID: String? = nil
    )
}

Example:

// Password authentication
let passwordCreds = OpenStackCredentials.password(
    username: "admin",
    password: "secret",
    projectName: "admin",
    userDomainName: "default",
    projectDomainName: "default"
)

// Application credential authentication
let appCreds = OpenStackCredentials.applicationCredential(
    id: "app-cred-id",
    secret: "app-cred-secret",
    projectName: nil  // Project is encoded in the app credential
)

Retry Policy

Retry Flow with Exponential Backoff

flowchart TD
    Request[API Request] --> Execute[Execute Request]
    Execute --> Response{Response Status}

    Response --> |2xx Success| Success[Return Result]
    Response --> |4xx Client Error| ClientError{Retryable?}
    Response --> |5xx Server Error| ServerError{Retryable?}
    Response --> |Network Error| NetworkError{Retryable?}

    ClientError --> |429 Rate Limit| Retry
    ClientError --> |Other 4xx| Fail[Return Error]

    ServerError --> |500, 502, 503, 504| Retry
    ServerError --> |Other 5xx| Fail

    NetworkError --> |Timeout| Retry
    NetworkError --> |Other| Fail

    Retry{Attempts < Max?} --> |Yes| Backoff[Wait: baseDelay * 2^attempt]
    Retry --> |No| Fail

    Backoff --> Execute

    style Success fill:#51cf66,stroke:#333,color:#fff
    style Fail fill:#ff6b6b,stroke:#333,color:#fff
    style Backoff fill:#ffd43b,stroke:#333,color:#000
/// Configuration for automatic request retries
public struct RetryPolicy: Sendable {
    /// Maximum number of retry attempts
    public let maxAttempts: Int

    /// Base delay between retries (exponential backoff)
    public let baseDelay: TimeInterval

    /// Maximum delay cap for retries
    public let maxDelay: TimeInterval

    /// HTTP status codes that trigger a retry
    public let retryStatusCodes: Set<Int>

    public init(
        maxAttempts: Int = 3,
        baseDelay: TimeInterval = 1.0,
        maxDelay: TimeInterval = 60.0,
        retryStatusCodes: Set<Int> = [429, 500, 502, 503, 504]
    )
}

Service Clients

Service Relationships

graph LR
    subgraph "Compute"
        Nova[NovaService]
        Servers[Servers]
        Flavors[Flavors]
        KeyPairs[KeyPairs]
        ServerGroups[ServerGroups]
        Hypervisors[Hypervisors]
    end

    subgraph "Networking"
        Neutron[NeutronService]
        Networks[Networks]
        Subnets[Subnets]
        Routers[Routers]
        Ports[Ports]
        FloatingIPs[FloatingIPs]
        SecurityGroups[SecurityGroups]
    end

    subgraph "Storage"
        Cinder[CinderService]
        Volumes[Volumes]
        Snapshots[Snapshots]
        Backups[Backups]

        SwiftSvc[SwiftService]
        Containers[Containers]
        Objects[Objects]
    end

    subgraph "Other Services"
        Glance[GlanceService]
        Images[Images]

        Keystone[KeystoneService]
        Projects[Projects]
        Users[Users]

        Barbican[BarbicanService]
        Secrets[Secrets]

        Magnum[MagnumService]
        Clusters[Clusters]
    end

    Nova --> Servers
    Nova --> Flavors
    Nova --> KeyPairs
    Nova --> ServerGroups
    Nova --> Hypervisors

    Neutron --> Networks
    Neutron --> Subnets
    Neutron --> Routers
    Neutron --> Ports
    Neutron --> FloatingIPs
    Neutron --> SecurityGroups

    Cinder --> Volumes
    Cinder --> Snapshots
    Cinder --> Backups

    SwiftSvc --> Containers
    SwiftSvc --> Objects

    Glance --> Images
    Keystone --> Projects
    Keystone --> Users
    Barbican --> Secrets
    Magnum --> Clusters

    style Nova fill:#ff6b6b,stroke:#333,color:#fff
    style Neutron fill:#51cf66,stroke:#333,color:#fff
    style Cinder fill:#ffd43b,stroke:#333,color:#000
    style Glance fill:#be4bdb,stroke:#333,color:#fff

NovaService (Compute)

Compute service for managing servers, flavors, keypairs, server groups, and hypervisors.

public actor NovaService: OpenStackService {
    // Server operations
    /// List servers with optional pagination
    public func listServers(
        options: PaginationOptions = PaginationOptions(),
        forceRefresh: Bool = false
    ) async throws -> ServerListResponse

    /// Get server details
    public func getServer(
        id: String,
        forceRefresh: Bool = false
    ) async throws -> Server

    /// Create a new server
    public func createServer(
        request: CreateServerRequest
    ) async throws -> Server

    /// Delete a server
    public func deleteServer(id: String) async throws

    /// Server actions
    public func startServer(_ id: String) async throws
    public func stopServer(_ id: String) async throws
    public func rebootServer(
        id: String,
        type: RebootType = .soft
    ) async throws
    public func resizeServer(
        id: String,
        flavorRef: String
    ) async throws
    public func confirmResize(id: String) async throws
    public func revertResize(id: String) async throws

    /// Console access
    public func getConsoleOutput(
        id: String,
        length: Int? = nil
    ) async throws -> String
    public func getRemoteConsole(
        id: String,
        protocol: String = "vnc",
        type: String = "novnc"
    ) async throws -> RemoteConsole

    /// Create server snapshot
    public func createServerSnapshot(
        serverId: String,
        name: String,
        metadata: [String: String]? = nil
    ) async throws -> String

    // Flavor operations
    /// List available flavors
    public func listFlavors(
        includePublic: Bool = true,
        options: PaginationOptions = PaginationOptions(),
        forceRefresh: Bool = false
    ) async throws -> [Flavor]

    /// Get flavor details
    public func getFlavor(
        id: String,
        forceRefresh: Bool = false
    ) async throws -> Flavor

    // Key pair operations
    /// List key pairs
    public func listKeyPairs(
        forceRefresh: Bool = false
    ) async throws -> [KeyPair]

    /// Create a key pair
    public func createKeyPair(
        name: String,
        publicKey: String? = nil
    ) async throws -> KeyPair

    /// Delete a key pair
    public func deleteKeyPair(name: String) async throws

    // Server group operations
    /// List server groups
    public func listServerGroups(
        forceRefresh: Bool = false
    ) async throws -> [ServerGroup]

    /// Create a server group
    public func createServerGroup(
        name: String,
        policies: [String]
    ) async throws -> ServerGroup

    /// Delete a server group
    public func deleteServerGroup(id: String) async throws

    // Hypervisor operations (admin only)
    /// List hypervisors
    public func listHypervisors(
        forceRefresh: Bool = false
    ) async throws -> [Hypervisor]

    /// Get hypervisor details
    public func getHypervisor(
        id: String,
        forceRefresh: Bool = false
    ) async throws -> Hypervisor

    /// List servers on a hypervisor
    public func listHypervisorServers(
        hypervisorId: String
    ) async throws -> [HypervisorServer]
}

NeutronService (Networking)

Networking service for managing networks, subnets, routers, ports, security groups, and floating IPs.

public actor NeutronService: OpenStackService {
    // Network operations
    /// List networks
    public func listNetworks(
        options: PaginationOptions = PaginationOptions(),
        forceRefresh: Bool = false
    ) async throws -> [Network]

    /// Get network details
    public func getNetwork(
        id: String,
        forceRefresh: Bool = false
    ) async throws -> Network

    /// Create network
    public func createNetwork(
        request: CreateNetworkRequest
    ) async throws -> Network

    /// Update network
    public func updateNetwork(
        id: String,
        request: UpdateNetworkRequest
    ) async throws -> Network

    /// Delete network
    public func deleteNetwork(id: String) async throws

    // Subnet operations
    /// List subnets
    public func listSubnets(
        networkId: String? = nil,
        options: PaginationOptions = PaginationOptions(),
        forceRefresh: Bool = false
    ) async throws -> [Subnet]

    /// Get subnet details
    public func getSubnet(
        id: String,
        forceRefresh: Bool = false
    ) async throws -> Subnet

    /// Create subnet
    public func createSubnet(
        request: CreateSubnetRequest
    ) async throws -> Subnet

    /// Update subnet
    public func updateSubnet(
        id: String,
        request: UpdateSubnetRequest
    ) async throws -> Subnet

    /// Delete subnet
    public func deleteSubnet(id: String) async throws

    // Port operations
    /// List ports
    public func listPorts(
        networkId: String? = nil,
        deviceId: String? = nil,
        options: PaginationOptions = PaginationOptions(),
        forceRefresh: Bool = false
    ) async throws -> [Port]

    /// Get port details
    public func getPort(
        id: String,
        forceRefresh: Bool = false
    ) async throws -> Port

    /// Create port
    public func createPort(
        request: CreatePortRequest
    ) async throws -> Port

    /// Update port
    public func updatePort(
        id: String,
        request: UpdatePortRequest
    ) async throws -> Port

    /// Delete port
    public func deletePort(id: String) async throws

    // Router operations
    /// List routers
    public func listRouters(
        options: PaginationOptions = PaginationOptions(),
        forceRefresh: Bool = false
    ) async throws -> [Router]

    /// Get router details
    public func getRouter(
        id: String,
        forceRefresh: Bool = false
    ) async throws -> Router

    /// Create router
    public func createRouter(
        request: CreateRouterRequest
    ) async throws -> Router

    /// Update router
    public func updateRouter(
        id: String,
        request: UpdateRouterRequest
    ) async throws -> Router

    /// Delete router
    public func deleteRouter(id: String) async throws

    /// Add interface to router
    public func addRouterInterface(
        routerId: String,
        subnetId: String? = nil,
        portId: String? = nil
    ) async throws -> RouterInterface

    /// Remove interface from router
    public func removeRouterInterface(
        routerId: String,
        subnetId: String? = nil,
        portId: String? = nil
    ) async throws

    // Security group operations
    /// List security groups
    public func listSecurityGroups(
        options: PaginationOptions = PaginationOptions(),
        forceRefresh: Bool = false
    ) async throws -> [SecurityGroup]

    /// Get security group details
    public func getSecurityGroup(id: String) async throws -> SecurityGroup

    /// Create security group
    public func createSecurityGroup(
        request: CreateSecurityGroupRequest
    ) async throws -> SecurityGroup

    /// Delete security group
    public func deleteSecurityGroup(id: String) async throws

    /// Create security group rule
    public func createSecurityGroupRule(
        request: CreateSecurityGroupRuleRequest
    ) async throws -> SecurityGroupRule

    /// Delete security group rule
    public func deleteSecurityGroupRule(id: String) async throws

    // Floating IP operations
    /// List floating IPs
    public func listFloatingIPs(
        options: PaginationOptions = PaginationOptions(),
        forceRefresh: Bool = false
    ) async throws -> [FloatingIP]

    /// Get floating IP details
    public func getFloatingIP(id: String) async throws -> FloatingIP

    /// Create floating IP
    public func createFloatingIP(
        networkID: String,
        portID: String? = nil,
        subnetID: String? = nil,
        description: String? = nil
    ) async throws -> FloatingIP

    /// Update floating IP (associate/disassociate)
    public func updateFloatingIP(
        id: String,
        portID: String? = nil,
        fixedIP: String? = nil
    ) async throws -> FloatingIP

    /// Delete floating IP
    public func deleteFloatingIP(id: String) async throws
}

CinderService (Block Storage)

Block storage service for managing volumes, snapshots, and backups.

public actor CinderService: OpenStackService {
    // Volume operations
    /// List volumes
    public func listVolumes(
        options: PaginationOptions = PaginationOptions()
    ) async throws -> [Volume]

    /// Get volume details
    public func getVolume(id: String) async throws -> Volume

    /// Create volume
    public func createVolume(
        request: CreateVolumeRequest
    ) async throws -> Volume

    /// Update volume
    public func updateVolume(
        id: String,
        request: UpdateVolumeRequest
    ) async throws -> Volume

    /// Delete volume
    public func deleteVolume(id: String) async throws

    /// Extend volume size
    public func extendVolume(
        id: String,
        newSize: Int
    ) async throws

    /// Attach volume to server
    public func attachVolume(
        id: String,
        serverId: String,
        device: String? = nil
    ) async throws

    /// Detach volume from server
    public func detachVolume(id: String) async throws

    // Volume type operations
    /// List volume types
    public func listVolumeTypes() async throws -> [VolumeType]

    /// Get volume type details
    public func getVolumeType(id: String) async throws -> VolumeType

    // Snapshot operations
    /// List volume snapshots
    public func listSnapshots(
        volumeId: String? = nil,
        options: PaginationOptions = PaginationOptions()
    ) async throws -> [VolumeSnapshot]

    /// Get snapshot details
    public func getSnapshot(id: String) async throws -> VolumeSnapshot

    /// Create snapshot
    public func createSnapshot(
        request: CreateSnapshotRequest
    ) async throws -> VolumeSnapshot

    /// Delete snapshot
    public func deleteSnapshot(id: String) async throws

    // Backup operations
    /// List volume backups
    public func listBackups(
        volumeId: String? = nil,
        options: PaginationOptions = PaginationOptions()
    ) async throws -> [VolumeBackup]

    /// Get backup details
    public func getBackup(id: String) async throws -> VolumeBackup

    /// Create backup
    public func createBackup(
        request: CreateBackupRequest
    ) async throws -> VolumeBackup

    /// Delete backup
    public func deleteBackup(id: String) async throws

    /// Restore backup
    public func restoreBackup(
        id: String,
        volumeId: String? = nil
    ) async throws -> VolumeBackupRestore
}

GlanceService (Image)

Image service for managing virtual machine images.

public actor GlanceService: OpenStackService {
    // Image operations
    /// List images
    public func listImages(
        options: PaginationOptions = PaginationOptions()
    ) async throws -> [Image]

    /// Get image details
    public func getImage(id: String) async throws -> Image

    /// Create image (metadata only)
    public func createImage(
        request: CreateImageRequest
    ) async throws -> Image

    /// Update image metadata
    public func updateImage(
        id: String,
        request: UpdateImageRequest
    ) async throws -> Image

    /// Delete image
    public func deleteImage(id: String) async throws

    /// Upload image data
    public func uploadImageData(
        id: String,
        data: Data
    ) async throws

    /// Download image data
    public func downloadImageData(id: String) async throws -> Data

    /// Add tag to image
    public func addImageTag(
        id: String,
        tag: String
    ) async throws

    /// Remove tag from image
    public func removeImageTag(
        id: String,
        tag: String
    ) async throws
}

KeystoneService (Identity)

Identity service for managing projects, users, roles, and domains.

public actor KeystoneService: OpenStackService {
    // Project operations
    /// List projects
    public func listProjects(
        options: PaginationOptions = PaginationOptions()
    ) async throws -> [Project]

    /// Get project details
    public func getProject(id: String) async throws -> Project

    /// Create project
    public func createProject(
        request: CreateProjectRequest
    ) async throws -> Project

    /// Update project
    public func updateProject(
        id: String,
        request: UpdateProjectRequest
    ) async throws -> Project

    /// Delete project
    public func deleteProject(id: String) async throws

    // User operations
    /// List users
    public func listUsers(
        domainId: String? = nil,
        options: PaginationOptions = PaginationOptions()
    ) async throws -> [User]

    /// Get user details
    public func getUser(id: String) async throws -> User

    /// Create user
    public func createUser(
        request: CreateUserRequest
    ) async throws -> User

    /// Update user
    public func updateUser(
        id: String,
        request: UpdateUserRequest
    ) async throws -> User

    /// Delete user
    public func deleteUser(id: String) async throws

    // Role operations
    /// List roles
    public func listRoles(
        options: PaginationOptions = PaginationOptions()
    ) async throws -> [Role]

    /// Grant role to user on project
    public func grantRoleToUserOnProject(
        userId: String,
        projectId: String,
        roleId: String
    ) async throws

    /// Revoke role from user on project
    public func revokeRoleFromUserOnProject(
        userId: String,
        projectId: String,
        roleId: String
    ) async throws

    // Domain operations
    /// List domains
    public func listDomains(
        options: PaginationOptions = PaginationOptions()
    ) async throws -> [Domain]

    /// Get domain details
    public func getDomain(id: String) async throws -> Domain
}

BarbicanService (Key Management)

Key management service for secrets, certificates, and encryption keys.

public actor BarbicanService: OpenStackService {
    // Secret operations
    /// List secrets
    public func listSecrets(
        options: PaginationOptions = PaginationOptions()
    ) async throws -> [Secret]

    /// Get secret details
    public func getSecret(id: String) async throws -> SecretDetailResponse

    /// Create secret
    public func createSecret(
        request: CreateSecretRequest
    ) async throws -> SecretRef

    /// Delete secret
    public func deleteSecret(id: String) async throws

    /// Get secret payload
    public func getSecretPayload(
        id: String,
        payloadContentType: String? = nil
    ) async throws -> Data

    /// Store secret payload
    public func storeSecretPayload(
        id: String,
        payload: Data,
        contentType: String
    ) async throws

    // Container operations
    /// List containers
    public func listContainers(
        options: PaginationOptions = PaginationOptions()
    ) async throws -> [BarbicanContainer]

    /// Get container details
    public func getContainer(id: String) async throws -> BarbicanContainer

    /// Create container
    public func createContainer(
        request: BarbicanCreateContainerRequest
    ) async throws -> ContainerRef

    /// Delete container
    public func deleteContainer(id: String) async throws
}

SwiftService (Object Storage)

Object storage service for managing containers and objects.

public actor SwiftService: OpenStackService {
    // Container operations
    /// List containers
    public func listContainers(
        limit: Int? = nil,
        marker: String? = nil,
        prefix: String? = nil
    ) async throws -> [SwiftContainer]

    /// Get container metadata
    public func getContainerMetadata(
        containerName: String
    ) async throws -> SwiftContainerMetadataResponse

    /// Create container
    public func createContainer(
        request: CreateSwiftContainerRequest
    ) async throws

    /// Update container metadata
    public func updateContainerMetadata(
        containerName: String,
        request: UpdateSwiftContainerMetadataRequest
    ) async throws

    /// Delete container
    public func deleteContainer(
        containerName: String
    ) async throws

    // Object operations
    /// List objects in container
    public func listObjects(
        containerName: String,
        limit: Int? = nil,
        marker: String? = nil,
        prefix: String? = nil,
        delimiter: String? = nil
    ) async throws -> [SwiftObject]

    /// Get object metadata
    public func getObjectMetadata(
        containerName: String,
        objectName: String
    ) async throws -> SwiftObjectMetadataResponse

    /// Upload object
    public func uploadObject(
        request: UploadSwiftObjectRequest
    ) async throws

    /// Download object
    public func downloadObject(
        containerName: String,
        objectName: String
    ) async throws -> Data

    /// Copy object
    public func copyObject(
        request: CopySwiftObjectRequest
    ) async throws

    /// Delete object
    public func deleteObject(
        containerName: String,
        objectName: String
    ) async throws

    // Bulk operations
    /// Bulk delete objects
    public func bulkDelete(
        request: BulkDeleteRequest
    ) async throws -> BulkDeleteResponse

    // Account operations
    /// Get account information
    public func getAccountInfo() async throws -> SwiftAccountInfo
}

MagnumService (Container Infrastructure)

Container infrastructure service for managing Kubernetes and other COE clusters.

public actor MagnumService: OpenStackService {
    // Cluster operations
    /// List all clusters
    public func listClusters() async throws -> [Cluster]

    /// Get cluster details
    public func getCluster(id: String) async throws -> Cluster

    /// Create a new cluster
    public func createCluster(
        request: ClusterCreateRequest
    ) async throws -> Cluster

    /// Delete a cluster
    public func deleteCluster(id: String) async throws

    /// Resize a cluster (change node count)
    public func resizeCluster(
        id: String,
        nodeCount: Int
    ) async throws

    /// Get kubeconfig for a cluster
    public func getKubeconfig(id: String) async throws -> String

    // Cluster template operations
    /// List all cluster templates
    public func listClusterTemplates() async throws -> [ClusterTemplate]

    /// Get cluster template details
    public func getClusterTemplate(id: String) async throws -> ClusterTemplate

    /// Create a cluster template
    public func createClusterTemplate(
        request: ClusterTemplateCreateRequest
    ) async throws -> ClusterTemplate

    /// Delete a cluster template
    public func deleteClusterTemplate(id: String) async throws
}

Data Models

Server Model

public struct Server: Codable, Sendable, ResourceIdentifiable, Timestamped {
    public let id: String
    public let name: String?
    public let status: String?
    public let taskState: String?
    public let vmState: String?
    public let powerState: Int?
    public let flavor: Flavor?
    public let image: ServerImage?
    public let addresses: [String: [ServerAddress]]?
    public let created: Date?
    public let updated: Date?
    public let metadata: [String: String]?
    public let securityGroups: [ServerSecurityGroup]?
    public let volumesAttached: [ServerVolumeAttachment]?
    public let keyName: String?
    public let availabilityZone: String?
    public let hostId: String?
    public let tenantId: String?
    public let userId: String?
}

Network Model

public struct Network: Codable, Sendable, ResourceIdentifiable, Timestamped {
    public let id: String
    public let name: String?
    public let status: String?
    public let shared: Bool?
    public let external: Bool?
    public let subnets: [String]?
    public let adminStateUp: Bool?
    public let mtu: Int?
    public let portSecurityEnabled: Bool?
    public let providerNetworkType: String?
    public let providerSegmentationId: Int?
    public let createdAt: Date?
    public let updatedAt: Date?
}

Volume Model

public struct Volume: Codable, Sendable, ResourceIdentifiable, Timestamped {
    public let id: String
    public let name: String?
    public let description: String?
    public let status: String?
    public let size: Int?
    public let volumeType: String?
    public let bootable: String?
    public let encrypted: Bool?
    public let multiattach: Bool?
    public let attachments: [VolumeAttachment]?
    public let availabilityZone: String?
    public let snapshotId: String?
    public let sourceVolid: String?
    public let createdAt: Date?
    public let updatedAt: Date?
}

Cluster Model (Magnum)

public struct Cluster: Codable, Sendable, ResourceIdentifiable, Timestamped {
    public let uuid: String
    public let name: String?
    public let status: String?
    public let statusReason: String?
    public let clusterTemplateId: String
    public let keypair: String?
    public let masterCount: Int?
    public let nodeCount: Int?
    public let masterAddresses: [String]?
    public let nodeAddresses: [String]?
    public let apiAddress: String?
    public let coeVersion: String?
    public let floatingIpEnabled: Bool?
    public let masterLbEnabled: Bool?
    public let labels: [String: String]?
    public let createdAt: Date?
    public let updatedAt: Date?

    // ResourceIdentifiable conformance
    public var id: String { uuid }
}

ClusterTemplate Model (Magnum)

public struct ClusterTemplate: Codable, Sendable, ResourceIdentifiable {
    public let uuid: String
    public let name: String?
    public let coe: String?
    public let imageId: String?
    public let keypairId: String?
    public let externalNetworkId: String?
    public let fixedNetwork: String?
    public let fixedSubnet: String?
    public let flavorId: String?
    public let masterFlavorId: String?
    public let volumeDriver: String?
    public let dockerVolumeSize: Int?
    public let networkDriver: String?
    public let dnsNameserver: String?
    public let floatingIpEnabled: Bool?
    public let masterLbEnabled: Bool?
    public let labels: [String: String]?

    // ResourceIdentifiable conformance
    public var id: String { uuid }
}

Hypervisor Model

public struct Hypervisor: Codable, Sendable, ResourceIdentifiable {
    public let id: String
    public let hypervisorHostname: String?
    public let hypervisorType: String?
    public let hypervisorVersion: Int?
    public let hostIp: String?
    public let state: String?
    public let status: String?
    public let vcpus: Int?
    public let vcpusUsed: Int?
    public let memoryMb: Int?
    public let memoryMbUsed: Int?
    public let localGb: Int?
    public let localGbUsed: Int?
    public let runningVms: Int?
    public let currentWorkload: Int?
    public let freeDiskGb: Int?
    public let freeRamMb: Int?
}

Error Handling

Error Classification

flowchart TD
    Error[API Error] --> Type{Error Type}

    Type --> |401| Auth[authenticationFailed]
    Type --> |403| Unauth[unauthorized]
    Type --> |404| NotFound[notFound]
    Type --> |409| Conflict[conflict]
    Type --> |413| Quota[quotaExceeded]
    Type --> |5xx| Server[serverError]
    Type --> |Network| Network[networkError]
    Type --> |Timeout| Timeout[timeout]
    Type --> |503| Unavail[serviceUnavailable]
    Type --> |Parse Error| Invalid[invalidResponse]
    Type --> |Other| Unknown[unknown]

    subgraph "Recoverable"
        Auth
        Quota
        Timeout
        Unavail
    end

    subgraph "Non-Recoverable"
        NotFound
        Conflict
        Invalid
    end

    style Auth fill:#ffd43b,stroke:#333,color:#000
    style NotFound fill:#ff6b6b,stroke:#333,color:#fff
    style Conflict fill:#ff6b6b,stroke:#333,color:#fff
public enum OpenStackError: Error, Sendable {
    case authenticationFailed
    case unauthorized
    case notFound(String)
    case conflict(String)
    case serverError(String)
    case networkError(String)
    case configurationError(String)
    case invalidResponse(String)
    case quotaExceeded(String)
    case serviceUnavailable(String)
    case timeout
    case unknown(String)
}

Example:

do {
    let server = try await client.getServer(id: "nonexistent")
} catch OpenStackError.notFound(let message) {
    print("Server not found: \(message)")
} catch OpenStackError.authenticationFailed {
    print("Need to re-authenticate")
} catch {
    print("Unexpected error: \(error)")
}

Logging

/// Protocol for custom logging implementations
public protocol OpenStackClientLogger: Sendable {
    func logError(_ message: String, context: [String: any Sendable])
    func logInfo(_ message: String, context: [String: any Sendable])
    func logDebug(_ message: String, context: [String: any Sendable])
    func logAPICall(_ method: String, url: String, statusCode: Int?, duration: TimeInterval?)
}

/// Default console logger implementation
public struct ConsoleLogger: OpenStackClientLogger {
    public init()
}

Custom Logger Example:

struct MyLogger: OpenStackClientLogger {
    func logError(_ message: String, context: [String: any Sendable]) {
        // Log to your logging system
    }

    func logInfo(_ message: String, context: [String: any Sendable]) {
        // Log informational messages
    }

    func logDebug(_ message: String, context: [String: any Sendable]) {
        // Log debug messages
    }

    func logAPICall(_ method: String, url: String, statusCode: Int?, duration: TimeInterval?) {
        // Log API calls for monitoring
    }
}

let client = try await OpenStackClient.connect(
    config: config,
    credentials: credentials,
    logger: MyLogger()
)

Best Practices

1. Use Convenience Methods When Possible

// Convenience method on client
let servers = try await client.listServers()

// Equivalent service access (more verbose)
let nova = await client.nova
let response = try await nova.listServers()
let servers = response.servers

2. Handle Authentication Expiration

// The client automatically refreshes tokens
// But you can monitor expiration if needed
if let expiration = client.timeUntilTokenExpiration, expiration < 60 {
    // Token expires soon, might want to warn user
}

3. Use Force Refresh Sparingly

// Default: Uses cache when available
let servers = try await client.listServers()

// Force API call (ignores cache)
let freshServers = try await client.listServers(forceRefresh: true)

4. Configure SSL for Development

// For self-signed certificates in development
let config = OpenStackConfig(
    authURL: URL(string: "https://dev-keystone:5000/v3")!,
    verifySSL: false  // WARNING: Only for development
)

5. Use Data Managers for UI Applications

// Data managers provide caching and incremental loading
let serverManager = await client.serverDataManager
let servers = try await serverManager.loadServers()

See Also: