2015-06-22 22:47:27 +02:00
|
|
|
"""
|
2016-04-07 19:29:08 +02:00
|
|
|
.. module: lemur.plugins.lemur_aws.elb
|
2015-06-22 22:47:27 +02:00
|
|
|
:synopsis: Module contains some often used and helpful classes that
|
|
|
|
are used to deal with ELBs
|
|
|
|
|
2015-08-03 18:49:33 +02:00
|
|
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
2015-06-22 22:47:27 +02:00
|
|
|
"""
|
2016-07-12 17:40:49 +02:00
|
|
|
import botocore
|
2015-06-22 22:47:27 +02:00
|
|
|
from flask import current_app
|
|
|
|
|
2016-07-12 17:40:49 +02:00
|
|
|
from retrying import retry
|
|
|
|
|
2019-04-25 22:50:41 +02:00
|
|
|
from lemur.extensions import metrics, sentry
|
2015-06-22 22:47:27 +02:00
|
|
|
from lemur.exceptions import InvalidListener
|
2016-12-01 00:11:17 +01:00
|
|
|
from lemur.plugins.lemur_aws.sts import sts_client
|
2015-06-22 22:47:27 +02:00
|
|
|
|
|
|
|
|
2016-07-12 17:40:49 +02:00
|
|
|
def retry_throttled(exception):
|
|
|
|
"""
|
2016-12-27 19:31:33 +01:00
|
|
|
Determines if this exception is due to throttling
|
2016-07-12 17:40:49 +02:00
|
|
|
:param exception:
|
|
|
|
:return:
|
|
|
|
"""
|
2019-04-26 19:16:18 +02:00
|
|
|
|
|
|
|
# Log details about the exception
|
|
|
|
try:
|
|
|
|
raise exception
|
|
|
|
except Exception as e:
|
|
|
|
current_app.logger.error("ELB retry_throttled triggered", exc_info=True)
|
2019-07-01 17:35:04 +02:00
|
|
|
metrics.send("elb_retry", "counter", 1, metric_tags={"exception": str(e)})
|
2019-04-26 19:16:18 +02:00
|
|
|
sentry.captureException()
|
|
|
|
|
2016-07-12 17:40:49 +02:00
|
|
|
if isinstance(exception, botocore.exceptions.ClientError):
|
2019-05-16 16:57:02 +02:00
|
|
|
if exception.response["Error"]["Code"] == "LoadBalancerNotFound":
|
2017-01-05 02:46:47 +01:00
|
|
|
return False
|
2016-12-28 18:52:23 +01:00
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
if exception.response["Error"]["Code"] == "CertificateNotFound":
|
2017-01-05 02:46:47 +01:00
|
|
|
return False
|
2016-12-27 19:31:33 +01:00
|
|
|
return True
|
2016-07-12 17:40:49 +02:00
|
|
|
|
|
|
|
|
2015-06-22 22:47:27 +02:00
|
|
|
def is_valid(listener_tuple):
|
|
|
|
"""
|
|
|
|
There are a few rules that aws has when creating listeners,
|
|
|
|
this function ensures those rules are met before we try and create
|
|
|
|
or update a listener.
|
|
|
|
|
|
|
|
While these could be caught with boto exception handling, I would
|
|
|
|
rather be nice and catch these early before we sent them out to aws.
|
|
|
|
It also gives us an opportunity to create nice user warnings.
|
|
|
|
|
|
|
|
This validity check should also be checked in the frontend
|
|
|
|
but must also be enforced by server.
|
|
|
|
|
|
|
|
:param listener_tuple:
|
|
|
|
"""
|
|
|
|
lb_port, i_port, lb_protocol, arn = listener_tuple
|
2019-05-16 16:57:02 +02:00
|
|
|
if lb_protocol.lower() in ["ssl", "https"]:
|
2015-06-22 22:47:27 +02:00
|
|
|
if not arn:
|
|
|
|
raise InvalidListener
|
|
|
|
|
|
|
|
return listener_tuple
|
|
|
|
|
2015-07-21 22:06:13 +02:00
|
|
|
|
2016-06-27 23:40:46 +02:00
|
|
|
def get_all_elbs(**kwargs):
|
|
|
|
"""
|
2016-07-12 17:40:49 +02:00
|
|
|
Fetches all elbs for a given account/region
|
|
|
|
|
|
|
|
:param kwargs:
|
|
|
|
:return:
|
2016-06-27 23:40:46 +02:00
|
|
|
"""
|
2016-07-12 17:40:49 +02:00
|
|
|
elbs = []
|
2019-04-26 19:16:18 +02:00
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
response = get_elbs(**kwargs)
|
2016-07-12 17:40:49 +02:00
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
elbs += response["LoadBalancerDescriptions"]
|
2016-07-12 17:40:49 +02:00
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
if not response.get("NextMarker"):
|
2019-04-26 19:16:18 +02:00
|
|
|
return elbs
|
|
|
|
else:
|
2019-05-16 16:57:02 +02:00
|
|
|
kwargs.update(dict(Marker=response["NextMarker"]))
|
2019-04-26 19:16:18 +02:00
|
|
|
except Exception as e: # noqa
|
2019-05-16 16:57:02 +02:00
|
|
|
metrics.send("get_all_elbs_error", "counter", 1)
|
2019-04-26 19:16:18 +02:00
|
|
|
sentry.captureException()
|
|
|
|
raise
|
2016-06-27 23:40:46 +02:00
|
|
|
|
|
|
|
|
2016-12-21 17:23:14 +01:00
|
|
|
def get_all_elbs_v2(**kwargs):
|
|
|
|
"""
|
|
|
|
Fetches all elbs for a given account/region
|
|
|
|
|
|
|
|
:param kwargs:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
elbs = []
|
|
|
|
|
2019-04-26 19:16:18 +02:00
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
response = get_elbs_v2(**kwargs)
|
2019-05-16 16:57:02 +02:00
|
|
|
elbs += response["LoadBalancers"]
|
2019-04-26 19:16:18 +02:00
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
if not response.get("NextMarker"):
|
2019-04-26 19:16:18 +02:00
|
|
|
return elbs
|
|
|
|
else:
|
2019-05-16 16:57:02 +02:00
|
|
|
kwargs.update(dict(Marker=response["NextMarker"]))
|
2019-04-26 19:16:18 +02:00
|
|
|
except Exception as e: # noqa
|
2019-05-16 16:57:02 +02:00
|
|
|
metrics.send("get_all_elbs_v2_error", "counter", 1)
|
2019-04-26 19:16:18 +02:00
|
|
|
sentry.captureException()
|
|
|
|
raise
|
2016-12-21 17:23:14 +01:00
|
|
|
|
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
@sts_client("elbv2")
|
2019-04-26 19:16:18 +02:00
|
|
|
@retry(retry_on_exception=retry_throttled, wait_fixed=2000, stop_max_attempt_number=20)
|
2016-12-21 17:23:14 +01:00
|
|
|
def get_listener_arn_from_endpoint(endpoint_name, endpoint_port, **kwargs):
|
|
|
|
"""
|
2017-09-26 00:33:42 +02:00
|
|
|
Get a listener ARN from an endpoint.
|
2016-12-21 17:23:14 +01:00
|
|
|
:param endpoint_name:
|
|
|
|
:param endpoint_port:
|
|
|
|
:return:
|
|
|
|
"""
|
2019-04-26 19:16:18 +02:00
|
|
|
try:
|
2019-05-16 16:57:02 +02:00
|
|
|
client = kwargs.pop("client")
|
2019-04-26 19:16:18 +02:00
|
|
|
elbs = client.describe_load_balancers(Names=[endpoint_name])
|
2019-05-16 16:57:02 +02:00
|
|
|
for elb in elbs["LoadBalancers"]:
|
|
|
|
listeners = client.describe_listeners(
|
|
|
|
LoadBalancerArn=elb["LoadBalancerArn"]
|
|
|
|
)
|
|
|
|
for listener in listeners["Listeners"]:
|
|
|
|
if listener["Port"] == endpoint_port:
|
|
|
|
return listener["ListenerArn"]
|
2019-04-26 19:16:18 +02:00
|
|
|
except Exception as e: # noqa
|
2019-05-16 16:57:02 +02:00
|
|
|
metrics.send(
|
|
|
|
"get_listener_arn_from_endpoint_error",
|
|
|
|
"counter",
|
|
|
|
1,
|
|
|
|
metric_tags={
|
2019-07-01 17:35:04 +02:00
|
|
|
"error": str(e),
|
2019-05-16 16:57:02 +02:00
|
|
|
"endpoint_name": endpoint_name,
|
|
|
|
"endpoint_port": endpoint_port,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
sentry.captureException(
|
|
|
|
extra={
|
|
|
|
"endpoint_name": str(endpoint_name),
|
|
|
|
"endpoint_port": str(endpoint_port),
|
|
|
|
}
|
|
|
|
)
|
2019-04-26 19:16:18 +02:00
|
|
|
raise
|
2016-12-21 17:23:14 +01:00
|
|
|
|
|
|
|
|
2020-11-24 00:24:11 +01:00
|
|
|
@sts_client("elbv2")
|
|
|
|
@retry(retry_on_exception=retry_throttled, wait_fixed=2000, stop_max_attempt_number=5)
|
|
|
|
def get_load_balancer_arn_from_endpoint(endpoint_name, **kwargs):
|
|
|
|
"""
|
|
|
|
Get a load balancer ARN from an endpoint.
|
|
|
|
:param endpoint_name:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
client = kwargs.pop("client")
|
|
|
|
elbs = client.describe_load_balancers(Names=[endpoint_name])
|
2020-11-30 20:15:23 +01:00
|
|
|
if "LoadBalancers" in elbs and elbs["LoadBalancers"]:
|
|
|
|
return elbs["LoadBalancers"][0]["LoadBalancerArn"]
|
2020-11-24 00:24:11 +01:00
|
|
|
|
|
|
|
except Exception as e: # noqa
|
|
|
|
metrics.send(
|
|
|
|
"get_load_balancer_arn_from_endpoint",
|
|
|
|
"counter",
|
|
|
|
1,
|
|
|
|
metric_tags={
|
|
|
|
"error": str(e),
|
|
|
|
"endpoint_name": endpoint_name,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
sentry.captureException(
|
|
|
|
extra={
|
|
|
|
"endpoint_name": str(endpoint_name),
|
|
|
|
}
|
|
|
|
)
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
@sts_client("elb")
|
2019-04-26 19:16:18 +02:00
|
|
|
@retry(retry_on_exception=retry_throttled, wait_fixed=2000, stop_max_attempt_number=20)
|
2016-12-21 17:23:14 +01:00
|
|
|
def get_elbs(**kwargs):
|
|
|
|
"""
|
|
|
|
Fetches one page elb objects for a given account and region.
|
|
|
|
"""
|
2019-04-26 19:16:18 +02:00
|
|
|
try:
|
2019-05-16 16:57:02 +02:00
|
|
|
client = kwargs.pop("client")
|
2019-04-26 19:16:18 +02:00
|
|
|
return client.describe_load_balancers(**kwargs)
|
|
|
|
except Exception as e: # noqa
|
2019-07-01 17:35:04 +02:00
|
|
|
metrics.send("get_elbs_error", "counter", 1, metric_tags={"error": str(e)})
|
2019-04-26 19:16:18 +02:00
|
|
|
sentry.captureException()
|
|
|
|
raise
|
2016-12-21 17:23:14 +01:00
|
|
|
|
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
@sts_client("elbv2")
|
2019-04-26 19:16:18 +02:00
|
|
|
@retry(retry_on_exception=retry_throttled, wait_fixed=2000, stop_max_attempt_number=20)
|
2016-12-21 17:23:14 +01:00
|
|
|
def get_elbs_v2(**kwargs):
|
|
|
|
"""
|
|
|
|
Fetches one page of elb objects for a given account and region.
|
|
|
|
|
|
|
|
:param kwargs:
|
|
|
|
:return:
|
|
|
|
"""
|
2019-04-26 19:16:18 +02:00
|
|
|
try:
|
2019-05-16 16:57:02 +02:00
|
|
|
client = kwargs.pop("client")
|
2019-04-26 19:16:18 +02:00
|
|
|
return client.describe_load_balancers(**kwargs)
|
|
|
|
except Exception as e: # noqa
|
2019-07-01 17:35:04 +02:00
|
|
|
metrics.send("get_elbs_v2_error", "counter", 1, metric_tags={"error": str(e)})
|
2019-04-26 19:16:18 +02:00
|
|
|
sentry.captureException()
|
|
|
|
raise
|
2016-12-21 17:23:14 +01:00
|
|
|
|
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
@sts_client("elbv2")
|
2019-04-26 19:16:18 +02:00
|
|
|
@retry(retry_on_exception=retry_throttled, wait_fixed=2000, stop_max_attempt_number=20)
|
2016-12-21 17:23:14 +01:00
|
|
|
def describe_listeners_v2(**kwargs):
|
|
|
|
"""
|
|
|
|
Fetches one page of listener objects for a given elb arn.
|
|
|
|
|
|
|
|
:param kwargs:
|
|
|
|
:return:
|
|
|
|
"""
|
2019-04-26 19:16:18 +02:00
|
|
|
try:
|
2019-05-16 16:57:02 +02:00
|
|
|
client = kwargs.pop("client")
|
2019-04-26 19:16:18 +02:00
|
|
|
return client.describe_listeners(**kwargs)
|
|
|
|
except Exception as e: # noqa
|
2019-05-16 16:57:02 +02:00
|
|
|
metrics.send(
|
2019-07-01 17:35:04 +02:00
|
|
|
"describe_listeners_v2_error", "counter", 1, metric_tags={"error": str(e)}
|
2019-05-16 16:57:02 +02:00
|
|
|
)
|
2019-04-26 19:16:18 +02:00
|
|
|
sentry.captureException()
|
|
|
|
raise
|
2016-12-21 17:23:14 +01:00
|
|
|
|
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
@sts_client("elb")
|
2019-04-25 22:50:41 +02:00
|
|
|
@retry(retry_on_exception=retry_throttled, wait_fixed=2000, stop_max_attempt_number=20)
|
2016-06-27 23:40:46 +02:00
|
|
|
def describe_load_balancer_policies(load_balancer_name, policy_names, **kwargs):
|
2015-06-22 22:47:27 +02:00
|
|
|
"""
|
2016-06-27 23:40:46 +02:00
|
|
|
Fetching all policies currently associated with an ELB.
|
2015-06-22 22:47:27 +02:00
|
|
|
|
2016-06-27 23:40:46 +02:00
|
|
|
:param load_balancer_name:
|
2015-06-22 22:47:27 +02:00
|
|
|
:return:
|
|
|
|
"""
|
2019-04-26 19:16:18 +02:00
|
|
|
|
2019-04-25 22:50:41 +02:00
|
|
|
try:
|
2019-05-16 16:57:02 +02:00
|
|
|
return kwargs["client"].describe_load_balancer_policies(
|
|
|
|
LoadBalancerName=load_balancer_name, PolicyNames=policy_names
|
|
|
|
)
|
2019-04-25 22:50:41 +02:00
|
|
|
except Exception as e: # noqa
|
2019-05-16 16:57:02 +02:00
|
|
|
metrics.send(
|
|
|
|
"describe_load_balancer_policies_error",
|
|
|
|
"counter",
|
|
|
|
1,
|
|
|
|
metric_tags={
|
|
|
|
"load_balancer_name": load_balancer_name,
|
|
|
|
"policy_names": policy_names,
|
2019-07-01 17:35:04 +02:00
|
|
|
"error": str(e),
|
2019-05-16 16:57:02 +02:00
|
|
|
},
|
|
|
|
)
|
|
|
|
sentry.captureException(
|
|
|
|
extra={
|
|
|
|
"load_balancer_name": str(load_balancer_name),
|
|
|
|
"policy_names": str(policy_names),
|
|
|
|
}
|
|
|
|
)
|
2019-04-25 22:50:41 +02:00
|
|
|
raise
|
2015-06-22 22:47:27 +02:00
|
|
|
|
2015-07-21 22:06:13 +02:00
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
@sts_client("elbv2")
|
2019-04-25 22:50:41 +02:00
|
|
|
@retry(retry_on_exception=retry_throttled, wait_fixed=2000, stop_max_attempt_number=20)
|
2016-12-21 17:23:14 +01:00
|
|
|
def describe_ssl_policies_v2(policy_names, **kwargs):
|
|
|
|
"""
|
|
|
|
Fetching all policies currently associated with an ELB.
|
|
|
|
|
|
|
|
:param policy_names:
|
|
|
|
:return:
|
|
|
|
"""
|
2019-04-25 22:50:41 +02:00
|
|
|
try:
|
2019-05-16 16:57:02 +02:00
|
|
|
return kwargs["client"].describe_ssl_policies(Names=policy_names)
|
2019-04-25 22:50:41 +02:00
|
|
|
except Exception as e: # noqa
|
2019-05-16 16:57:02 +02:00
|
|
|
metrics.send(
|
|
|
|
"describe_ssl_policies_v2_error",
|
|
|
|
"counter",
|
|
|
|
1,
|
2019-07-01 17:35:04 +02:00
|
|
|
metric_tags={"policy_names": policy_names, "error": str(e)},
|
2019-05-16 16:57:02 +02:00
|
|
|
)
|
2019-04-26 19:18:54 +02:00
|
|
|
sentry.captureException(extra={"policy_names": str(policy_names)})
|
2019-04-25 22:50:41 +02:00
|
|
|
raise
|
2016-12-21 17:23:14 +01:00
|
|
|
|
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
@sts_client("elb")
|
2019-04-26 19:16:18 +02:00
|
|
|
@retry(retry_on_exception=retry_throttled, wait_fixed=2000, stop_max_attempt_number=20)
|
2016-06-27 23:40:46 +02:00
|
|
|
def describe_load_balancer_types(policies, **kwargs):
|
2015-06-22 22:47:27 +02:00
|
|
|
"""
|
2016-06-27 23:40:46 +02:00
|
|
|
Describe the policies with policy details.
|
2015-06-22 22:47:27 +02:00
|
|
|
|
2016-06-27 23:40:46 +02:00
|
|
|
:param policies:
|
|
|
|
:return:
|
2015-06-22 22:47:27 +02:00
|
|
|
"""
|
2019-05-16 16:57:02 +02:00
|
|
|
return kwargs["client"].describe_load_balancer_policy_types(
|
|
|
|
PolicyTypeNames=policies
|
|
|
|
)
|
2015-06-22 22:47:27 +02:00
|
|
|
|
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
@sts_client("elb")
|
2019-04-26 19:16:18 +02:00
|
|
|
@retry(retry_on_exception=retry_throttled, wait_fixed=2000, stop_max_attempt_number=20)
|
2016-12-01 00:11:17 +01:00
|
|
|
def attach_certificate(name, port, certificate_id, **kwargs):
|
2015-06-22 22:47:27 +02:00
|
|
|
"""
|
|
|
|
Attaches a certificate to a listener, throws exception
|
|
|
|
if certificate specified does not exist in a particular account.
|
|
|
|
|
|
|
|
:param name:
|
|
|
|
:param port:
|
|
|
|
:param certificate_id:
|
|
|
|
"""
|
2016-12-06 00:12:55 +01:00
|
|
|
try:
|
2019-05-16 16:57:02 +02:00
|
|
|
return kwargs["client"].set_load_balancer_listener_ssl_certificate(
|
|
|
|
LoadBalancerName=name,
|
|
|
|
LoadBalancerPort=port,
|
|
|
|
SSLCertificateId=certificate_id,
|
|
|
|
)
|
2016-12-06 00:12:55 +01:00
|
|
|
except botocore.exceptions.ClientError as e:
|
2019-05-16 16:57:02 +02:00
|
|
|
if e.response["Error"]["Code"] == "LoadBalancerNotFound":
|
2016-12-06 00:12:55 +01:00
|
|
|
current_app.logger.warning("Loadbalancer does not exist.")
|
|
|
|
else:
|
|
|
|
raise e
|
2016-12-21 17:23:14 +01:00
|
|
|
|
|
|
|
|
2019-05-16 16:57:02 +02:00
|
|
|
@sts_client("elbv2")
|
2019-04-26 19:16:18 +02:00
|
|
|
@retry(retry_on_exception=retry_throttled, wait_fixed=2000, stop_max_attempt_number=20)
|
2016-12-21 17:23:14 +01:00
|
|
|
def attach_certificate_v2(listener_arn, port, certificates, **kwargs):
|
|
|
|
"""
|
|
|
|
Attaches a certificate to a listener, throws exception
|
|
|
|
if certificate specified does not exist in a particular account.
|
|
|
|
|
|
|
|
:param listener_arn:
|
|
|
|
:param port:
|
|
|
|
:param certificates:
|
|
|
|
"""
|
|
|
|
try:
|
2019-05-16 16:57:02 +02:00
|
|
|
return kwargs["client"].modify_listener(
|
|
|
|
ListenerArn=listener_arn, Port=port, Certificates=certificates
|
|
|
|
)
|
2016-12-21 17:23:14 +01:00
|
|
|
except botocore.exceptions.ClientError as e:
|
2019-05-16 16:57:02 +02:00
|
|
|
if e.response["Error"]["Code"] == "LoadBalancerNotFound":
|
2016-12-21 17:23:14 +01:00
|
|
|
current_app.logger.warning("Loadbalancer does not exist.")
|
|
|
|
else:
|
|
|
|
raise e
|