Skip to content

Helpers

cloudspells.core.helper

Cloud-neutral utility helpers for CloudSpells resource management.

Provides Helper, a stateless utility class whose methods cover two cloud-neutral areas:

  • Subnet CIDR calculation — splitting a supernet CIDR into n equal sub-networks.
  • SSH key generation — creating RSA 4096-bit key pairs on the fly using the system ssh-keygen binary.

OCI-specific helpers (image resolution and availability-domain mapping) live in providers.oci.helper.

Helper

Stateless utility methods used by CloudSpells spells.

All methods are safe to call multiple times and have no side-effects on instance state. Instantiate with Helper() wherever needed.

Source code in packages/cloudspells-core/src/cloudspells/core/helper.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
class Helper:
    """Stateless utility methods used by CloudSpells spells.

    All methods are safe to call multiple times and have no side-effects
    on instance state.  Instantiate with `Helper()` wherever needed.
    """

    def get_random_word(self) -> str:
        """Return a single random English word.

        Uses the `random-word` library internally.  Useful for generating
        unique name suffixes during testing or prototyping.

        Returns:
            A random lower-case word string (e.g. `"banana"`).
        """
        r = RandomWords()
        return r.get_random_word()

    def generate_ssh_key_pair(self, stack_name: str, resource_name: str) -> tuple[str, str]:
        """Generate an RSA 4096-bit SSH key pair using `ssh-keygen`.

        The key pair is created in a temporary directory that is
        automatically cleaned up after the keys have been read.  The key
        comment is set to `"ociblocks-{stack_name}-{resource_name}"` for
        traceability.

        Args:
            stack_name: Stack identifier embedded in the key comment.
            resource_name: Resource name embedded in the key comment.

        Returns:
            A `(public_key, private_key)` tuple where both elements are
            plain strings.  public_key is the single-line OpenSSH public
            key; private_key is the full PEM-encoded private key.

        Raises:
            subprocess.CalledProcessError: If `ssh-keygen` exits with a
                non-zero status.
        """
        with tempfile.TemporaryDirectory() as tmpdir:
            key_path = os.path.join(tmpdir, "id_rsa")
            subprocess.run(
                [
                    "ssh-keygen",
                    "-t",
                    "rsa",
                    "-b",
                    "4096",
                    "-f",
                    key_path,
                    "-N",
                    "",
                    "-C",
                    f"ociblocks-{stack_name}-{resource_name}",
                ],
                check=True,
                capture_output=True,
            )
            with open(f"{key_path}.pub") as f:
                public_key = f.read().strip()
            with open(key_path) as f:
                private_key = f.read()
        return public_key, private_key

    def calculate_subnets(self, cidr: str, num_subnets: int) -> List[str]:
        """Split a supernet CIDR into n equal sub-networks.

        The method increases the prefix length of cidr by the minimum
        number of bits required to accommodate at least num_subnets subnets,
        then returns the first num_subnets of them.

        Args:
            cidr: The supernet CIDR block string (e.g. `"10.0.0.0/16"`).
            num_subnets: The number of subnets to return.

        Returns:
            List of num_subnets CIDR block strings in address order
            (e.g. `["10.0.0.0/17", "10.0.128.0/17"]` for
            `calculate_subnets("10.0.0.0/16", 2)`).

        Example:
            >>> h = Helper()
            >>> h.calculate_subnets("10.0.0.0/16", 2)
            ['10.0.0.0/17', '10.0.128.0/17']
        """
        supernet: Union[ipaddress.IPv4Network, ipaddress.IPv6Network] = ipaddress.ip_network(cidr)
        new_prefix_length: int = supernet.prefixlen
        while (2 ** (new_prefix_length - supernet.prefixlen)) < num_subnets:
            new_prefix_length += 1
        subnets: List[Union[ipaddress.IPv4Network, ipaddress.IPv6Network]] = list(
            supernet.subnets(new_prefix=new_prefix_length)
        )
        return [str(subnet) for subnet in subnets[:num_subnets]]

get_random_word() -> str

Return a single random English word.

Uses the random-word library internally. Useful for generating unique name suffixes during testing or prototyping.

Returns:

Type Description
str

A random lower-case word string (e.g. "banana").

Source code in packages/cloudspells-core/src/cloudspells/core/helper.py
31
32
33
34
35
36
37
38
39
40
41
def get_random_word(self) -> str:
    """Return a single random English word.

    Uses the `random-word` library internally.  Useful for generating
    unique name suffixes during testing or prototyping.

    Returns:
        A random lower-case word string (e.g. `"banana"`).
    """
    r = RandomWords()
    return r.get_random_word()

generate_ssh_key_pair(stack_name: str, resource_name: str) -> tuple[str, str]

Generate an RSA 4096-bit SSH key pair using ssh-keygen.

The key pair is created in a temporary directory that is automatically cleaned up after the keys have been read. The key comment is set to "ociblocks-{stack_name}-{resource_name}" for traceability.

Parameters:

Name Type Description Default
stack_name str

Stack identifier embedded in the key comment.

required
resource_name str

Resource name embedded in the key comment.

required

Returns:

Type Description
str

A (public_key, private_key) tuple where both elements are

str

plain strings. public_key is the single-line OpenSSH public

tuple[str, str]

key; private_key is the full PEM-encoded private key.

Raises:

Type Description
CalledProcessError

If ssh-keygen exits with a non-zero status.

Source code in packages/cloudspells-core/src/cloudspells/core/helper.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def generate_ssh_key_pair(self, stack_name: str, resource_name: str) -> tuple[str, str]:
    """Generate an RSA 4096-bit SSH key pair using `ssh-keygen`.

    The key pair is created in a temporary directory that is
    automatically cleaned up after the keys have been read.  The key
    comment is set to `"ociblocks-{stack_name}-{resource_name}"` for
    traceability.

    Args:
        stack_name: Stack identifier embedded in the key comment.
        resource_name: Resource name embedded in the key comment.

    Returns:
        A `(public_key, private_key)` tuple where both elements are
        plain strings.  public_key is the single-line OpenSSH public
        key; private_key is the full PEM-encoded private key.

    Raises:
        subprocess.CalledProcessError: If `ssh-keygen` exits with a
            non-zero status.
    """
    with tempfile.TemporaryDirectory() as tmpdir:
        key_path = os.path.join(tmpdir, "id_rsa")
        subprocess.run(
            [
                "ssh-keygen",
                "-t",
                "rsa",
                "-b",
                "4096",
                "-f",
                key_path,
                "-N",
                "",
                "-C",
                f"ociblocks-{stack_name}-{resource_name}",
            ],
            check=True,
            capture_output=True,
        )
        with open(f"{key_path}.pub") as f:
            public_key = f.read().strip()
        with open(key_path) as f:
            private_key = f.read()
    return public_key, private_key

calculate_subnets(cidr: str, num_subnets: int) -> List[str]

Split a supernet CIDR into n equal sub-networks.

The method increases the prefix length of cidr by the minimum number of bits required to accommodate at least num_subnets subnets, then returns the first num_subnets of them.

Parameters:

Name Type Description Default
cidr str

The supernet CIDR block string (e.g. "10.0.0.0/16").

required
num_subnets int

The number of subnets to return.

required

Returns:

Type Description
List[str]

List of num_subnets CIDR block strings in address order

List[str]

(e.g. ["10.0.0.0/17", "10.0.128.0/17"] for

List[str]

calculate_subnets("10.0.0.0/16", 2)).

Example

h = Helper() h.calculate_subnets("10.0.0.0/16", 2) ['10.0.0.0/17', '10.0.128.0/17']

Source code in packages/cloudspells-core/src/cloudspells/core/helper.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def calculate_subnets(self, cidr: str, num_subnets: int) -> List[str]:
    """Split a supernet CIDR into n equal sub-networks.

    The method increases the prefix length of cidr by the minimum
    number of bits required to accommodate at least num_subnets subnets,
    then returns the first num_subnets of them.

    Args:
        cidr: The supernet CIDR block string (e.g. `"10.0.0.0/16"`).
        num_subnets: The number of subnets to return.

    Returns:
        List of num_subnets CIDR block strings in address order
        (e.g. `["10.0.0.0/17", "10.0.128.0/17"]` for
        `calculate_subnets("10.0.0.0/16", 2)`).

    Example:
        >>> h = Helper()
        >>> h.calculate_subnets("10.0.0.0/16", 2)
        ['10.0.0.0/17', '10.0.128.0/17']
    """
    supernet: Union[ipaddress.IPv4Network, ipaddress.IPv6Network] = ipaddress.ip_network(cidr)
    new_prefix_length: int = supernet.prefixlen
    while (2 ** (new_prefix_length - supernet.prefixlen)) < num_subnets:
        new_prefix_length += 1
    subnets: List[Union[ipaddress.IPv4Network, ipaddress.IPv6Network]] = list(
        supernet.subnets(new_prefix=new_prefix_length)
    )
    return [str(subnet) for subnet in subnets[:num_subnets]]