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