File size: 8,141 Bytes
105b369
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
from typing import Optional, Any, Dict, List, Union

from phi.aws.api_client import AwsApiClient
from phi.aws.resource.base import AwsResource
from phi.aws.resource.reference import AwsReference
from phi.aws.resource.cloudformation.stack import CloudFormationStack
from phi.cli.console import print_info
from phi.utils.log import logger


class DbSubnetGroup(AwsResource):
    """
    Reference:
    - https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds.html#RDS.Client.create_db_subnet_group

    Creates a new DB subnet group. DB subnet groups must contain at least one subnet in at least
    two AZs in the Amazon Web Services Region.
    """

    resource_type: Optional[str] = "DbSubnetGroup"
    service_name: str = "rds"

    # The name for the DB subnet group. This value is stored as a lowercase string.
    # Constraints:
    #   Must contain no more than 255 letters, numbers, periods, underscores, spaces, or hyphens.
    #   Must not be default.
    #   First character must be a letter.
    #   Example: mydbsubnetgroup
    name: str
    # The description for the DB subnet group.
    description: Optional[str] = None
    # The EC2 Subnet IDs for the DB subnet group.
    subnet_ids: Optional[Union[List[str], AwsReference]] = None
    # Get Subnet IDs from a VPC CloudFormationStack
    # First gets private subnets from the vpc stack, then public subnets
    vpc_stack: Optional[CloudFormationStack] = None
    # Tags to assign to the DB subnet group.
    tags: Optional[List[Dict[str, str]]] = None

    def get_subnet_ids(self, aws_client: AwsApiClient) -> List[str]:
        """Returns the subnet_ids for the DbSubnetGroup

        Args:
            aws_client: The AwsApiClient for the current cluster
        """
        subnet_ids = []
        if self.subnet_ids is not None:
            if isinstance(self.subnet_ids, list):
                logger.debug("Getting subnet_ids from list")
                subnet_ids = self.subnet_ids
            elif isinstance(self.subnet_ids, AwsReference):
                logger.debug("Getting subnet_ids from reference")
                subnet_ids = self.subnet_ids.get_reference(aws_client=aws_client)
        if len(subnet_ids) == 0 and self.vpc_stack is not None:
            logger.debug("Getting private subnet_ids from vpc stack")
            private_subnet_ids = self.vpc_stack.get_private_subnets(aws_client=aws_client)
            if private_subnet_ids is not None:
                subnet_ids.extend(private_subnet_ids)
            if len(subnet_ids) == 0:
                logger.debug("Getting public subnet_ids from vpc stack")
                public_subnet_ids = self.vpc_stack.get_public_subnets(aws_client=aws_client)
                if public_subnet_ids is not None:
                    subnet_ids.extend(public_subnet_ids)
        return subnet_ids

    def _create(self, aws_client: AwsApiClient) -> bool:
        """Creates the DbSubnetGroup

        Args:
            aws_client: The AwsApiClient for the current cluster
        """

        print_info(f"Creating {self.get_resource_type()}: {self.get_resource_name()}")
        try:
            # Get subnet_ids
            subnet_ids = self.get_subnet_ids(aws_client=aws_client)

            # create a dict of args which are not null, otherwise aws type validation fails
            not_null_args: Dict[str, Any] = {}
            if self.tags:
                not_null_args["Tags"] = self.tags

            # Create DbSubnetGroup
            service_client = self.get_service_client(aws_client)
            create_response = service_client.create_db_subnet_group(
                DBSubnetGroupName=self.name,
                DBSubnetGroupDescription=self.description or f"Created for {self.name}",
                SubnetIds=subnet_ids,
                **not_null_args,
            )
            logger.debug(f"create_response type: {type(create_response)}")
            logger.debug(f"create_response: {create_response}")

            self.active_resource = create_response.get("DBSubnetGroup", None)
            if self.active_resource is not None:
                print_info(f"{self.get_resource_type()}: {self.get_resource_name()} created")
                logger.debug(f"DbSubnetGroup: {self.active_resource}")
                return True
        except Exception as e:
            logger.error(f"{self.get_resource_type()} could not be created.")
            logger.error(e)
        return False

    def _read(self, aws_client: AwsApiClient) -> Optional[Any]:
        """Returns the DbSubnetGroup

        Args:
            aws_client: The AwsApiClient for the current cluster
        """
        from botocore.exceptions import ClientError

        logger.debug(f"Reading {self.get_resource_type()}: {self.get_resource_name()}")
        try:
            service_client = self.get_service_client(aws_client)
            describe_response = service_client.describe_db_subnet_groups(DBSubnetGroupName=self.name)
            logger.debug(f"describe_response type: {type(describe_response)}")
            logger.debug(f"describe_response: {describe_response}")

            db_subnet_group_list = describe_response.get("DBSubnetGroups", None)
            if db_subnet_group_list is not None and isinstance(db_subnet_group_list, list):
                for _db_subnet_group in db_subnet_group_list:
                    _db_sg_name = _db_subnet_group.get("DBSubnetGroupName", None)
                    if _db_sg_name == self.name:
                        self.active_resource = _db_subnet_group
                        break

            if self.active_resource is None:
                logger.debug(f"No {self.get_resource_type()} found")
                return None

            logger.debug(f"DbSubnetGroup: {self.active_resource}")
        except ClientError as ce:
            logger.debug(f"ClientError: {ce}")
        except Exception as e:
            logger.error(f"Error reading {self.get_resource_type()}.")
            logger.error(e)
        return self.active_resource

    def _delete(self, aws_client: AwsApiClient) -> bool:
        """Deletes the DbSubnetGroup

        Args:
            aws_client: The AwsApiClient for the current cluster
        """

        print_info(f"Deleting {self.get_resource_type()}: {self.get_resource_name()}")
        try:
            service_client = self.get_service_client(aws_client)
            self.active_resource = None

            delete_response = service_client.delete_db_subnet_group(DBSubnetGroupName=self.name)
            logger.debug(f"delete_response: {delete_response}")
            return True
        except Exception as e:
            logger.error(f"{self.get_resource_type()} could not be deleted.")
            logger.error("Please try again or delete resources manually.")
            logger.error(e)
        return False

    def _update(self, aws_client: AwsApiClient) -> bool:
        """Updates the DbSubnetGroup

        Args:
            aws_client: The AwsApiClient for the current cluster
        """

        print_info(f"Updating {self.get_resource_type()}: {self.get_resource_name()}")
        try:
            # Get subnet_ids
            subnet_ids = self.get_subnet_ids(aws_client=aws_client)

            # Update DbSubnetGroup
            service_client = self.get_service_client(aws_client)
            update_response = service_client.modify_db_subnet_group(
                DBSubnetGroupName=self.name,
                DBSubnetGroupDescription=self.description or f"Created for {self.name}",
                SubnetIds=subnet_ids,
            )
            logger.debug(f"update_response: {update_response}")

            self.active_resource = update_response.get("DBSubnetGroup", None)
            if self.active_resource is not None:
                print_info(f"{self.get_resource_type()}: {self.get_resource_name()} updated")
                return True
        except Exception as e:
            logger.error(f"{self.get_resource_type()} could not be updated.")
            logger.error("Please try again or update resources manually.")
            logger.error(e)
        return False