/
boto_elb.py
1022 lines (942 loc) · 38.6 KB
/
boto_elb.py
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
'''
Manage ELBs
.. versionadded:: 2014.7.0
Create and destroy ELBs. Be aware that this interacts with Amazon's
services, and so may incur charges.
This module uses ``boto``, which can be installed via package, or pip.
This module accepts explicit elb credentials but can also utilize
IAM roles assigned to the instance through Instance Profiles. Dynamic
credentials are then automatically obtained from AWS API and no further
configuration is necessary. More information available `here
<http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html>`_.
If IAM roles are not used you need to specify them either in a pillar file or
in the minion's config file:
.. code-block:: yaml
elb.keyid: GKTADJGHEIQSXMKKRBJ08H
elb.key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
It's also possible to specify ``key``, ``keyid`` and ``region`` via a profile, either
passed in as a dict, or as a string to pull from pillars or minion config:
.. code-block:: yaml
myprofile:
keyid: GKTADJGHEIQSXMKKRBJ08H
key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
region: us-east-1
.. code-block:: yaml
Ensure myelb ELB exists:
boto_elb.present:
- name: myelb
- region: us-east-1
- availability_zones:
- us-east-1a
- us-east-1c
- us-east-1d
- keyid: GKTADJGHEIQSXMKKRBJ08H
- key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
- listeners:
- elb_port: 443
instance_port: 80
elb_protocol: HTTPS
instance_protocol: HTTP
certificate: 'arn:aws:iam::1111111:server-certificate/mycert'
- elb_port: 8210
instance_port: 8210
elb_protocol: TCP
- health_check:
target: 'HTTP:80/'
- attributes:
cross_zone_load_balancing:
enabled: true
access_log:
enabled: true
s3_bucket_name: 'mybucket'
s3_bucket_prefix: 'my-logs'
emit_interval: 5
- cnames:
- name: mycname.example.com.
zone: example.com.
ttl: 60
- name: myothercname.example.com.
zone: example.com.
# Using a profile from pillars
Ensure myelb ELB exists:
boto_elb.present:
- name: myelb
- region: us-east-1
- profile: myelbprofile
# Passing in a profile
Ensure myelb ELB exists:
boto_elb.present:
- name: myelb
- region: us-east-1
- profile:
keyid: GKTADJGHEIQSXMKKRBJ08H
key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
It's possible to specify attributes from pillars by specifying a pillar. You
can override the values defined in the pillard by setting the attributes on the
resource. The module will use the default pillar key 'boto_elb_attributes',
which allows you to set default attributes for all ELB resources.
Setting the attributes pillar:
.. code-block:: yaml
my_elb_attributes:
cross_zone_load_balancing:
enabled: true
connection_draining:
enabled: true
timeout: 20
access_log:
enabled: true
s3_bucket_name: 'mybucket'
s3_bucket_prefix: 'my-logs'
emit_interval: 5
Overriding the attribute values on the resource:
.. code-block:: yaml
Ensure myelb ELB exists:
boto_elb.present:
- name: myelb
- region: us-east-1
- attributes_from_pillar: my_elb_attributes
# override cross_zone_load_balancing:enabled
- attributes:
cross_zone_load_balancing:
enabled: false
- profile: myelbprofile
It's possible to specify cloudwatch alarms that will be setup along with the
ELB. Note the alarm name will be defined by the name attribute provided, plus
the ELB resource name.
.. code-block:: yaml
Ensure myelb ELB exists:
boto_elb.present:
- name: myelb
- region: us-east-1
- profile: myelbprofile
- alarms:
UnHealthyHostCount:
name: 'ELB UnHealthyHostCount **MANAGED BY SALT**'
attributes:
metric: UnHealthyHostCount
namespace: AWS/ELB
statistic: Average
comparison: '>='
threshold: 1.0
period: 600
evaluation_periods: 6
unit: null
description: ELB UnHealthyHostCount
alarm_actions: ['arn:aws:sns:us-east-1:12345:myalarm']
insufficient_data_actions: []
ok_actions: ['arn:aws:sns:us-east-1:12345:myalarm']
You can also use alarms from pillars, and override values from the pillar
alarms by setting overrides on the resource. Note that 'boto_elb_alarms'
will be used as a default value for all resources, if defined and can be
used to ensure alarms are always set for a resource.
Setting the alarms in a pillar:
.. code-block:: yaml
my_elb_alarm:
UnHealthyHostCount:
name: 'ELB UnHealthyHostCount **MANAGED BY SALT**'
attributes:
metric: UnHealthyHostCount
namespace: AWS/ELB
statistic: Average
comparison: '>='
threshold: 1.0
period: 600
evaluation_periods: 6
unit: null
description: ELB UnHealthyHostCount
alarm_actions: ['arn:aws:sns:us-east-1:12345:myalarm']
insufficient_data_actions: []
ok_actions: ['arn:aws:sns:us-east-1:12345:myalarm']
Overriding the alarm values on the resource:
.. code-block:: yaml
Ensure myelb ELB exists:
boto_elb.present:
- name: myelb
- region: us-east-1
- profile: myelbprofile
- alarms_from_pillar: my_elb_alarm
# override UnHealthyHostCount:attributes:threshold
- alarms:
UnHealthyHostCount:
attributes:
threshold: 2.0
'''
from __future__ import absolute_import
import salt.utils.dictupdate as dictupdate
from salt.exceptions import SaltInvocationError
import salt.ext.six as six
def __virtual__():
'''
Only load if boto is available.
'''
return 'boto_elb' if 'boto_elb.exists' in __salt__ else False
def present(
name,
listeners,
availability_zones=None,
subnets=None,
security_groups=None,
scheme='internet-facing',
health_check=None,
attributes=None,
attributes_from_pillar="boto_elb_attributes",
cnames=None,
alarms=None,
alarms_from_pillar="boto_elb_alarms",
region=None,
key=None,
keyid=None,
profile=None,
wait_for_sync=True):
'''
Ensure the IAM role exists.
name
Name of the IAM role.
availability_zones
A list of availability zones for this ELB.
listeners
A list of listener lists; example:
[
['443', 'HTTPS', 'arn:aws:iam::1111111:server-certificate/mycert'],
['8443', '80', 'HTTPS', 'HTTP', 'arn:aws:iam::1111111:server-certificate/mycert']
]
subnets
A list of subnet IDs in your VPC to attach to your LoadBalancer.
security_groups
The security groups assigned to your LoadBalancer within your VPC.
scheme
The type of a LoadBalancer. internet-facing or internal. Once set, can not be modified.
health_check
A dict defining the health check for this ELB.
attributes
A dict defining the attributes to set on this ELB.
attributes_from_pillar
name of pillar dict that contains attributes. Attributes defined for this specific
state will override those from pillar.
cnames
A list of cname dicts with attributes: name, zone, ttl, and identifier.
See the boto_route53 state for information about these attributes.
alarms:
a dictionary of name->boto_cloudwatch_alarm sections to be associated with this ELB.
All attributes should be specified except for dimension which will be
automatically set to this ELB.
See the boto_cloudwatch_alarm state for information about these attributes.
alarms_from_pillar:
name of pillar dict that contains alarm settings. Alarms defined for this specific
state will override those from pillar.
region
Region to connect to.
key
Secret key to be used.
keyid
Access key to be used.
profile
A dict with region, key and keyid, or a pillar key (string)
that contains a dict with region, key and keyid.
wait_for_sync
Wait for an INSYNC change status from Route53.
'''
# load data from attributes_from_pillar and merge with attributes
tmp = __salt__['config.option'](attributes_from_pillar, {})
if attributes:
attributes = dictupdate.update(tmp, attributes)
else:
attributes = tmp
ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
_ret = _elb_present(name, availability_zones, listeners, subnets,
security_groups, scheme, region, key, keyid, profile)
ret['changes'] = _ret['changes']
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _attributes_present(name, attributes, region, key, keyid, profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _health_check_present(name, health_check, region, key, keyid,
profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _cnames_present(name, cnames, region, key, keyid, profile,
wait_for_sync)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
return ret
def register_instances(name, instances, region=None, key=None, keyid=None,
profile=None):
'''
Add instance/s to load balancer
.. versionsadded:: Beryllium
.. code-block:: yaml
add-instances:
boto_elb.register_instances:
- name: myloadbalancer
- instances:
- instance-id1
- instance-id2
'''
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
ret['name'] = name
lb = __salt__['boto_elb.exists'](name, region, key, keyid, profile)
if lb:
health = __salt__['boto_elb.get_instance_health'](name,
region,
key,
keyid,
profile)
nodes = []
new = []
for value in health:
nodes.append(value['instance_id'])
for value in instances:
if value not in nodes:
new.append(value)
if len(new) == 0:
ret['comment'] = 'Instance/s {0} already exist.' \
''.format(str(instances).strip('[]'))
ret['result'] = True
else:
state = __salt__['boto_elb.register_instances'](name,
instances,
region,
key,
keyid,
profile)
if state:
ret['comment'] = 'Load Balancer {0} has been changed' \
''.format(name)
ret['changes']['old'] = '\n'.join(nodes)
new = set().union(nodes, instances)
ret['changes']['new'] = '\n'.join(list(new))
ret['result'] = True
else:
ret['comment'] = 'Load balancer {0} failed to add instances' \
''.format(name)
ret['result'] = False
else:
ret['comment'] = 'Could not find lb {0}'.format(name)
return ret
def _elb_present(
name,
availability_zones,
listeners,
subnets,
security_groups,
scheme,
region,
key,
keyid,
profile):
ret = {'result': True, 'comment': '', 'changes': {}}
if not (availability_zones or subnets):
raise SaltInvocationError('Either availability_zones or subnets must'
' be provided as arguments.')
if availability_zones and subnets:
raise SaltInvocationError('availability_zones and subnets are mutually'
' exclusive arguments.')
if not listeners:
listeners = []
_listeners = []
for listener in listeners:
if len(listener) < 3:
raise SaltInvocationError('Listeners must have at minimum port,'
' instance_port and protocol values in'
' the provided list.')
if 'elb_port' not in listener:
raise SaltInvocationError('elb_port is a required value for'
' listeners.')
if 'instance_port' not in listener:
raise SaltInvocationError('instance_port is a required value for'
' listeners.')
if 'elb_protocol' not in listener:
raise SaltInvocationError('elb_protocol is a required value for'
' listeners.')
listener['elb_protocol'] = listener['elb_protocol'].upper()
if listener['elb_protocol'] == 'HTTPS' and 'certificate' not in listener:
raise SaltInvocationError('certificate is a required value for'
' listeners if HTTPS is set for'
' elb_protocol.')
# We define all listeners as complex listeners.
if 'instance_protocol' not in listener:
listener['instance_protocol'] = listener['elb_protocol'].upper()
else:
listener['instance_protocol'] = listener['instance_protocol'].upper()
_listener = [listener['elb_port'], listener['instance_port'],
listener['elb_protocol'], listener['instance_protocol']]
if 'certificate' in listener:
_listener.append(listener['certificate'])
_listeners.append(_listener)
if subnets:
vpc_id = __salt__['boto_vpc.get_subnet_association'](
subnets, region, key, keyid, profile
)
if not vpc_id:
msg = 'Subnets {0} do not map to a valid vpc id.'.format(subnets)
raise SaltInvocationError(msg)
security_groups = __salt__['boto_secgroup.convert_to_group_ids'](
security_groups, vpc_id, region, key, keyid, profile
)
if not security_groups:
msg = 'Security groups {0} do not map to valid security group ids.'
msg = msg.format(security_groups)
raise SaltInvocationError(msg)
exists = __salt__['boto_elb.exists'](name, region, key, keyid, profile)
if not exists:
if __opts__['test']:
ret['comment'] = 'ELB {0} is set to be created.'.format(name)
ret['result'] = None
return ret
created = __salt__['boto_elb.create'](name, availability_zones,
_listeners, subnets,
security_groups, scheme, region,
key, keyid, profile)
if created:
ret['changes']['old'] = {'elb': None}
ret['changes']['new'] = {'elb': name}
ret['comment'] = 'ELB {0} created.'.format(name)
else:
ret['result'] = False
ret['comment'] = 'Failed to create {0} ELB.'.format(name)
else:
ret['comment'] = 'ELB {0} present.'.format(name)
_ret = _security_groups_present(
name, security_groups, region, key, keyid, profile
)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _listeners_present(name, _listeners, region, key, keyid,
profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
if availability_zones:
_ret = _zones_present(name, availability_zones, region, key, keyid,
profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
elif subnets:
_ret = _subnets_present(name, subnets, region, key, keyid,
profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
return ret
def _listeners_present(
name,
listeners,
region,
key,
keyid,
profile):
ret = {'result': True, 'comment': '', 'changes': {}}
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
if not lb:
msg = '{0} ELB configuration could not be retrieved.'.format(name)
ret['comment'] = msg
ret['result'] = False
return ret
if not listeners:
listeners = []
to_delete = []
to_create = []
for listener in listeners:
if listener not in lb['listeners']:
to_create.append(listener)
for listener in lb['listeners']:
if listener not in listeners:
to_delete.append(listener[0])
if to_create or to_delete:
if __opts__['test']:
msg = 'ELB {0} set to have listeners modified.'.format(name)
ret['comment'] = msg
ret['result'] = None
return ret
if to_delete:
deleted = __salt__['boto_elb.delete_listeners'](name, to_delete,
region, key, keyid,
profile)
if deleted:
ret['comment'] = 'Deleted listeners on {0} ELB.'.format(name)
else:
msg = 'Failed to delete listeners on {0} ELB.'.format(name)
ret['comment'] = msg
ret['result'] = False
if to_create:
created = __salt__['boto_elb.create_listeners'](name, to_create,
region, key, keyid,
profile)
if created:
msg = 'Created listeners on {0} ELB.'
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
else:
msg = 'Failed to create listeners on {0} ELB.'
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
ret['result'] = False
ret['changes']['old'] = {'listeners': lb['listeners']}
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid,
profile)
ret['changes']['new'] = {'listeners': lb['listeners']}
return ret
def _security_groups_present(
name,
security_groups,
region,
key,
keyid,
profile):
ret = {'result': True, 'comment': '', 'changes': {}}
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
if not lb:
msg = '{0} ELB configuration could not be retrieved.'.format(name)
ret['comment'] = msg
ret['result'] = False
return ret
if not security_groups:
security_groups = []
change_needed = False
if set(security_groups) != set(lb['security_groups']):
change_needed = True
if change_needed:
if __opts__['test']:
msg = 'ELB {0} set to have security groups modified.'.format(name)
ret['comment'] = msg
ret['result'] = None
return ret
changed = __salt__['boto_elb.apply_security_groups'](
name, security_groups, region, key, keyid, profile
)
if changed:
msg = 'Modified security_groups on {0} ELB.'.format(name)
ret['comment'] = msg
else:
msg = 'Failed to modify security_groups on {0} ELB.'.format(name)
ret['comment'] = msg
ret['result'] = False
ret['changes']['old'] = {'security_groups': lb['security_groups']}
ret['changes']['new'] = {'security_groups': security_groups}
else:
ret['comment'] = 'security_groups already set on ELB {0}.'.format(name)
return ret
def _attributes_present(
name,
attributes,
region,
key,
keyid,
profile):
ret = {'result': True, 'comment': '', 'changes': {}}
_attributes = __salt__['boto_elb.get_attributes'](name, region, key, keyid,
profile)
if not _attributes:
if not __opts__['test']:
ret['result'] = False
msg = 'Failed to retrieve attributes for ELB {0}.'.format(name)
ret['comment'] = msg
return ret
attrs_to_set = []
if 'cross_zone_load_balancing' in attributes:
czlb = attributes['cross_zone_load_balancing']
_czlb = _attributes['cross_zone_load_balancing']
if czlb['enabled'] != _czlb['enabled']:
attrs_to_set.append('cross_zone_load_balancing')
if 'connection_draining' in attributes:
cd = attributes['connection_draining']
_cd = _attributes['connection_draining']
if (cd['enabled'] != _cd['enabled']
or cd.get('timeout', 300) != _cd.get('timeout')):
attrs_to_set.append('connection_draining')
if 'access_log' in attributes:
for attr, val in six.iteritems(attributes['access_log']):
if str(_attributes['access_log'][attr]) != str(val):
attrs_to_set.append('access_log')
if 's3_bucket_prefix' in attributes['access_log']:
sbp = attributes['access_log']['s3_bucket_prefix']
if sbp.startswith('/') or sbp.endswith('/'):
raise SaltInvocationError('s3_bucket_prefix can not start or'
' end with /.')
if attrs_to_set:
if __opts__['test']:
ret['comment'] = 'ELB {0} set to have attributes set.'.format(name)
ret['result'] = None
return ret
was_set = __salt__['boto_elb.set_attributes'](name, attributes,
region, key, keyid,
profile)
if was_set:
ret['changes']['old'] = {'attributes': _attributes}
ret['changes']['new'] = {'attributes': attributes}
ret['comment'] = 'Set attributes on ELB {0}.'.format(name)
else:
ret['result'] = False
msg = 'Failed to set attributes on ELB {0}.'.format(name)
ret['comment'] = msg
else:
ret['comment'] = 'Attributes already set on ELB {0}.'.format(name)
return ret
def _health_check_present(
name,
health_check,
region,
key,
keyid,
profile):
ret = {'result': True, 'comment': '', 'changes': {}}
if not health_check:
health_check = {}
_health_check = __salt__['boto_elb.get_health_check'](name, region, key,
keyid, profile)
if not _health_check:
if not __opts__['test']:
ret['result'] = False
msg = 'Failed to retrieve health_check for ELB {0}.'.format(name)
ret['comment'] = msg
return ret
need_to_set = False
for attr, val in six.iteritems(health_check):
if str(_health_check[attr]) != str(val):
need_to_set = True
if need_to_set:
if __opts__['test']:
msg = 'ELB {0} set to have health check set.'.format(name)
ret['result'] = True
ret['comment'] = msg
return ret
was_set = __salt__['boto_elb.set_health_check'](name, health_check,
region, key, keyid,
profile)
if was_set:
ret['changes']['old'] = {'health_check': _health_check}
_health_check = __salt__['boto_elb.get_health_check'](name, region,
key, keyid,
profile)
ret['changes']['new'] = {'health_check': _health_check}
ret['comment'] = 'Set health check on ELB {0}.'.format(name)
else:
ret['result'] = False
msg = 'Failed to set health check on ELB {0}.'.format(name)
ret['comment'] = msg
else:
ret['comment'] = 'Health check already set on ELB {0}.'.format(name)
return ret
def _zones_present(
name,
availability_zones,
region,
key,
keyid,
profile):
ret = {'result': True, 'comment': '', 'changes': {}}
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
if not lb:
if not __opts__['test']:
ret['result'] = False
msg = 'Failed to retrieve ELB {0}.'.format(name)
ret['comment'] = msg
return ret
to_enable = []
to_disable = []
_zones = lb['availability_zones']
for zone in availability_zones:
if zone not in _zones:
to_enable.append(zone)
for zone in _zones:
if zone not in availability_zones:
to_disable.append(zone)
if to_enable or to_disable:
if __opts__['test']:
msg = 'ELB {0} to have availability zones set.'.format(name)
ret['comment'] = msg
ret['result'] = None
return ret
if to_enable:
enabled = __salt__['boto_elb.enable_availability_zones'](name,
to_enable,
region,
key,
keyid,
profile)
if enabled:
msg = 'Enabled availability zones on {0} ELB.'.format(name)
ret['comment'] = msg
else:
msg = 'Failed to enable availability zones on {0} ELB.'
ret['comment'] = msg.format(name)
ret['result'] = False
if to_disable:
disabled = __salt__['boto_elb.disable_availability_zones'](name,
to_disable,
region,
key,
keyid,
profile)
if disabled:
msg = 'Disabled availability zones on {0} ELB.'
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
else:
msg = 'Failed to disable availability zones on {0} ELB.'
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
ret['result'] = False
ret['changes']['old'] = {'availability_zones':
lb['availability_zones']}
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid,
profile)
ret['changes']['new'] = {'availability_zones':
lb['availability_zones']}
else:
msg = 'Availability zones already set on ELB {0}.'.format(name)
ret['comment'] = msg
return ret
def _subnets_present(
name,
subnets,
region,
key,
keyid,
profile):
ret = {'result': True, 'comment': '', 'changes': {}}
if not subnets:
subnets = []
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
if not lb:
if not __opts__['test']:
ret['result'] = False
msg = 'Failed to retrieve ELB {0}.'.format(name)
ret['comment'] = msg
return ret
to_enable = []
to_disable = []
_subnets = lb['subnets']
for subnet in subnets:
if subnet not in _subnets:
to_enable.append(subnet)
for subnet in _subnets:
if subnet not in subnets:
to_disable.append(subnet)
if to_enable or to_disable:
if __opts__['test']:
msg = 'ELB {0} to have subnets set.'.format(name)
ret['comment'] = msg
ret['result'] = None
return ret
if to_enable:
attached = __salt__['boto_elb.attach_subnets'](name, to_enable,
region, key, keyid,
profile)
if attached:
msg = 'Attached subnets on {0} ELB.'.format(name)
ret['comment'] = msg
else:
msg = 'Failed to attach subnets on {0} ELB.'
ret['comment'] = msg.format(name)
ret['result'] = False
if to_disable:
detached = __salt__['boto_elb.detach_subnets'](name, to_disable,
region, key, keyid,
profile)
if detached:
msg = 'Detached subnets on {0} ELB.'
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
else:
msg = 'Failed to detach subnets on {0} ELB.'
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
ret['result'] = False
ret['changes']['old'] = {'subnets': lb['subnets']}
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid,
profile)
ret['changes']['new'] = {'subnets': lb['subnets']}
else:
msg = 'Subnets already set on ELB {0}.'.format(name)
ret['comment'] = msg
return ret
def _cnames_present(
name,
cnames,
region,
key,
keyid,
profile,
wait_for_sync):
ret = {'result': True, 'comment': '', 'changes': {}}
if not cnames:
cnames = []
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
if not lb:
if not __opts__['test']:
ret['result'] = False
msg = 'Failed to retrieve ELB {0}.'.format(name)
ret['comment'] = msg
return ret
to_create = []
to_update = []
for cname in cnames:
_name = cname.get('name', None)
_zone = cname.get('zone', None)
if not _name or not _zone:
raise SaltInvocationError('cnames must provide name and zone'
' attributes.')
record = __salt__['boto_route53.get_record'](_name, _zone, 'CNAME',
False, region, key,
keyid, profile)
if not record:
to_create.append(cname)
elif record['value'].rstrip('.') != lb['dns_name'].rstrip('.'):
to_update.append(cname)
if to_create or to_update:
if __opts__['test']:
msg = 'ELB {0} to have cnames modified.'.format(name)
ret['comment'] = msg
ret['result'] = None
return ret
if to_create:
created = []
not_created = []
for cname in to_create:
_name = cname.get('name')
_zone = cname.get('zone')
_iden = cname.get('identifier', None)
_ttl = cname.get('ttl', None)
_created = __salt__['boto_route53.add_record'](
_name, lb['dns_name'], _zone, 'CNAME', _iden, _ttl, region,
key, keyid, profile)
if _created:
created.append(_name)
else:
not_created.append(_name)
if created:
msg = 'Created cnames {0}.'.format(','.join(created))
ret['comment'] = msg
if not_created:
msg = 'Failed to create cnames {0}.'
msg = msg.format(','.join(not_created))
if 'comment' in ret:
ret['comment'] = ret['comment'] + ' ' + msg
else:
ret['comment'] = msg
ret['result'] = False
if to_update:
updated = []
not_updated = []
for cname in to_update:
_name = cname.get('name')
_zone = cname.get('zone')
_iden = cname.get('identifier', None)
_ttl = cname.get('ttl', None)
_updated = __salt__['boto_route53.update_record'](
_name, lb['dns_name'], _zone, 'CNAME', _iden, _ttl, region,
key, keyid, profile)
if _updated:
updated.append(_name)
else:
not_updated.append(_name)
if updated:
msg = 'Updated cnames {0}.'.format(','.join(updated))
if 'comment' in ret:
ret['comment'] = ret['comment'] + ' ' + msg
else:
ret['comment'] = msg
if not_updated:
msg = 'Failed to update cnames {0}.'
msg = msg.format(','.join(not_updated))
if 'comment' in ret:
ret['comment'] = ret['comment'] + ' ' + msg
else:
ret['comment'] = msg
ret['result'] = False
# We can't track old, since we'd need to know the zone to
# search for the ELB in the value.
ret['changes']['new'] = {'cnames': to_create + to_update}
else:
msg = 'cnames already set on ELB {0}.'.format(name)
ret['comment'] = msg
return ret
def _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profile):
'''helper method for present. ensure that cloudwatch_alarms are set'''
# load data from alarms_from_pillar
tmp = __salt__['config.option'](alarms_from_pillar, {})
# merge with data from alarms
if alarms:
tmp = dictupdate.update(tmp, alarms)
# set alarms, using boto_cloudwatch_alarm.present
merged_return_value = {'name': name, 'result': True, 'comment': '', 'changes': {}}
for _, info in six.iteritems(tmp):
# add elb to name and description
info["name"] = name + " " + info["name"]
info["attributes"]["description"] = name + " " + info["attributes"]["description"]
# add dimension attribute
info["attributes"]["dimensions"] = {"LoadBalancerName": [name]}
# set alarm
kwargs = {
"name": info["name"],
"attributes": info["attributes"],
"region": region,
"key": key,
"keyid": keyid,
"profile": profile,
}
ret = __salt__["state.single"]('boto_cloudwatch_alarm.present', **kwargs)
results = next(six.itervalues(ret))
if not results["result"]:
merged_return_value["result"] = False
if results.get("changes", {}) != {}:
merged_return_value["changes"][info["name"]] = results["changes"]
if "comment" in results:
merged_return_value["comment"] += results["comment"]
return merged_return_value
def absent(
name,
region=None,
key=None,