1# -*- coding: utf-8 -*- 2from .baseapi import BaseAPI, GET, POST, PUT, DELETE 3 4 5class StickySessions(object): 6 """ 7 An object holding information on a LoadBalancer's sticky sessions settings. 8 9 Args: 10 type (str): The type of sticky sessions used. Can be "cookies" or 11 "none" 12 cookie_name (str, optional): The name used for the client cookie when 13 using cookies for sticky session 14 cookie_ttl_seconds (int, optional): The number of seconds until the 15 cookie expires 16 """ 17 def __init__(self, type='none', cookie_name='', cookie_ttl_seconds=None, **kwargs): 18 self.type = type 19 if type == 'cookies': 20 self.cookie_name = 'DO-LB' 21 self.cookie_ttl_seconds = 300 22 self.cookie_name = cookie_name 23 self.cookie_ttl_seconds = cookie_ttl_seconds 24 25 26class ForwardingRule(object): 27 """ 28 An object holding information about a LoadBalancer forwarding rule setting. 29 30 Args: 31 entry_protocol (str): The protocol used for traffic to a LoadBalancer. 32 The possible values are: "http", "https", or "tcp" 33 entry_port (int): The port the LoadBalancer instance will listen on 34 target_protocol (str): The protocol used for traffic from a 35 LoadBalancer to the backend Droplets. The possible values are: 36 "http", "https", or "tcp" 37 target_port (int): The port on the backend Droplets on which the 38 LoadBalancer will send traffic 39 certificate_id (str, optional): The ID of the TLS certificate used for 40 SSL termination if enabled 41 tls_passthrough (bool, optional): A boolean indicating if SSL encrypted 42 traffic will be passed through to the backend Droplets 43 """ 44 def __init__(self, entry_protocol=None, entry_port=None, 45 target_protocol=None, target_port=None, certificate_id="", 46 tls_passthrough=False): 47 self.entry_protocol = entry_protocol 48 self.entry_port = entry_port 49 self.target_protocol = target_protocol 50 self.target_port = target_port 51 self.certificate_id = certificate_id 52 self.tls_passthrough = tls_passthrough 53 54 55class HealthCheck(object): 56 """ 57 An object holding information about a LoadBalancer health check settings. 58 59 Args: 60 protocol (str): The protocol used for health checks. The possible 61 values are "http" or "tcp". 62 port (int): The port on the backend Droplets for heath checks 63 path (str): The path to send a health check request to 64 check_interval_seconds (int): The number of seconds between between two 65 consecutive health checks 66 response_timeout_seconds (int): The number of seconds the Load Balancer 67 instance will wait for a response until marking a check as failed 68 healthy_threshold (int): The number of times a health check must fail 69 for a backend Droplet to be removed from the pool 70 unhealthy_threshold (int): The number of times a health check must pass 71 for a backend Droplet to be re-added to the pool 72 """ 73 def __init__(self, protocol='http', port=80, path='/', 74 check_interval_seconds=10, response_timeout_seconds=5, 75 healthy_threshold=5, unhealthy_threshold=3): 76 self.protocol = protocol 77 self.port = port 78 self.path = path 79 self.check_interval_seconds = check_interval_seconds 80 self.response_timeout_seconds = response_timeout_seconds 81 self.healthy_threshold = healthy_threshold 82 self.unhealthy_threshold = unhealthy_threshold 83 84 85class LoadBalancer(BaseAPI): 86 """ 87 An object representing an DigitalOcean Load Balancer. 88 89 Attributes accepted at creation time: 90 91 Args: 92 name (str): The Load Balancer's name 93 region (str): The slug identifier for a DigitalOcean region 94 algorithm (str, optional): The load balancing algorithm to be \ 95 used. Currently, it must be either "round_robin" or \ 96 "least_connections" 97 forwarding_rules (obj:`list`): A list of `ForwrdingRules` objects 98 health_check (obj, optional): A `HealthCheck` object 99 sticky_sessions (obj, optional): A `StickySessions` object 100 redirect_http_to_https (bool, optional): A boolean indicating \ 101 whether HTTP requests to the Load Balancer should be \ 102 redirected to HTTPS 103 droplet_ids (obj:`list` of `int`): A list of IDs representing \ 104 Droplets to be added to the Load Balancer (mutually \ 105 exclusive with 'tag') 106 tag (str): A string representing a DigitalOcean Droplet tag \ 107 (mutually exclusive with 'droplet_ids') 108 vpc_uuid (str): ID of a VPC in which the Load Balancer will be created 109 110 Attributes returned by API: 111 * name (str): The Load Balancer's name 112 * id (str): An unique identifier for a LoadBalancer 113 * ip (str): Public IP address for a LoadBalancer 114 * region (str): The slug identifier for a DigitalOcean region 115 * algorithm (str, optional): The load balancing algorithm to be \ 116 used. Currently, it must be either "round_robin" or \ 117 "least_connections" 118 * forwarding_rules (obj:`list`): A list of `ForwrdingRules` objects 119 * health_check (obj, optional): A `HealthCheck` object 120 * sticky_sessions (obj, optional): A `StickySessions` object 121 * redirect_http_to_https (bool, optional): A boolean indicating \ 122 whether HTTP requests to the Load Balancer should be \ 123 redirected to HTTPS 124 * droplet_ids (obj:`list` of `int`): A list of IDs representing \ 125 Droplets to be added to the Load Balancer 126 * tag (str): A string representing a DigitalOcean Droplet tag 127 * status (string): An indication the current state of the LoadBalancer 128 * created_at (str): The date and time when the LoadBalancer was created 129 * vpc_uuid (str): ID of a VPC which the Load Balancer is assigned to 130 """ 131 def __init__(self, *args, **kwargs): 132 self.id = None 133 self.name = None 134 self.region = None 135 self.algorithm = None 136 self.forwarding_rules = [] 137 self.health_check = None 138 self.sticky_sessions = None 139 self.redirect_http_to_https = False 140 self.droplet_ids = [] 141 self.tag = None 142 self.status = None 143 self.created_at = None 144 self.vpc_uuid = None 145 146 super(LoadBalancer, self).__init__(*args, **kwargs) 147 148 @classmethod 149 def get_object(cls, api_token, id): 150 """ 151 Class method that will return a LoadBalancer object by its ID. 152 153 Args: 154 api_token (str): DigitalOcean API token 155 id (str): Load Balancer ID 156 """ 157 load_balancer = cls(token=api_token, id=id) 158 load_balancer.load() 159 return load_balancer 160 161 def load(self): 162 """ 163 Loads updated attributues for a LoadBalancer object. 164 165 Requires self.id to be set. 166 """ 167 data = self.get_data('load_balancers/%s' % self.id, type=GET) 168 load_balancer = data['load_balancer'] 169 170 # Setting the attribute values 171 for attr in load_balancer.keys(): 172 if attr == 'health_check': 173 health_check = HealthCheck(**load_balancer['health_check']) 174 setattr(self, attr, health_check) 175 elif attr == 'sticky_sessions': 176 sticky_ses = StickySessions(**load_balancer['sticky_sessions']) 177 setattr(self, attr, sticky_ses) 178 elif attr == 'forwarding_rules': 179 rules = list() 180 for rule in load_balancer['forwarding_rules']: 181 rules.append(ForwardingRule(**rule)) 182 setattr(self, attr, rules) 183 else: 184 setattr(self, attr, load_balancer[attr]) 185 186 return self 187 188 def create(self, *args, **kwargs): 189 """ 190 Creates a new LoadBalancer. 191 192 Note: Every argument and parameter given to this method will be 193 assigned to the object. 194 195 Args: 196 name (str): The Load Balancer's name 197 region (str): The slug identifier for a DigitalOcean region 198 algorithm (str, optional): The load balancing algorithm to be 199 used. Currently, it must be either "round_robin" or 200 "least_connections" 201 forwarding_rules (obj:`list`): A list of `ForwrdingRules` objects 202 health_check (obj, optional): A `HealthCheck` object 203 sticky_sessions (obj, optional): A `StickySessions` object 204 redirect_http_to_https (bool, optional): A boolean indicating 205 whether HTTP requests to the Load Balancer should be 206 redirected to HTTPS 207 droplet_ids (obj:`list` of `int`): A list of IDs representing 208 Droplets to be added to the Load Balancer (mutually 209 exclusive with 'tag') 210 tag (str): A string representing a DigitalOcean Droplet tag 211 (mutually exclusive with 'droplet_ids') 212 vpc_uuid (str): ID of a Load Balancer in which the Droplet will be 213 created 214 """ 215 rules_dict = [rule.__dict__ for rule in self.forwarding_rules] 216 217 params = {'name': self.name, 'region': self.region, 218 'forwarding_rules': rules_dict, 219 'redirect_http_to_https': self.redirect_http_to_https, 220 'vpc_uuid': self.vpc_uuid} 221 222 if self.droplet_ids and self.tag: 223 raise ValueError('droplet_ids and tag are mutually exclusive args') 224 elif self.tag: 225 params['tag'] = self.tag 226 else: 227 params['droplet_ids'] = self.droplet_ids 228 229 if self.algorithm: 230 params['algorithm'] = self.algorithm 231 if self.health_check: 232 params['health_check'] = self.health_check.__dict__ 233 if self.sticky_sessions: 234 params['sticky_sessions'] = self.sticky_sessions.__dict__ 235 236 data = self.get_data('load_balancers', type=POST, params=params) 237 238 if data: 239 self.id = data['load_balancer']['id'] 240 self.ip = data['load_balancer']['ip'] 241 self.algorithm = data['load_balancer']['algorithm'] 242 self.health_check = HealthCheck( 243 **data['load_balancer']['health_check']) 244 self.sticky_sessions = StickySessions( 245 **data['load_balancer']['sticky_sessions']) 246 self.droplet_ids = data['load_balancer']['droplet_ids'] 247 self.status = data['load_balancer']['status'] 248 self.created_at = data['load_balancer']['created_at'] 249 self.vpc_uuid = data['load_balancer']['vpc_uuid'] 250 251 return self 252 253 def save(self): 254 """ 255 Save the LoadBalancer 256 """ 257 forwarding_rules = [rule.__dict__ for rule in self.forwarding_rules] 258 259 data = { 260 'name': self.name, 261 'region': self.region['slug'], 262 'forwarding_rules': forwarding_rules, 263 'redirect_http_to_https': self.redirect_http_to_https, 264 'vpc_uuid': self.vpc_uuid 265 } 266 267 if self.tag: 268 data['tag'] = self.tag 269 else: 270 data['droplet_ids'] = self.droplet_ids 271 272 if self.algorithm: 273 data["algorithm"] = self.algorithm 274 if self.health_check: 275 data['health_check'] = self.health_check.__dict__ 276 if self.sticky_sessions: 277 data['sticky_sessions'] = self.sticky_sessions.__dict__ 278 279 return self.get_data("load_balancers/%s" % self.id, 280 type=PUT, 281 params=data) 282 283 def destroy(self): 284 """ 285 Destroy the LoadBalancer 286 """ 287 return self.get_data('load_balancers/%s' % self.id, type=DELETE) 288 289 def add_droplets(self, droplet_ids): 290 """ 291 Assign a LoadBalancer to a Droplet. 292 293 Args: 294 droplet_ids (obj:`list` of `int`): A list of Droplet IDs 295 """ 296 return self.get_data( 297 "load_balancers/%s/droplets" % self.id, 298 type=POST, 299 params={"droplet_ids": droplet_ids} 300 ) 301 302 def remove_droplets(self, droplet_ids): 303 """ 304 Unassign a LoadBalancer. 305 306 Args: 307 droplet_ids (obj:`list` of `int`): A list of Droplet IDs 308 """ 309 return self.get_data( 310 "load_balancers/%s/droplets" % self.id, 311 type=DELETE, 312 params={"droplet_ids": droplet_ids} 313 ) 314 315 def add_forwarding_rules(self, forwarding_rules): 316 """ 317 Adds new forwarding rules to a LoadBalancer. 318 319 Args: 320 forwarding_rules (obj:`list`): A list of `ForwrdingRules` objects 321 """ 322 rules_dict = [rule.__dict__ for rule in forwarding_rules] 323 324 return self.get_data( 325 "load_balancers/%s/forwarding_rules" % self.id, 326 type=POST, 327 params={"forwarding_rules": rules_dict} 328 ) 329 330 def remove_forwarding_rules(self, forwarding_rules): 331 """ 332 Removes existing forwarding rules from a LoadBalancer. 333 334 Args: 335 forwarding_rules (obj:`list`): A list of `ForwrdingRules` objects 336 """ 337 rules_dict = [rule.__dict__ for rule in forwarding_rules] 338 339 return self.get_data( 340 "load_balancers/%s/forwarding_rules" % self.id, 341 type=DELETE, 342 params={"forwarding_rules": rules_dict} 343 ) 344 345 def __str__(self): 346 return "%s" % (self.id) 347