From fb8ef9f0f033f5794b263d7ff4651c602e6979dd Mon Sep 17 00:00:00 2001 From: wangyue Date: Thu, 19 Jan 2017 22:08:56 +0800 Subject: [PATCH] update alicloud provider (#11235) --- builtin/providers/alicloud/common.go | 52 + builtin/providers/alicloud/config.go | 103 ++ .../alicloud/data_source_alicloud_common.go | 18 + .../alicloud/data_source_alicloud_images.go | 324 +++++ .../data_source_alicloud_images_test.go | 130 ++ .../data_source_alicloud_instance_types.go | 127 ++ ...ata_source_alicloud_instance_types_test.go | 56 + .../alicloud/data_source_alicloud_regions.go | 114 ++ .../data_source_alicloud_regions_test.go | 114 ++ .../alicloud/data_source_alicloud_zones.go | 137 ++ .../data_source_alicloud_zones_test.go | 70 ++ builtin/providers/alicloud/errors.go | 30 + builtin/providers/alicloud/extension_ecs.go | 32 + .../alicloud/extension_nat_gateway.go | 194 +++ builtin/providers/alicloud/extension_slb.go | 71 ++ builtin/providers/alicloud/extension_tags.go | 43 + builtin/providers/alicloud/provider.go | 88 ++ builtin/providers/alicloud/provider_test.go | 59 + .../alicloud/resource_alicloud_disk.go | 247 ++++ .../resource_alicloud_disk_attachment.go | 176 +++ .../resource_alicloud_disk_attachment_test.go | 154 +++ .../alicloud/resource_alicloud_disk_test.go | 157 +++ .../alicloud/resource_alicloud_eip.go | 156 +++ .../resource_alicloud_eip_association.go | 131 ++ .../resource_alicloud_eip_association_test.go | 150 +++ .../alicloud/resource_alicloud_eip_test.go | 131 ++ .../alicloud/resource_alicloud_instance.go | 534 ++++++++ .../resource_alicloud_instance_test.go | 1106 +++++++++++++++++ .../alicloud/resource_alicloud_nat_gateway.go | 282 +++++ .../resource_alicloud_nat_gateway_test.go | 241 ++++ .../resource_alicloud_security_group.go | 169 +++ .../resource_alicloud_security_group_rule.go | 285 +++++ ...ource_alicloud_security_group_rule_test.go | 263 ++++ .../resource_alicloud_security_group_test.go | 151 +++ .../alicloud/resource_alicloud_slb.go | 420 +++++++ .../resource_alicloud_slb_attachment.go | 144 +++ .../resource_alicloud_slb_attachment_test.go | 110 ++ .../alicloud/resource_alicloud_slb_test.go | 294 +++++ .../alicloud/resource_alicloud_vpc.go | 190 +++ .../alicloud/resource_alicloud_vpc_test.go | 140 +++ .../resource_alicloud_vroute_entry.go | 145 +++ .../resource_alicloud_vroute_entry_test.go | 183 +++ .../alicloud/resource_alicloud_vswitch.go | 220 ++++ .../resource_alicloud_vswitch_test.go | 105 ++ .../alicloud/service_alicloud_ecs.go | 208 ++++ .../alicloud/service_alicloud_slb.go | 21 + .../alicloud/service_alicloud_vpc.go | 134 ++ .../alicloud/struct_security_groups.go | 11 + builtin/providers/alicloud/tags.go | 126 ++ builtin/providers/alicloud/validators.go | 453 +++++++ builtin/providers/alicloud/validators_test.go | 429 +++++++ examples/alicloud-ecs-image/README.md | 27 + examples/alicloud-ecs-image/main.tf | 55 + examples/alicloud-ecs-image/outputs.tf | 15 + examples/alicloud-ecs-image/variables.tf | 57 + examples/alicloud-ecs-slb/README.md | 18 + examples/alicloud-ecs-slb/main.tf | 53 + examples/alicloud-ecs-slb/outputs.tf | 20 + examples/alicloud-ecs-slb/variables.tf | 58 + examples/alicloud-ecs-special-sg/README.md | 20 + examples/alicloud-ecs-special-sg/main.tf | 39 + examples/alicloud-ecs-special-sg/outputs.tf | 7 + examples/alicloud-ecs-special-sg/variables.tf | 17 + examples/alicloud-ecs-userdata/README.md | 17 + examples/alicloud-ecs-userdata/main.tf | 37 + examples/alicloud-ecs-userdata/outputs.tf | 7 + examples/alicloud-ecs-userdata/userdata.sh | 6 + examples/alicloud-ecs-userdata/variables.tf | 19 + examples/alicloud-ecs-vpc-cluster/README.md | 19 + examples/alicloud-ecs-vpc-cluster/main.tf | 62 + .../alicloud-ecs-vpc-cluster/variables.tf | 59 + examples/alicloud-ecs-vpc/README.md | 31 + examples/alicloud-ecs-vpc/main.tf | 45 + examples/alicloud-ecs-vpc/outputs.tf | 7 + examples/alicloud-ecs-vpc/variables.tf | 67 + examples/alicloud-ecs-zone-type/README.md | 19 + examples/alicloud-ecs-zone-type/main.tf | 42 + examples/alicloud-ecs-zone-type/outputs.tf | 15 + examples/alicloud-ecs-zone-type/variables.tf | 35 + examples/alicloud-ecs/README.md | 27 + examples/alicloud-ecs/main.tf | 49 + examples/alicloud-ecs/outputs.tf | 15 + examples/alicloud-ecs/variables.tf | 51 + examples/alicloud-security-group-rule/main.tf | 14 + .../alicloud-security-group-rule/outputs.tf | 15 + .../alicloud-security-group-rule/variables.tf | 7 + examples/alicloud-security-group/README.md | 19 + examples/alicloud-security-group/main.tf | 5 + examples/alicloud-security-group/outputs.tf | 3 + examples/alicloud-security-group/variables.tf | 4 + examples/alicloud-slb-vpc/README.md | 19 + examples/alicloud-slb-vpc/main.tf | 27 + examples/alicloud-slb-vpc/outputs.tf | 7 + examples/alicloud-slb-vpc/variables.tf | 30 + examples/alicloud-slb/README.md | Bin 0 -> 470567 bytes examples/alicloud-slb/main.tf | 25 + examples/alicloud-slb/outputs.tf | 7 + examples/alicloud-slb/variables.tf | 11 + examples/alicloud-vpc-cluster-sg/README.md | 19 + examples/alicloud-vpc-cluster-sg/main.tf | 23 + examples/alicloud-vpc-cluster-sg/outputs.tf | 15 + examples/alicloud-vpc-cluster-sg/variables.tf | 4 + examples/alicloud-vpc-multi-region/README.md | 18 + examples/alicloud-vpc-multi-region/main.tf | 21 + examples/alicloud-vpc-multi-region/outputs.tf | 7 + .../alicloud-vpc-multi-region/variables.tf | 12 + examples/alicloud-vpc-route-entry/main.tf | 55 + examples/alicloud-vpc-route-entry/ouputs.tf | 15 + .../alicloud-vpc-route-entry/variables.tf | 28 + examples/alicloud-vpc/README.md | 18 + examples/alicloud-vpc/main.tf | 28 + examples/alicloud-vpc/outputs.tf | 11 + examples/alicloud-vpc/variables.tf | 25 + .../denverdino/aliyungo/LICENSE.txt | 191 +++ .../denverdino/aliyungo/common/client.go | 225 ++++ .../denverdino/aliyungo/common/regions.go | 29 + .../denverdino/aliyungo/common/request.go | 101 ++ .../denverdino/aliyungo/common/types.go | 15 + .../denverdino/aliyungo/common/version.go | 3 + .../denverdino/aliyungo/ecs/client.go | 37 + .../denverdino/aliyungo/ecs/disks.go | 330 +++++ .../denverdino/aliyungo/ecs/images.go | 280 +++++ .../denverdino/aliyungo/ecs/instance_types.go | 51 + .../denverdino/aliyungo/ecs/instances.go | 540 ++++++++ .../denverdino/aliyungo/ecs/monitoring.go | 136 ++ .../denverdino/aliyungo/ecs/networks.go | 249 ++++ .../denverdino/aliyungo/ecs/regions.go | 34 + .../denverdino/aliyungo/ecs/route_tables.go | 176 +++ .../aliyungo/ecs/security_groups.go | 272 ++++ .../denverdino/aliyungo/ecs/snapshots.go | 131 ++ .../denverdino/aliyungo/ecs/tags.go | 120 ++ .../denverdino/aliyungo/ecs/vpcs.go | 151 +++ .../denverdino/aliyungo/ecs/vrouters.go | 68 + .../denverdino/aliyungo/ecs/vswitches.go | 152 +++ .../denverdino/aliyungo/ecs/zones.go | 65 + .../denverdino/aliyungo/slb/certificates.go | 108 ++ .../denverdino/aliyungo/slb/client.go | 31 + .../denverdino/aliyungo/slb/listeners.go | 495 ++++++++ .../denverdino/aliyungo/slb/loadbalancers.go | 238 ++++ .../denverdino/aliyungo/slb/regions.go | 34 + .../denverdino/aliyungo/slb/servers.go | 122 ++ .../denverdino/aliyungo/slb/tags.go | 85 ++ .../denverdino/aliyungo/slb/vserver_group.go | 159 +++ .../denverdino/aliyungo/util/attempt.go | 76 ++ .../denverdino/aliyungo/util/encoding.go | 152 +++ .../denverdino/aliyungo/util/iso6801.go | 80 ++ .../denverdino/aliyungo/util/signature.go | 40 + .../denverdino/aliyungo/util/util.go | 147 +++ vendor/vendor.json | 24 + .../providers/alicloud/d/images.html.markdown | 50 + .../alicloud/d/instance_types.html.markdown | 48 + .../alicloud/d/regions.html.markdown | 34 + .../providers/alicloud/d/zones.html.markdown | 48 + .../providers/alicloud/index.html.markdown | 113 ++ .../providers/alicloud/r/disk.html.markdown | 56 + .../alicloud/r/disk_attachment.html.markdown | 68 + .../providers/alicloud/r/eip.html.markdown | 38 + .../alicloud/r/eip_association.html.markdown | 75 ++ .../alicloud/r/instance.html.markdown | 100 ++ .../alicloud/r/nat_gateway.html.markdown | 74 ++ .../alicloud/r/security_group.html.markdown | 53 + .../r/security_group_rule.html.markdown | 61 + .../providers/alicloud/r/slb.html.markdown | 90 ++ .../alicloud/r/slb_attachment.html.markdown | 43 + .../providers/alicloud/r/vpc.html.markdown | 41 + .../alicloud/r/vroute_entry.html.markdown | 53 + .../alicloud/r/vswitch.html.markdown | 48 + 167 files changed, 17175 insertions(+) create mode 100644 builtin/providers/alicloud/common.go create mode 100644 builtin/providers/alicloud/config.go create mode 100644 builtin/providers/alicloud/data_source_alicloud_common.go create mode 100644 builtin/providers/alicloud/data_source_alicloud_images.go create mode 100644 builtin/providers/alicloud/data_source_alicloud_images_test.go create mode 100644 builtin/providers/alicloud/data_source_alicloud_instance_types.go create mode 100644 builtin/providers/alicloud/data_source_alicloud_instance_types_test.go create mode 100644 builtin/providers/alicloud/data_source_alicloud_regions.go create mode 100644 builtin/providers/alicloud/data_source_alicloud_regions_test.go create mode 100644 builtin/providers/alicloud/data_source_alicloud_zones.go create mode 100644 builtin/providers/alicloud/data_source_alicloud_zones_test.go create mode 100644 builtin/providers/alicloud/errors.go create mode 100644 builtin/providers/alicloud/extension_ecs.go create mode 100644 builtin/providers/alicloud/extension_nat_gateway.go create mode 100644 builtin/providers/alicloud/extension_slb.go create mode 100644 builtin/providers/alicloud/extension_tags.go create mode 100644 builtin/providers/alicloud/provider.go create mode 100644 builtin/providers/alicloud/provider_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_disk.go create mode 100644 builtin/providers/alicloud/resource_alicloud_disk_attachment.go create mode 100644 builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_disk_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_eip.go create mode 100644 builtin/providers/alicloud/resource_alicloud_eip_association.go create mode 100644 builtin/providers/alicloud/resource_alicloud_eip_association_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_eip_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_instance.go create mode 100644 builtin/providers/alicloud/resource_alicloud_instance_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_nat_gateway.go create mode 100644 builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_security_group.go create mode 100644 builtin/providers/alicloud/resource_alicloud_security_group_rule.go create mode 100644 builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_security_group_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_slb.go create mode 100644 builtin/providers/alicloud/resource_alicloud_slb_attachment.go create mode 100644 builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_slb_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_vpc.go create mode 100644 builtin/providers/alicloud/resource_alicloud_vpc_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_vroute_entry.go create mode 100644 builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go create mode 100644 builtin/providers/alicloud/resource_alicloud_vswitch.go create mode 100644 builtin/providers/alicloud/resource_alicloud_vswitch_test.go create mode 100644 builtin/providers/alicloud/service_alicloud_ecs.go create mode 100644 builtin/providers/alicloud/service_alicloud_slb.go create mode 100644 builtin/providers/alicloud/service_alicloud_vpc.go create mode 100644 builtin/providers/alicloud/struct_security_groups.go create mode 100644 builtin/providers/alicloud/tags.go create mode 100644 builtin/providers/alicloud/validators.go create mode 100644 builtin/providers/alicloud/validators_test.go create mode 100644 examples/alicloud-ecs-image/README.md create mode 100644 examples/alicloud-ecs-image/main.tf create mode 100644 examples/alicloud-ecs-image/outputs.tf create mode 100644 examples/alicloud-ecs-image/variables.tf create mode 100644 examples/alicloud-ecs-slb/README.md create mode 100644 examples/alicloud-ecs-slb/main.tf create mode 100644 examples/alicloud-ecs-slb/outputs.tf create mode 100644 examples/alicloud-ecs-slb/variables.tf create mode 100644 examples/alicloud-ecs-special-sg/README.md create mode 100644 examples/alicloud-ecs-special-sg/main.tf create mode 100644 examples/alicloud-ecs-special-sg/outputs.tf create mode 100644 examples/alicloud-ecs-special-sg/variables.tf create mode 100644 examples/alicloud-ecs-userdata/README.md create mode 100644 examples/alicloud-ecs-userdata/main.tf create mode 100644 examples/alicloud-ecs-userdata/outputs.tf create mode 100644 examples/alicloud-ecs-userdata/userdata.sh create mode 100644 examples/alicloud-ecs-userdata/variables.tf create mode 100644 examples/alicloud-ecs-vpc-cluster/README.md create mode 100644 examples/alicloud-ecs-vpc-cluster/main.tf create mode 100644 examples/alicloud-ecs-vpc-cluster/variables.tf create mode 100644 examples/alicloud-ecs-vpc/README.md create mode 100644 examples/alicloud-ecs-vpc/main.tf create mode 100644 examples/alicloud-ecs-vpc/outputs.tf create mode 100644 examples/alicloud-ecs-vpc/variables.tf create mode 100644 examples/alicloud-ecs-zone-type/README.md create mode 100644 examples/alicloud-ecs-zone-type/main.tf create mode 100644 examples/alicloud-ecs-zone-type/outputs.tf create mode 100644 examples/alicloud-ecs-zone-type/variables.tf create mode 100644 examples/alicloud-ecs/README.md create mode 100644 examples/alicloud-ecs/main.tf create mode 100644 examples/alicloud-ecs/outputs.tf create mode 100644 examples/alicloud-ecs/variables.tf create mode 100644 examples/alicloud-security-group-rule/main.tf create mode 100644 examples/alicloud-security-group-rule/outputs.tf create mode 100644 examples/alicloud-security-group-rule/variables.tf create mode 100644 examples/alicloud-security-group/README.md create mode 100644 examples/alicloud-security-group/main.tf create mode 100644 examples/alicloud-security-group/outputs.tf create mode 100644 examples/alicloud-security-group/variables.tf create mode 100644 examples/alicloud-slb-vpc/README.md create mode 100644 examples/alicloud-slb-vpc/main.tf create mode 100644 examples/alicloud-slb-vpc/outputs.tf create mode 100644 examples/alicloud-slb-vpc/variables.tf create mode 100644 examples/alicloud-slb/README.md create mode 100644 examples/alicloud-slb/main.tf create mode 100644 examples/alicloud-slb/outputs.tf create mode 100644 examples/alicloud-slb/variables.tf create mode 100644 examples/alicloud-vpc-cluster-sg/README.md create mode 100644 examples/alicloud-vpc-cluster-sg/main.tf create mode 100644 examples/alicloud-vpc-cluster-sg/outputs.tf create mode 100644 examples/alicloud-vpc-cluster-sg/variables.tf create mode 100644 examples/alicloud-vpc-multi-region/README.md create mode 100644 examples/alicloud-vpc-multi-region/main.tf create mode 100644 examples/alicloud-vpc-multi-region/outputs.tf create mode 100644 examples/alicloud-vpc-multi-region/variables.tf create mode 100644 examples/alicloud-vpc-route-entry/main.tf create mode 100644 examples/alicloud-vpc-route-entry/ouputs.tf create mode 100644 examples/alicloud-vpc-route-entry/variables.tf create mode 100644 examples/alicloud-vpc/README.md create mode 100644 examples/alicloud-vpc/main.tf create mode 100644 examples/alicloud-vpc/outputs.tf create mode 100644 examples/alicloud-vpc/variables.tf create mode 100644 vendor/github.com/denverdino/aliyungo/LICENSE.txt create mode 100755 vendor/github.com/denverdino/aliyungo/common/client.go create mode 100644 vendor/github.com/denverdino/aliyungo/common/regions.go create mode 100644 vendor/github.com/denverdino/aliyungo/common/request.go create mode 100644 vendor/github.com/denverdino/aliyungo/common/types.go create mode 100644 vendor/github.com/denverdino/aliyungo/common/version.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/client.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/disks.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/images.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/instance_types.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/instances.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/monitoring.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/networks.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/regions.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/route_tables.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/security_groups.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/snapshots.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/tags.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/vpcs.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/vrouters.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/vswitches.go create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/zones.go create mode 100644 vendor/github.com/denverdino/aliyungo/slb/certificates.go create mode 100644 vendor/github.com/denverdino/aliyungo/slb/client.go create mode 100644 vendor/github.com/denverdino/aliyungo/slb/listeners.go create mode 100644 vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go create mode 100644 vendor/github.com/denverdino/aliyungo/slb/regions.go create mode 100644 vendor/github.com/denverdino/aliyungo/slb/servers.go create mode 100644 vendor/github.com/denverdino/aliyungo/slb/tags.go create mode 100644 vendor/github.com/denverdino/aliyungo/slb/vserver_group.go create mode 100644 vendor/github.com/denverdino/aliyungo/util/attempt.go create mode 100644 vendor/github.com/denverdino/aliyungo/util/encoding.go create mode 100644 vendor/github.com/denverdino/aliyungo/util/iso6801.go create mode 100644 vendor/github.com/denverdino/aliyungo/util/signature.go create mode 100644 vendor/github.com/denverdino/aliyungo/util/util.go create mode 100644 website/source/docs/providers/alicloud/d/images.html.markdown create mode 100644 website/source/docs/providers/alicloud/d/instance_types.html.markdown create mode 100644 website/source/docs/providers/alicloud/d/regions.html.markdown create mode 100644 website/source/docs/providers/alicloud/d/zones.html.markdown create mode 100644 website/source/docs/providers/alicloud/index.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/disk.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/disk_attachment.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/eip.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/eip_association.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/instance.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/nat_gateway.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/security_group.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/security_group_rule.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/slb.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/slb_attachment.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/vpc.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/vroute_entry.html.markdown create mode 100644 website/source/docs/providers/alicloud/r/vswitch.html.markdown diff --git a/builtin/providers/alicloud/common.go b/builtin/providers/alicloud/common.go new file mode 100644 index 0000000000..24c3647db7 --- /dev/null +++ b/builtin/providers/alicloud/common.go @@ -0,0 +1,52 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/hashicorp/terraform/helper/schema" +) + +type InstanceNetWork string + +const ( + ClassicNet = InstanceNetWork("classic") + VpcNet = InstanceNetWork("vpc") +) + +const defaultTimeout = 120 + +func getRegion(d *schema.ResourceData, meta interface{}) common.Region { + return meta.(*AliyunClient).Region +} + +func notFoundError(err error) bool { + if e, ok := err.(*common.Error); ok && (e.StatusCode == 404 || e.ErrorResponse.Message == "Not found") { + return true + } + + return false +} + +// Protocal represents network protocal +type Protocal string + +// Constants of protocal definition +const ( + Http = Protocal("http") + Https = Protocal("https") + Tcp = Protocal("tcp") + Udp = Protocal("udp") +) + +// ValidProtocals network protocal list +var ValidProtocals = []Protocal{Http, Https, Tcp, Udp} + +// simple array value check method, support string type only +func isProtocalValid(value string) bool { + res := false + for _, v := range ValidProtocals { + if string(v) == value { + res = true + } + } + return res +} diff --git a/builtin/providers/alicloud/config.go b/builtin/providers/alicloud/config.go new file mode 100644 index 0000000000..352e2e21c1 --- /dev/null +++ b/builtin/providers/alicloud/config.go @@ -0,0 +1,103 @@ +package alicloud + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/denverdino/aliyungo/slb" +) + +// Config of aliyun +type Config struct { + AccessKey string + SecretKey string + Region common.Region +} + +// AliyunClient of aliyun +type AliyunClient struct { + Region common.Region + ecsconn *ecs.Client + vpcconn *ecs.Client + slbconn *slb.Client +} + +// Client for AliyunClient +func (c *Config) Client() (*AliyunClient, error) { + err := c.loadAndValidate() + if err != nil { + return nil, err + } + + ecsconn, err := c.ecsConn() + if err != nil { + return nil, err + } + + slbconn, err := c.slbConn() + if err != nil { + return nil, err + } + + vpcconn, err := c.vpcConn() + if err != nil { + return nil, err + } + + return &AliyunClient{ + Region: c.Region, + ecsconn: ecsconn, + vpcconn: vpcconn, + slbconn: slbconn, + }, nil +} + +func (c *Config) loadAndValidate() error { + err := c.validateRegion() + if err != nil { + return err + } + + return nil +} + +func (c *Config) validateRegion() error { + + for _, valid := range common.ValidRegions { + if c.Region == valid { + return nil + } + } + + return fmt.Errorf("Not a valid region: %s", c.Region) +} + +func (c *Config) ecsConn() (*ecs.Client, error) { + client := ecs.NewClient(c.AccessKey, c.SecretKey) + _, err := client.DescribeRegions() + + if err != nil { + return nil, err + } + + return client, nil +} + +func (c *Config) slbConn() (*slb.Client, error) { + client := slb.NewClient(c.AccessKey, c.SecretKey) + + return client, nil +} + +func (c *Config) vpcConn() (*ecs.Client, error) { + _, err := c.ecsConn() + + if err != nil { + return nil, err + } + + client := &ecs.Client{} + client.Init("https://vpc.aliyuncs.com/", "2016-04-28", c.AccessKey, c.SecretKey) + return client, nil +} diff --git a/builtin/providers/alicloud/data_source_alicloud_common.go b/builtin/providers/alicloud/data_source_alicloud_common.go new file mode 100644 index 0000000000..1da432cfc9 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_common.go @@ -0,0 +1,18 @@ +package alicloud + +import ( + "bytes" + "fmt" + "github.com/hashicorp/terraform/helper/hashcode" +) + +// Generates a hash for the set hash function used by the ID +func dataResourceIdHash(ids []string) string { + var buf bytes.Buffer + + for _, id := range ids { + buf.WriteString(fmt.Sprintf("%s-", id)) + } + + return fmt.Sprintf("%d", hashcode.String(buf.String())) +} diff --git a/builtin/providers/alicloud/data_source_alicloud_images.go b/builtin/providers/alicloud/data_source_alicloud_images.go new file mode 100644 index 0000000000..ae5b660693 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_images.go @@ -0,0 +1,324 @@ +package alicloud + +import ( + "fmt" + "log" + "regexp" + "sort" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func dataSourceAlicloudImages() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAlicloudImagesRead, + + Schema: map[string]*schema.Schema{ + "name_regex": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateNameRegex, + }, + "most_recent": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + "owners": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateImageOwners, + }, + // Computed values. + "images": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "image_id": { + Type: schema.TypeString, + Computed: true, + }, + "architecture": { + Type: schema.TypeString, + Computed: true, + }, + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "image_owner_alias": { + Type: schema.TypeString, + Computed: true, + }, + "os_type": { + Type: schema.TypeString, + Computed: true, + }, + "os_name": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "platform": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "size": { + Type: schema.TypeInt, + Computed: true, + }, + // Complex computed values + "disk_device_mappings": { + Type: schema.TypeList, + Computed: true, + //Set: imageDiskDeviceMappingHash, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "device": { + Type: schema.TypeString, + Computed: true, + }, + "size": { + Type: schema.TypeString, + Computed: true, + }, + "snapshot_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "product_code": { + Type: schema.TypeString, + Computed: true, + }, + "is_self_shared": { + Type: schema.TypeString, + Computed: true, + }, + "is_subscribed": { + Type: schema.TypeBool, + Computed: true, + }, + "is_copied": { + Type: schema.TypeBool, + Computed: true, + }, + "is_support_io_optimized": { + Type: schema.TypeBool, + Computed: true, + }, + "image_version": { + Type: schema.TypeString, + Computed: true, + }, + "progress": { + Type: schema.TypeString, + Computed: true, + }, + "usage": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tagsSchema(), + }, + }, + }, + }, + } +} + +// dataSourceAlicloudImagesDescriptionRead performs the Alicloud Image lookup. +func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + nameRegex, nameRegexOk := d.GetOk("name_regex") + owners, ownersOk := d.GetOk("owners") + mostRecent, mostRecentOk := d.GetOk("most_recent") + + if nameRegexOk == false && ownersOk == false && mostRecentOk == false { + return fmt.Errorf("One of name_regex, owners or most_recent must be assigned") + } + + params := &ecs.DescribeImagesArgs{ + RegionId: getRegion(d, meta), + } + + if ownersOk { + params.ImageOwnerAlias = ecs.ImageOwnerAlias(owners.(string)) + } + + resp, _, err := conn.DescribeImages(params) + if err != nil { + return err + } + + var filteredImages []ecs.ImageType + if nameRegexOk { + r := regexp.MustCompile(nameRegex.(string)) + for _, image := range resp { + // Check for a very rare case where the response would include no + // image name. No name means nothing to attempt a match against, + // therefore we are skipping such image. + if image.ImageName == "" { + log.Printf("[WARN] Unable to find Image name to match against "+ + "for image ID %q, nothing to do.", + image.ImageId) + continue + } + if r.MatchString(image.ImageName) { + filteredImages = append(filteredImages, image) + } + } + } else { + filteredImages = resp[:] + } + + var images []ecs.ImageType + if len(filteredImages) < 1 { + return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + } + + log.Printf("[DEBUG] alicloud_image - multiple results found and `most_recent` is set to: %t", mostRecent.(bool)) + if len(filteredImages) > 1 && mostRecent.(bool) { + // Query returned single result. + images = append(images, mostRecentImage(filteredImages)) + } else { + images = filteredImages + } + + log.Printf("[DEBUG] alicloud_image - Images found: %#v", images) + return imagesDescriptionAttributes(d, images, meta) +} + +// populate the numerous fields that the image description returns. +func imagesDescriptionAttributes(d *schema.ResourceData, images []ecs.ImageType, meta interface{}) error { + var ids []string + var s []map[string]interface{} + for _, image := range images { + mapping := map[string]interface{}{ + "id": image.ImageId, + "architecture": image.Architecture, + "creation_time": image.CreationTime.String(), + "description": image.Description, + "image_id": image.ImageId, + "image_owner_alias": image.ImageOwnerAlias, + "os_name": image.OSName, + "os_type": image.OSType, + "name": image.ImageName, + "platform": image.Platform, + "status": image.Status, + "state": image.Status, + "size": image.Size, + "is_self_shared": image.IsSelfShared, + "is_subscribed": image.IsSubscribed, + "is_copied": image.IsCopied, + "is_support_io_optimized": image.IsSupportIoOptimized, + "image_version": image.ImageVersion, + "progress": image.Progress, + "usage": image.Usage, + "product_code": image.ProductCode, + + // Complex types get their own functions + "disk_device_mappings": imageDiskDeviceMappings(image.DiskDeviceMappings.DiskDeviceMapping), + "tags": imageTagsMappings(d, image.ImageId, meta), + } + + log.Printf("[DEBUG] alicloud_image - adding image mapping: %v", mapping) + ids = append(ids, image.ImageId) + s = append(s, mapping) + } + + d.SetId(dataResourceIdHash(ids)) + if err := d.Set("images", s); err != nil { + return err + } + return nil +} + +//Find most recent image +type imageSort []ecs.ImageType + +func (a imageSort) Len() int { + return len(a) +} +func (a imageSort) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} +func (a imageSort) Less(i, j int) bool { + itime, _ := time.Parse(time.RFC3339, a[i].CreationTime.String()) + jtime, _ := time.Parse(time.RFC3339, a[j].CreationTime.String()) + return itime.Unix() < jtime.Unix() +} + +// Returns the most recent Image out of a slice of images. +func mostRecentImage(images []ecs.ImageType) ecs.ImageType { + sortedImages := images + sort.Sort(imageSort(sortedImages)) + return sortedImages[len(sortedImages)-1] +} + +// Returns a set of disk device mappings. +func imageDiskDeviceMappings(m []ecs.DiskDeviceMapping) []map[string]interface{} { + var s []map[string]interface{} + + for _, v := range m { + mapping := map[string]interface{}{ + "device": v.Device, + "size": v.Size, + "snapshot_id": v.SnapshotId, + } + + log.Printf("[DEBUG] alicloud_image - adding disk device mapping: %v", mapping) + s = append(s, mapping) + } + + return s +} + +//Returns a mapping of image tags +func imageTagsMappings(d *schema.ResourceData, imageId string, meta interface{}) map[string]string { + client := meta.(*AliyunClient) + conn := client.ecsconn + + tags, _, err := conn.DescribeTags(&ecs.DescribeTagsArgs{ + RegionId: getRegion(d, meta), + ResourceType: ecs.TagResourceImage, + ResourceId: imageId, + }) + + if err != nil { + log.Printf("[ERROR] DescribeTags for image got error: %#v", err) + return nil + } + + log.Printf("[DEBUG] DescribeTags for image : %v", tags) + return tagsToMap(tags) +} diff --git a/builtin/providers/alicloud/data_source_alicloud_images_test.go b/builtin/providers/alicloud/data_source_alicloud_images_test.go new file mode 100644 index 0000000000..7512c6e916 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_images_test.go @@ -0,0 +1,130 @@ +package alicloud + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAlicloudImagesDataSource_images(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudImagesDataSourceImagesConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_images.multi_image"), + + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.#", "2"), + + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.architecture", "x86_64"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.disk_device_mappings.#", "0"), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.0.creation_time", regexp.MustCompile("^20[0-9]{2}-")), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.0.image_id", regexp.MustCompile("^centos_6\\w{1,5}[64]{1}.")), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.image_owner_alias", "system"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.os_type", "linux"), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.0.name", regexp.MustCompile("^centos_6[a-zA-Z0-9_]{1,5}[64]{1}.")), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.progress", "100%"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.state", "Available"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.status", "Available"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.usage", "instance"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.0.tags.%", "0"), + + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.architecture", "i386"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.disk_device_mappings.#", "0"), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.1.creation_time", regexp.MustCompile("^20[0-9]{2}-")), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.1.image_id", regexp.MustCompile("^centos_6[a-zA-Z0-9_]{1,5}[32]{1}.")), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.image_owner_alias", "system"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.os_type", "linux"), + resource.TestMatchResourceAttr("data.alicloud_images.multi_image", "images.1.name", regexp.MustCompile("^centos_6\\w{1,5}[32]{1}.")), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.progress", "100%"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.state", "Available"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.status", "Available"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.usage", "instance"), + resource.TestCheckResourceAttr("data.alicloud_images.multi_image", "images.1.tags.%", "0"), + ), + }, + }, + }) +} + +func TestAccAlicloudImagesDataSource_owners(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudImagesDataSourceOwnersConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_images.owners_filtered_image"), + ), + }, + }, + }) +} + +func TestAccAlicloudImagesDataSource_ownersEmpty(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudImagesDataSourceEmptyOwnersConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_images.empty_owners_filtered_image"), + resource.TestCheckResourceAttr("data.alicloud_images.empty_owners_filtered_image", "most_recent", "true"), + ), + }, + }, + }) +} + +func TestAccAlicloudImagesDataSource_nameRegexFilter(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudImagesDataSourceNameRegexConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_images.name_regex_filtered_image"), + resource.TestMatchResourceAttr("data.alicloud_images.name_regex_filtered_image", "images.0.image_id", regexp.MustCompile("^centos_")), + ), + }, + }, + }) +} + +// Instance store test - using centos images +const testAccCheckAlicloudImagesDataSourceImagesConfig = ` +data "alicloud_images" "multi_image" { + owners = "system" + name_regex = "^centos_6" +} +` + +// Testing owner parameter +const testAccCheckAlicloudImagesDataSourceOwnersConfig = ` +data "alicloud_images" "owners_filtered_image" { + most_recent = true + owners = "system" +} +` + +const testAccCheckAlicloudImagesDataSourceEmptyOwnersConfig = ` +data "alicloud_images" "empty_owners_filtered_image" { + most_recent = true + owners = "" +} +` + +// Testing name_regex parameter +const testAccCheckAlicloudImagesDataSourceNameRegexConfig = ` +data "alicloud_images" "name_regex_filtered_image" { + most_recent = true + owners = "system" + name_regex = "^centos_6\\w{1,5}[64]{1}.*" +} +` diff --git a/builtin/providers/alicloud/data_source_alicloud_instance_types.go b/builtin/providers/alicloud/data_source_alicloud_instance_types.go new file mode 100644 index 0000000000..87d6bfc432 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_instance_types.go @@ -0,0 +1,127 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "log" +) + +func dataSourceAlicloudInstanceTypes() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAlicloudInstanceTypesRead, + + Schema: map[string]*schema.Schema{ + "instance_type_family": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "cpu_core_count": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "memory_size": { + Type: schema.TypeFloat, + Optional: true, + ForceNew: true, + }, + // Computed values. + "instance_types": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "cpu_core_count": { + Type: schema.TypeInt, + Computed: true, + }, + "memory_size": { + Type: schema.TypeFloat, + Computed: true, + }, + "family": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceAlicloudInstanceTypesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + cpu, _ := d.Get("cpu_core_count").(int) + mem, _ := d.Get("memory_size").(float64) + + args, err := buildAliyunAlicloudInstanceTypesArgs(d, meta) + + if err != nil { + return err + } + + resp, err := conn.DescribeInstanceTypesNew(args) + if err != nil { + return err + } + + var instanceTypes []ecs.InstanceTypeItemType + for _, types := range resp { + if cpu > 0 && types.CpuCoreCount != cpu { + continue + } + + if mem > 0 && types.MemorySize != mem { + continue + } + instanceTypes = append(instanceTypes, types) + } + + if len(instanceTypes) < 1 { + return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + } + + log.Printf("[DEBUG] alicloud_instance_type - Types found: %#v", instanceTypes) + return instanceTypesDescriptionAttributes(d, instanceTypes) +} + +func instanceTypesDescriptionAttributes(d *schema.ResourceData, types []ecs.InstanceTypeItemType) error { + var ids []string + var s []map[string]interface{} + for _, t := range types { + mapping := map[string]interface{}{ + "id": t.InstanceTypeId, + "cpu_core_count": t.CpuCoreCount, + "memory_size": t.MemorySize, + "family": t.InstanceTypeFamily, + } + + log.Printf("[DEBUG] alicloud_instance_type - adding type mapping: %v", mapping) + ids = append(ids, t.InstanceTypeId) + s = append(s, mapping) + } + + d.SetId(dataResourceIdHash(ids)) + if err := d.Set("instance_types", s); err != nil { + return err + } + return nil +} + +func buildAliyunAlicloudInstanceTypesArgs(d *schema.ResourceData, meta interface{}) (*ecs.DescribeInstanceTypesArgs, error) { + args := &ecs.DescribeInstanceTypesArgs{} + + if v := d.Get("instance_type_family").(string); v != "" { + args.InstanceTypeFamily = v + } + + return args, nil +} diff --git a/builtin/providers/alicloud/data_source_alicloud_instance_types_test.go b/builtin/providers/alicloud/data_source_alicloud_instance_types_test.go new file mode 100644 index 0000000000..43a180deff --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_instance_types_test.go @@ -0,0 +1,56 @@ +package alicloud + +import ( + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccAlicloudInstanceTypesDataSource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudInstanceTypesDataSourceBasicConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_instance_types.4c8g"), + + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.#", "4"), + + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.cpu_core_count", "4"), + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.memory_size", "8"), + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.id", "ecs.s3.large"), + ), + }, + + resource.TestStep{ + Config: testAccCheckAlicloudInstanceTypesDataSourceBasicConfigUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_instance_types.4c8g"), + + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.#", "1"), + + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.cpu_core_count", "4"), + resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.memory_size", "8"), + ), + }, + }, + }) +} + +const testAccCheckAlicloudInstanceTypesDataSourceBasicConfig = ` +data "alicloud_instance_types" "4c8g" { + cpu_core_count = 4 + memory_size = 8 +} +` + +const testAccCheckAlicloudInstanceTypesDataSourceBasicConfigUpdate = ` +data "alicloud_instance_types" "4c8g" { + instance_type_family= "ecs.s3" + cpu_core_count = 4 + memory_size = 8 +} +` diff --git a/builtin/providers/alicloud/data_source_alicloud_regions.go b/builtin/providers/alicloud/data_source_alicloud_regions.go new file mode 100644 index 0000000000..8bdee26b6a --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_regions.go @@ -0,0 +1,114 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "log" +) + +func dataSourceAlicloudRegions() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAlicloudRegionsRead, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "current": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + + //Computed value + "regions": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "region_id": { + Type: schema.TypeString, + Computed: true, + }, + "local_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceAlicloudRegionsRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + currentRegion := getRegion(d, meta) + + resp, err := conn.DescribeRegions() + if err != nil { + return err + } + if resp == nil || len(resp) == 0 { + return fmt.Errorf("no matching regions found") + } + name, nameOk := d.GetOk("name") + current := d.Get("current").(bool) + var filterRegions []ecs.RegionType + for _, region := range resp { + if current { + if nameOk && common.Region(name.(string)) != currentRegion { + return fmt.Errorf("name doesn't match current region: %#v, please input again.", currentRegion) + } + if region.RegionId == currentRegion { + filterRegions = append(filterRegions, region) + break + } + continue + } + if nameOk { + if common.Region(name.(string)) == region.RegionId { + filterRegions = append(filterRegions, region) + break + } + continue + } + filterRegions = append(filterRegions, region) + } + if len(filterRegions) < 1 { + return fmt.Errorf("Your query region returned no results. Please change your search criteria and try again.") + } + + return regionsDescriptionAttributes(d, filterRegions) +} + +func regionsDescriptionAttributes(d *schema.ResourceData, regions []ecs.RegionType) error { + var ids []string + var s []map[string]interface{} + for _, region := range regions { + mapping := map[string]interface{}{ + "id": region.RegionId, + "region_id": region.RegionId, + "local_name": region.LocalName, + } + + log.Printf("[DEBUG] alicloud_regions - adding region mapping: %v", mapping) + ids = append(ids, string(region.RegionId)) + s = append(s, mapping) + } + + d.SetId(dataResourceIdHash(ids)) + if err := d.Set("regions", s); err != nil { + return err + } + return nil +} diff --git a/builtin/providers/alicloud/data_source_alicloud_regions_test.go b/builtin/providers/alicloud/data_source_alicloud_regions_test.go new file mode 100644 index 0000000000..f2aff1bbe4 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_regions_test.go @@ -0,0 +1,114 @@ +package alicloud + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAlicloudRegionsDataSource_regions(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRegionsDataSourceRegionsConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_regions.region"), + + resource.TestCheckResourceAttr("data.alicloud_regions.region", "name", "cn-beijing"), + resource.TestCheckResourceAttr("data.alicloud_regions.region", "current", "true"), + + resource.TestCheckResourceAttr("data.alicloud_regions.region", "regions.#", "1"), + + resource.TestCheckResourceAttr("data.alicloud_regions.region", "regions.0.id", "cn-beijing"), + resource.TestCheckResourceAttr("data.alicloud_regions.region", "regions.0.region_id", "cn-beijing"), + resource.TestCheckResourceAttr("data.alicloud_regions.region", "regions.0.local_name", "华北 2"), + ), + }, + }, + }) +} + +func TestAccAlicloudRegionsDataSource_name(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRegionsDataSourceNameConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_regions.name_filtered_region"), + resource.TestCheckResourceAttr("data.alicloud_regions.name_filtered_region", "name", "cn-hangzhou")), + }, + }, + }) +} + +func TestAccAlicloudRegionsDataSource_current(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRegionsDataSourceCurrentConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_regions.current_filtered_region"), + resource.TestCheckResourceAttr("data.alicloud_regions.current_filtered_region", "current", "true"), + ), + }, + }, + }) +} + +func TestAccAlicloudRegionsDataSource_empty(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRegionsDataSourceEmptyConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_regions.empty_params_region"), + + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "name", ""), + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "current", ""), + + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.#", "13"), + + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.id", "cn-shenzhen"), + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.region_id", "cn-shenzhen"), + resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.local_name", "华南 1"), + ), + }, + }, + }) +} + +// Instance store test - using centos regions +const testAccCheckAlicloudRegionsDataSourceRegionsConfig = ` +data "alicloud_regions" "region" { + name = "cn-beijing" + current = true +} +` + +// Testing name parameter +const testAccCheckAlicloudRegionsDataSourceNameConfig = ` +data "alicloud_regions" "name_filtered_region" { + name = "cn-hangzhou" +} +` + +// Testing current parameter +const testAccCheckAlicloudRegionsDataSourceCurrentConfig = ` +data "alicloud_regions" "current_filtered_region" { + current = true +} +` + +// Testing empty parmas +const testAccCheckAlicloudRegionsDataSourceEmptyConfig = ` +data "alicloud_regions" "empty_params_region" { +} +` diff --git a/builtin/providers/alicloud/data_source_alicloud_zones.go b/builtin/providers/alicloud/data_source_alicloud_zones.go new file mode 100644 index 0000000000..689e98a167 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_zones.go @@ -0,0 +1,137 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "log" + "reflect" +) + +func dataSourceAlicloudZones() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAlicloudZonesRead, + + Schema: map[string]*schema.Schema{ + "available_instance_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "available_resource_creation": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "available_disk_category": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + // Computed values. + "zones": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "local_name": { + Type: schema.TypeString, + Computed: true, + }, + "available_instance_types": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "available_resource_creation": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "available_disk_categories": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + }, + } +} + +func dataSourceAlicloudZonesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + insType, _ := d.Get("available_instance_type").(string) + resType, _ := d.Get("available_resource_creation").(string) + diskType, _ := d.Get("available_disk_category").(string) + + resp, err := conn.DescribeZones(getRegion(d, meta)) + if err != nil { + return err + } + + var zoneTypes []ecs.ZoneType + for _, types := range resp { + if insType != "" && !constraints(types.AvailableInstanceTypes.InstanceTypes, insType) { + continue + } + + if resType != "" && !constraints(types.AvailableResourceCreation.ResourceTypes, resType) { + continue + } + + if diskType != "" && !constraints(types.AvailableDiskCategories.DiskCategories, diskType) { + continue + } + zoneTypes = append(zoneTypes, types) + } + + if len(zoneTypes) < 1 { + return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + } + + log.Printf("[DEBUG] alicloud_zones - Zones found: %#v", zoneTypes) + return zonesDescriptionAttributes(d, zoneTypes) +} + +// check array constraints str +func constraints(arr interface{}, v string) bool { + arrs := reflect.ValueOf(arr) + len := arrs.Len() + for i := 0; i < len; i++ { + if arrs.Index(i).String() == v { + return true + } + } + return false +} + +func zonesDescriptionAttributes(d *schema.ResourceData, types []ecs.ZoneType) error { + var ids []string + var s []map[string]interface{} + for _, t := range types { + mapping := map[string]interface{}{ + "id": t.ZoneId, + "local_name": t.LocalName, + "available_instance_types": t.AvailableInstanceTypes.InstanceTypes, + "available_resource_creation": t.AvailableResourceCreation.ResourceTypes, + "available_disk_categories": t.AvailableDiskCategories.DiskCategories, + } + + log.Printf("[DEBUG] alicloud_zones - adding zone mapping: %v", mapping) + ids = append(ids, t.ZoneId) + s = append(s, mapping) + } + + d.SetId(dataResourceIdHash(ids)) + if err := d.Set("zones", s); err != nil { + return err + } + return nil +} diff --git a/builtin/providers/alicloud/data_source_alicloud_zones_test.go b/builtin/providers/alicloud/data_source_alicloud_zones_test.go new file mode 100644 index 0000000000..4b07c96717 --- /dev/null +++ b/builtin/providers/alicloud/data_source_alicloud_zones_test.go @@ -0,0 +1,70 @@ +package alicloud + +import ( + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccAlicloudZonesDataSource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudZonesDataSourceBasicConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"), + ), + }, + }, + }) +} + +func TestAccAlicloudZonesDataSource_filter(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudZonesDataSourceFilter, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"), + resource.TestCheckResourceAttr("data.alicloud_zones.foo", "zones.#", "2"), + ), + }, + + resource.TestStep{ + Config: testAccCheckAlicloudZonesDataSourceFilterIoOptimized, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"), + resource.TestCheckResourceAttr("data.alicloud_zones.foo", "zones.#", "1"), + ), + }, + }, + }) +} + +const testAccCheckAlicloudZonesDataSourceBasicConfig = ` +data "alicloud_zones" "foo" { +} +` + +const testAccCheckAlicloudZonesDataSourceFilter = ` +data "alicloud_zones" "foo" { + "available_instance_type"= "ecs.c2.xlarge" + "available_resource_creation"= "VSwitch" + "available_disk_category"= "cloud_efficiency" +} +` + +const testAccCheckAlicloudZonesDataSourceFilterIoOptimized = ` +data "alicloud_zones" "foo" { + "available_instance_type"= "ecs.c2.xlarge" + "available_resource_creation"= "IoOptimized" + "available_disk_category"= "cloud" +} +` diff --git a/builtin/providers/alicloud/errors.go b/builtin/providers/alicloud/errors.go new file mode 100644 index 0000000000..f285bf968a --- /dev/null +++ b/builtin/providers/alicloud/errors.go @@ -0,0 +1,30 @@ +package alicloud + +const ( + // common + Notfound = "Not found" + // ecs + InstanceNotfound = "Instance.Notfound" + // disk + DiskIncorrectStatus = "IncorrectDiskStatus" + DiskCreatingSnapshot = "DiskCreatingSnapshot" + InstanceLockedForSecurity = "InstanceLockedForSecurity" + // eip + EipIncorrectStatus = "IncorrectEipStatus" + InstanceIncorrectStatus = "IncorrectInstanceStatus" + HaVipIncorrectStatus = "IncorrectHaVipStatus" + // slb + LoadBalancerNotFound = "InvalidLoadBalancerId.NotFound" + + // security_group + InvalidInstanceIdAlreadyExists = "InvalidInstanceId.AlreadyExists" + InvalidSecurityGroupIdNotFound = "InvalidSecurityGroupId.NotFound" + SgDependencyViolation = "DependencyViolation" + + //Nat gateway + NatGatewayInvalidRegionId = "Invalid.RegionId" + DependencyViolationBandwidthPackages = "DependencyViolation.BandwidthPackages" + + // vswitch + VswitcInvalidRegionId = "InvalidRegionId.NotFound" +) diff --git a/builtin/providers/alicloud/extension_ecs.go b/builtin/providers/alicloud/extension_ecs.go new file mode 100644 index 0000000000..091bd97085 --- /dev/null +++ b/builtin/providers/alicloud/extension_ecs.go @@ -0,0 +1,32 @@ +package alicloud + +type GroupRuleDirection string + +const ( + GroupRuleIngress = GroupRuleDirection("ingress") + GroupRuleEgress = GroupRuleDirection("egress") +) + +type GroupRuleIpProtocol string + +const ( + GroupRuleTcp = GroupRuleIpProtocol("tcp") + GroupRuleUdp = GroupRuleIpProtocol("udp") + GroupRuleIcmp = GroupRuleIpProtocol("icmp") + GroupRuleGre = GroupRuleIpProtocol("gre") + GroupRuleAll = GroupRuleIpProtocol("all") +) + +type GroupRuleNicType string + +const ( + GroupRuleInternet = GroupRuleNicType("internet") + GroupRuleIntranet = GroupRuleNicType("intranet") +) + +type GroupRulePolicy string + +const ( + GroupRulePolicyAccept = GroupRulePolicy("accept") + GroupRulePolicyDrop = GroupRulePolicy("drop") +) diff --git a/builtin/providers/alicloud/extension_nat_gateway.go b/builtin/providers/alicloud/extension_nat_gateway.go new file mode 100644 index 0000000000..3dac446a3f --- /dev/null +++ b/builtin/providers/alicloud/extension_nat_gateway.go @@ -0,0 +1,194 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" +) + +type BandwidthPackageType struct { + IpCount int + Bandwidth int + Zone string +} + +type CreateNatGatewayArgs struct { + RegionId common.Region + VpcId string + Spec string + BandwidthPackage []BandwidthPackageType + Name string + Description string + ClientToken string +} + +type ForwardTableIdType struct { + ForwardTableId []string +} + +type BandwidthPackageIdType struct { + BandwidthPackageId []string +} + +type CreateNatGatewayResponse struct { + common.Response + NatGatewayId string + ForwardTableIds ForwardTableIdType + BandwidthPackageIds BandwidthPackageIdType +} + +// CreateNatGateway creates Virtual Private Cloud +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&createvpc +func CreateNatGateway(client *ecs.Client, args *CreateNatGatewayArgs) (resp *CreateNatGatewayResponse, err error) { + response := CreateNatGatewayResponse{} + err = client.Invoke("CreateNatGateway", args, &response) + if err != nil { + return nil, err + } + return &response, err +} + +type NatGatewaySetType struct { + BusinessStatus string + Description string + BandwidthPackageIds BandwidthPackageIdType + ForwardTableIds ForwardTableIdType + InstanceChargeType string + Name string + NatGatewayId string + RegionId common.Region + Spec string + Status string + VpcId string +} + +type DescribeNatGatewayResponse struct { + common.Response + common.PaginationResult + NatGateways struct { + NatGateway []NatGatewaySetType + } +} + +type DescribeNatGatewaysArgs struct { + RegionId common.Region + NatGatewayId string + VpcId string + common.Pagination +} + +func DescribeNatGateways(client *ecs.Client, args *DescribeNatGatewaysArgs) (natGateways []NatGatewaySetType, + pagination *common.PaginationResult, err error) { + + args.Validate() + response := DescribeNatGatewayResponse{} + + err = client.Invoke("DescribeNatGateways", args, &response) + + if err == nil { + return response.NatGateways.NatGateway, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type ModifyNatGatewayAttributeArgs struct { + RegionId common.Region + NatGatewayId string + Name string + Description string +} + +type ModifyNatGatewayAttributeResponse struct { + common.Response +} + +func ModifyNatGatewayAttribute(client *ecs.Client, args *ModifyNatGatewayAttributeArgs) error { + response := ModifyNatGatewayAttributeResponse{} + return client.Invoke("ModifyNatGatewayAttribute", args, &response) +} + +type ModifyNatGatewaySpecArgs struct { + RegionId common.Region + NatGatewayId string + Spec NatGatewaySpec +} + +func ModifyNatGatewaySpec(client *ecs.Client, args *ModifyNatGatewaySpecArgs) error { + response := ModifyNatGatewayAttributeResponse{} + return client.Invoke("ModifyNatGatewaySpec", args, &response) +} + +type DeleteNatGatewayArgs struct { + RegionId common.Region + NatGatewayId string +} + +type DeleteNatGatewayResponse struct { + common.Response +} + +func DeleteNatGateway(client *ecs.Client, args *DeleteNatGatewayArgs) error { + response := DeleteNatGatewayResponse{} + err := client.Invoke("DeleteNatGateway", args, &response) + return err +} + +type DescribeBandwidthPackagesArgs struct { + RegionId common.Region + BandwidthPackageId string + NatGatewayId string +} + +type DescribeBandwidthPackageType struct { + Bandwidth string + BandwidthPackageId string + IpCount string +} + +type DescribeBandwidthPackagesResponse struct { + common.Response + BandwidthPackages struct { + BandwidthPackage []DescribeBandwidthPackageType + } +} + +func DescribeBandwidthPackages(client *ecs.Client, args *DescribeBandwidthPackagesArgs) ([]DescribeBandwidthPackageType, error) { + response := &DescribeBandwidthPackagesResponse{} + err := client.Invoke("DescribeBandwidthPackages", args, response) + if err != nil { + return nil, err + } + return response.BandwidthPackages.BandwidthPackage, err +} + +type DeleteBandwidthPackageArgs struct { + RegionId common.Region + BandwidthPackageId string +} + +type DeleteBandwidthPackageResponse struct { + common.Response +} + +func DeleteBandwidthPackage(client *ecs.Client, args *DeleteBandwidthPackageArgs) error { + response := DeleteBandwidthPackageResponse{} + err := client.Invoke("DeleteBandwidthPackage", args, &response) + return err +} + +type DescribeSnatTableEntriesArgs struct { + RegionId common.Region +} + +func DescribeSnatTableEntries(client *ecs.Client, args *DescribeSnatTableEntriesArgs) { + +} + +type NatGatewaySpec string + +const ( + NatGatewaySmallSpec = NatGatewaySpec("Small") + NatGatewayMiddleSpec = NatGatewaySpec("Middle") + NatGatewayLargeSpec = NatGatewaySpec("Large") +) diff --git a/builtin/providers/alicloud/extension_slb.go b/builtin/providers/alicloud/extension_slb.go new file mode 100644 index 0000000000..9213f47979 --- /dev/null +++ b/builtin/providers/alicloud/extension_slb.go @@ -0,0 +1,71 @@ +package alicloud + +import ( + "fmt" + "strings" + + "github.com/denverdino/aliyungo/slb" +) + +type Listener struct { + InstancePort int + LoadBalancerPort int + Protocol string + SSLCertificateId string + Bandwidth int +} + +// Takes the result of flatmap.Expand for an array of listeners and +// returns ELB API compatible objects +func expandListeners(configured []interface{}) ([]*Listener, error) { + listeners := make([]*Listener, 0, len(configured)) + + // Loop over our configured listeners and create + // an array of aws-sdk-go compatabile objects + for _, lRaw := range configured { + data := lRaw.(map[string]interface{}) + + ip := data["instance_port"].(int) + lp := data["lb_port"].(int) + l := &Listener{ + InstancePort: ip, + LoadBalancerPort: lp, + Protocol: data["lb_protocol"].(string), + Bandwidth: data["bandwidth"].(int), + } + + if v, ok := data["ssl_certificate_id"]; ok { + l.SSLCertificateId = v.(string) + } + + var valid bool + if l.SSLCertificateId != "" { + // validate the protocol is correct + for _, p := range []string{"https", "ssl"} { + if strings.ToLower(l.Protocol) == p { + valid = true + } + } + } else { + valid = true + } + + if valid { + listeners = append(listeners, l) + } else { + return nil, fmt.Errorf("[ERR] SLB Listener: ssl_certificate_id may be set only when protocol is 'https' or 'ssl'") + } + } + + return listeners, nil +} + +func expandBackendServers(list []interface{}) []slb.BackendServerType { + result := make([]slb.BackendServerType, 0, len(list)) + for _, i := range list { + if i.(string) != "" { + result = append(result, slb.BackendServerType{ServerId: i.(string), Weight: 100}) + } + } + return result +} diff --git a/builtin/providers/alicloud/extension_tags.go b/builtin/providers/alicloud/extension_tags.go new file mode 100644 index 0000000000..5b86ebab74 --- /dev/null +++ b/builtin/providers/alicloud/extension_tags.go @@ -0,0 +1,43 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" +) + +type Tag struct { + Key string + Value string +} + +type AddTagsArgs struct { + ResourceId string + ResourceType ecs.TagResourceType //image, instance, snapshot or disk + RegionId common.Region + Tag []Tag +} + +type RemoveTagsArgs struct { + ResourceId string + ResourceType ecs.TagResourceType //image, instance, snapshot or disk + RegionId common.Region + Tag []Tag +} + +func AddTags(client *ecs.Client, args *AddTagsArgs) error { + response := ecs.AddTagsResponse{} + err := client.Invoke("AddTags", args, &response) + if err != nil { + return err + } + return err +} + +func RemoveTags(client *ecs.Client, args *RemoveTagsArgs) error { + response := ecs.RemoveTagsResponse{} + err := client.Invoke("RemoveTags", args, &response) + if err != nil { + return err + } + return err +} diff --git a/builtin/providers/alicloud/provider.go b/builtin/providers/alicloud/provider.go new file mode 100644 index 0000000000..907f3271d9 --- /dev/null +++ b/builtin/providers/alicloud/provider.go @@ -0,0 +1,88 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/hashicorp/terraform/helper/mutexkv" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +// Provider returns a schema.Provider for alicloud +func Provider() terraform.ResourceProvider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "access_key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ACCESS_KEY", nil), + Description: descriptions["access_key"], + }, + "secret_key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SECRET_KEY", nil), + Description: descriptions["secret_key"], + }, + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", "cn-beijing"), + Description: descriptions["region"], + }, + }, + DataSourcesMap: map[string]*schema.Resource{ + + "alicloud_images": dataSourceAlicloudImages(), + "alicloud_regions": dataSourceAlicloudRegions(), + "alicloud_zones": dataSourceAlicloudZones(), + "alicloud_instance_types": dataSourceAlicloudInstanceTypes(), + }, + ResourcesMap: map[string]*schema.Resource{ + "alicloud_instance": resourceAliyunInstance(), + "alicloud_disk": resourceAliyunDisk(), + "alicloud_disk_attachment": resourceAliyunDiskAttachment(), + "alicloud_security_group": resourceAliyunSecurityGroup(), + "alicloud_security_group_rule": resourceAliyunSecurityGroupRule(), + "alicloud_vpc": resourceAliyunVpc(), + "alicloud_nat_gateway": resourceAliyunNatGateway(), + //both subnet and vswith exists,cause compatible old version, and compatible aws habit. + "alicloud_subnet": resourceAliyunSubnet(), + "alicloud_vswitch": resourceAliyunSubnet(), + "alicloud_route_entry": resourceAliyunRouteEntry(), + "alicloud_eip": resourceAliyunEip(), + "alicloud_eip_association": resourceAliyunEipAssociation(), + "alicloud_slb": resourceAliyunSlb(), + "alicloud_slb_attachment": resourceAliyunSlbAttachment(), + }, + + ConfigureFunc: providerConfigure, + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + config := Config{ + AccessKey: d.Get("access_key").(string), + SecretKey: d.Get("secret_key").(string), + Region: common.Region(d.Get("region").(string)), + } + + client, err := config.Client() + if err != nil { + return nil, err + } + + return client, nil +} + +// This is a global MutexKV for use within this plugin. +var alicloudMutexKV = mutexkv.NewMutexKV() + +var descriptions map[string]string + +func init() { + descriptions = map[string]string{ + "access_key": "Access key of alicloud", + "secret_key": "Secret key of alicloud", + "region": "Region of alicloud", + } +} diff --git a/builtin/providers/alicloud/provider_test.go b/builtin/providers/alicloud/provider_test.go new file mode 100644 index 0000000000..f0f5e9becf --- /dev/null +++ b/builtin/providers/alicloud/provider_test.go @@ -0,0 +1,59 @@ +package alicloud + +import ( + "log" + "os" + "testing" + + "fmt" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +var testAccProviders map[string]terraform.ResourceProvider +var testAccProvider *schema.Provider + +func init() { + testAccProvider = Provider().(*schema.Provider) + testAccProviders = map[string]terraform.ResourceProvider{ + "alicloud": testAccProvider, + } +} + +func TestProvider(t *testing.T) { + if err := Provider().(*schema.Provider).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestProvider_impl(t *testing.T) { + var _ terraform.ResourceProvider = Provider() +} + +func testAccPreCheck(t *testing.T) { + if v := os.Getenv("ALICLOUD_ACCESS_KEY"); v == "" { + t.Fatal("ALICLOUD_ACCESS_KEY must be set for acceptance tests") + } + if v := os.Getenv("ALICLOUD_SECRET_KEY"); v == "" { + t.Fatal("ALICLOUD_SECRET_KEY must be set for acceptance tests") + } + if v := os.Getenv("ALICLOUD_REGION"); v == "" { + log.Println("[INFO] Test: Using cn-beijing as test region") + os.Setenv("ALICLOUD_REGION", "cn-beijing") + } +} + +func testAccCheckAlicloudDataSourceID(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Can't find data source: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("data source ID not set") + } + return nil + } +} diff --git a/builtin/providers/alicloud/resource_alicloud_disk.go b/builtin/providers/alicloud/resource_alicloud_disk.go new file mode 100644 index 0000000000..b31c37d937 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_disk.go @@ -0,0 +1,247 @@ +package alicloud + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "log" + "time" +) + +func resourceAliyunDisk() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunDiskCreate, + Read: resourceAliyunDiskRead, + Update: resourceAliyunDiskUpdate, + Delete: resourceAliyunDiskDelete, + + Schema: map[string]*schema.Schema{ + "availability_zone": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateDiskName, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateDiskDescription, + }, + + "category": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateDiskCategory, + Default: "cloud", + }, + + "size": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + }, + + "snapshot_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceAliyunDiskCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + + conn := client.ecsconn + + availabilityZone, err := client.DescribeZone(d.Get("availability_zone").(string)) + if err != nil { + return err + } + + args := &ecs.CreateDiskArgs{ + RegionId: getRegion(d, meta), + ZoneId: availabilityZone.ZoneId, + } + + if v, ok := d.GetOk("category"); ok && v.(string) != "" { + category := ecs.DiskCategory(v.(string)) + if err := client.DiskAvailable(availabilityZone, category); err != nil { + return err + } + args.DiskCategory = category + } + + if v, ok := d.GetOk("size"); ok { + size := v.(int) + if args.DiskCategory == ecs.DiskCategoryCloud && (size < 5 || size > 2000) { + return fmt.Errorf("the size of cloud disk must between 5 to 2000") + } + + if (args.DiskCategory == ecs.DiskCategoryCloudEfficiency || + args.DiskCategory == ecs.DiskCategoryCloudSSD) && (size < 20 || size > 32768) { + return fmt.Errorf("the size of %s disk must between 20 to 32768", args.DiskCategory) + } + args.Size = size + + d.Set("size", args.Size) + } + + if v, ok := d.GetOk("snapshot_id"); ok && v.(string) != "" { + args.SnapshotId = v.(string) + } + + if args.Size <= 0 && args.SnapshotId == "" { + return fmt.Errorf("One of size or snapshot_id is required when specifying an ECS disk.") + } + + if v, ok := d.GetOk("name"); ok && v.(string) != "" { + args.DiskName = v.(string) + } + + if v, ok := d.GetOk("description"); ok && v.(string) != "" { + args.Description = v.(string) + } + + diskID, err := conn.CreateDisk(args) + if err != nil { + return fmt.Errorf("CreateDisk got a error: %#v", err) + } + + d.SetId(diskID) + + return resourceAliyunDiskUpdate(d, meta) +} + +func resourceAliyunDiskRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + disks, _, err := conn.DescribeDisks(&ecs.DescribeDisksArgs{ + RegionId: getRegion(d, meta), + DiskIds: []string{d.Id()}, + }) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error DescribeDiskAttribute: %#v", err) + } + + log.Printf("[DEBUG] DescribeDiskAttribute for instance: %#v", disks) + + if disks == nil || len(disks) <= 0 { + return fmt.Errorf("No disks found.") + } + + disk := disks[0] + d.Set("availability_zone", disk.ZoneId) + d.Set("category", disk.Category) + d.Set("size", disk.Size) + d.Set("status", disk.Status) + d.Set("name", disk.DiskName) + d.Set("description", disk.Description) + d.Set("snapshot_id", disk.SourceSnapshotId) + + tags, _, err := conn.DescribeTags(&ecs.DescribeTagsArgs{ + RegionId: getRegion(d, meta), + ResourceType: ecs.TagResourceDisk, + ResourceId: d.Id(), + }) + + if err != nil { + log.Printf("[DEBUG] DescribeTags for disk got error: %#v", err) + } + + d.Set("tags", tagsToMap(tags)) + + return nil +} + +func resourceAliyunDiskUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + conn := client.ecsconn + + d.Partial(true) + + if err := setTags(client, ecs.TagResourceDisk, d); err != nil { + log.Printf("[DEBUG] Set tags for instance got error: %#v", err) + return fmt.Errorf("Set tags for instance got error: %#v", err) + } else { + d.SetPartial("tags") + } + attributeUpdate := false + args := &ecs.ModifyDiskAttributeArgs{ + DiskId: d.Id(), + } + + if d.HasChange("name") { + d.SetPartial("name") + val := d.Get("name").(string) + args.DiskName = val + + attributeUpdate = true + } + + if d.HasChange("description") { + d.SetPartial("description") + val := d.Get("description").(string) + args.Description = val + + attributeUpdate = true + } + if attributeUpdate { + if err := conn.ModifyDiskAttribute(args); err != nil { + return err + } + } + + d.Partial(false) + + return resourceAliyunDiskRead(d, meta) +} + +func resourceAliyunDiskDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteDisk(d.Id()) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == DiskIncorrectStatus || e.ErrorResponse.Code == DiskCreatingSnapshot { + return resource.RetryableError(fmt.Errorf("Disk in use - trying again while it is deleted.")) + } + } + + disks, _, descErr := conn.DescribeDisks(&ecs.DescribeDisksArgs{ + RegionId: getRegion(d, meta), + DiskIds: []string{d.Id()}, + }) + + if descErr != nil { + log.Printf("[ERROR] Delete disk is failed.") + return resource.NonRetryableError(descErr) + } + if disks == nil || len(disks) < 1 { + return nil + } + + return resource.RetryableError(fmt.Errorf("Disk in use - trying again while it is deleted.")) + }) +} diff --git a/builtin/providers/alicloud/resource_alicloud_disk_attachment.go b/builtin/providers/alicloud/resource_alicloud_disk_attachment.go new file mode 100644 index 0000000000..48a2c2c5c3 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_disk_attachment.go @@ -0,0 +1,176 @@ +package alicloud + +import ( + "fmt" + "strings" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "log" + "time" +) + +func resourceAliyunDiskAttachment() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunDiskAttachmentCreate, + Read: resourceAliyunDiskAttachmentRead, + Delete: resourceAliyunDiskAttachmentDelete, + + Schema: map[string]*schema.Schema{ + "instance_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "disk_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "device_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + } +} + +func resourceAliyunDiskAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + + err := diskAttachment(d, meta) + if err != nil { + return err + } + + d.SetId(d.Get("disk_id").(string) + ":" + d.Get("instance_id").(string)) + + return resourceAliyunDiskAttachmentRead(d, meta) +} + +func resourceAliyunDiskAttachmentRead(d *schema.ResourceData, meta interface{}) error { + diskId, instanceId, err := getDiskIDAndInstanceID(d, meta) + if err != nil { + return err + } + + conn := meta.(*AliyunClient).ecsconn + disks, _, err := conn.DescribeDisks(&ecs.DescribeDisksArgs{ + RegionId: getRegion(d, meta), + InstanceId: instanceId, + DiskIds: []string{diskId}, + }) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error DescribeDiskAttribute: %#v", err) + } + + log.Printf("[DEBUG] DescribeDiskAttribute for instance: %#v", disks) + if disks == nil || len(disks) <= 0 { + return fmt.Errorf("No Disks Found.") + } + + disk := disks[0] + d.Set("instance_id", disk.InstanceId) + d.Set("disk_id", disk.DiskId) + d.Set("device_name", disk.Device) + + return nil +} + +func resourceAliyunDiskAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + diskID, instanceID, err := getDiskIDAndInstanceID(d, meta) + if err != nil { + return err + } + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DetachDisk(instanceID, diskID) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == DiskIncorrectStatus || e.ErrorResponse.Code == InstanceLockedForSecurity { + return resource.RetryableError(fmt.Errorf("Disk in use - trying again while it detaches")) + } + } + + disks, _, descErr := conn.DescribeDisks(&ecs.DescribeDisksArgs{ + RegionId: getRegion(d, meta), + DiskIds: []string{diskID}, + }) + + if descErr != nil { + log.Printf("[ERROR] Disk %s is not detached.", diskID) + return resource.NonRetryableError(err) + } + + for _, disk := range disks { + if disk.Status != ecs.DiskStatusAvailable { + return resource.RetryableError(fmt.Errorf("Disk in use - trying again while it is deleted.")) + } + } + return nil + }) +} + +func getDiskIDAndInstanceID(d *schema.ResourceData, meta interface{}) (string, string, error) { + parts := strings.Split(d.Id(), ":") + + if len(parts) != 2 { + return "", "", fmt.Errorf("invalid resource id") + } + return parts[0], parts[1], nil +} +func diskAttachment(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + diskID := d.Get("disk_id").(string) + instanceID := d.Get("instance_id").(string) + + deviceName := d.Get("device_name").(string) + + args := &ecs.AttachDiskArgs{ + InstanceId: instanceID, + DiskId: diskID, + Device: deviceName, + } + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.AttachDisk(args) + log.Printf("error : %s", err) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == DiskIncorrectStatus || e.ErrorResponse.Code == InstanceIncorrectStatus { + return resource.RetryableError(fmt.Errorf("Disk or Instance status is incorrect - trying again while it attaches")) + } + return resource.NonRetryableError(err) + } + + disks, _, descErr := conn.DescribeDisks(&ecs.DescribeDisksArgs{ + RegionId: getRegion(d, meta), + InstanceId: instanceID, + DiskIds: []string{diskID}, + }) + + if descErr != nil { + log.Printf("[ERROR] Disk %s is not attached.", diskID) + return resource.NonRetryableError(err) + } + + if disks == nil || len(disks) <= 0 { + return resource.RetryableError(fmt.Errorf("Disk in attaching - trying again while it is attached.")) + } + + return nil + + }) +} diff --git a/builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go b/builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go new file mode 100644 index 0000000000..a0fe32a0a1 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go @@ -0,0 +1,154 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "time" +) + +func TestAccAlicloudDiskAttachment(t *testing.T) { + var i ecs.InstanceAttributesType + var v ecs.DiskItemType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_disk_attachment.disk-att", + Providers: testAccProviders, + CheckDestroy: testAccCheckDiskAttachmentDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDiskAttachmentConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.instance", &i), + testAccCheckDiskExists( + "alicloud_disk.disk", &v), + testAccCheckDiskAttachmentExists( + "alicloud_disk_attachment.disk-att", &i, &v), + resource.TestCheckResourceAttr( + "alicloud_disk_attachment.disk-att", + "device_name", + "/dev/xvdb"), + ), + }, + }, + }) + +} + +func testAccCheckDiskAttachmentExists(n string, instance *ecs.InstanceAttributesType, disk *ecs.DiskItemType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Disk ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + + request := &ecs.DescribeDisksArgs{ + RegionId: client.Region, + DiskIds: []string{rs.Primary.Attributes["disk_id"]}, + } + + return resource.Retry(3*time.Minute, func() *resource.RetryError { + response, _, err := conn.DescribeDisks(request) + if response != nil { + for _, d := range response { + if d.Status != ecs.DiskStatusInUse { + return resource.RetryableError(fmt.Errorf("Disk is in attaching - trying again while it attaches")) + } else if d.InstanceId == instance.InstanceId { + // pass + *disk = d + return nil + } + } + } + if err != nil { + return resource.NonRetryableError(err) + } + + return resource.NonRetryableError(fmt.Errorf("Error finding instance/disk")) + }) + } +} + +func testAccCheckDiskAttachmentDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_disk_attachment" { + continue + } + // Try to find the Disk + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + + request := &ecs.DescribeDisksArgs{ + RegionId: client.Region, + DiskIds: []string{rs.Primary.ID}, + } + + response, _, err := conn.DescribeDisks(request) + + for _, disk := range response { + if disk.Status != ecs.DiskStatusAvailable { + return fmt.Errorf("Error ECS Disk Attachment still exist") + } + } + + if err != nil { + // Verify the error is what we want + return err + } + } + + return nil +} + +const testAccDiskAttachmentConfig = ` +resource "alicloud_disk" "disk" { + availability_zone = "cn-beijing-a" + size = "50" + + tags { + Name = "TerraformTest-disk" + } +} + +resource "alicloud_instance" "instance" { + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_type = "ecs.s1.small" + availability_zone = "cn-beijing-a" + security_groups = ["${alicloud_security_group.group.id}"] + instance_name = "hello" + internet_charge_type = "PayByBandwidth" + io_optimized = "none" + + tags { + Name = "TerraformTest-instance" + } +} + +resource "alicloud_disk_attachment" "disk-att" { + disk_id = "${alicloud_disk.disk.id}" + instance_id = "${alicloud_instance.instance.id}" + device_name = "/dev/xvdb" +} + +resource "alicloud_security_group" "group" { + name = "terraform-test-group" + description = "New security group" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_disk_test.go b/builtin/providers/alicloud/resource_alicloud_disk_test.go new file mode 100644 index 0000000000..6cb55bd8ea --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_disk_test.go @@ -0,0 +1,157 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudDisk_basic(t *testing.T) { + var v ecs.DiskItemType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_disk.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDiskDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDiskConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckDiskExists( + "alicloud_disk.foo", &v), + resource.TestCheckResourceAttr( + "alicloud_disk.foo", + "category", + "cloud_efficiency"), + resource.TestCheckResourceAttr( + "alicloud_disk.foo", + "size", + "30"), + ), + }, + }, + }) + +} + +func TestAccAlicloudDisk_withTags(t *testing.T) { + var v ecs.DiskItemType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + //module name + IDRefreshName: "alicloud_disk.bar", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDiskDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDiskConfigWithTags, + Check: resource.ComposeTestCheckFunc( + testAccCheckDiskExists("alicloud_disk.bar", &v), + resource.TestCheckResourceAttr( + "alicloud_disk.bar", + "tags.Name", + "TerraformTest"), + ), + }, + }, + }) +} + +func testAccCheckDiskExists(n string, disk *ecs.DiskItemType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Disk ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + + request := &ecs.DescribeDisksArgs{ + RegionId: client.Region, + DiskIds: []string{rs.Primary.ID}, + } + + response, _, err := conn.DescribeDisks(request) + log.Printf("[WARN] disk ids %#v", rs.Primary.ID) + + if err == nil { + if response != nil && len(response) > 0 { + *disk = response[0] + return nil + } + } + return fmt.Errorf("Error finding ECS Disk %#v", rs.Primary.ID) + } +} + +func testAccCheckDiskDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_disk" { + continue + } + + // Try to find the Disk + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + + request := &ecs.DescribeDisksArgs{ + RegionId: client.Region, + DiskIds: []string{rs.Primary.ID}, + } + + response, _, err := conn.DescribeDisks(request) + + if response != nil && len(response) > 0 { + return fmt.Errorf("Error ECS Disk still exist") + } + + if err != nil { + // Verify the error is what we want + return err + } + } + + return nil +} + +const testAccDiskConfig = ` +resource "alicloud_disk" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + name = "New-disk" + description = "Hello ecs disk." + category = "cloud_efficiency" + size = "30" +} +` +const testAccDiskConfigWithTags = ` +resource "alicloud_disk" "bar" { + # cn-beijing + availability_zone = "cn-beijing-b" + size = "10" + tags { + Name = "TerraformTest" + } +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_eip.go b/builtin/providers/alicloud/resource_alicloud_eip.go new file mode 100644 index 0000000000..8a0329eb88 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_eip.go @@ -0,0 +1,156 @@ +package alicloud + +import ( + "strconv" + + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAliyunEip() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunEipCreate, + Read: resourceAliyunEipRead, + Update: resourceAliyunEipUpdate, + Delete: resourceAliyunEipDelete, + + Schema: map[string]*schema.Schema{ + "bandwidth": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 5, + }, + "internet_charge_type": &schema.Schema{ + Type: schema.TypeString, + Default: "PayByBandwidth", + Optional: true, + ForceNew: true, + ValidateFunc: validateInternetChargeType, + }, + + "ip_address": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "instance": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceAliyunEipCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + args, err := buildAliyunEipArgs(d, meta) + if err != nil { + return err + } + + _, allocationID, err := conn.AllocateEipAddress(args) + if err != nil { + return err + } + + d.SetId(allocationID) + + return resourceAliyunEipRead(d, meta) +} + +func resourceAliyunEipRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + + eip, err := client.DescribeEipAddress(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return err + } + + bandwidth, _ := strconv.Atoi(eip.Bandwidth) + d.Set("bandwidth", bandwidth) + d.Set("internet_charge_type", eip.InternetChargeType) + d.Set("ip_address", eip.IpAddress) + d.Set("status", eip.Status) + + if eip.InstanceId != "" { + d.Set("instance", eip.InstanceId) + } else { + d.Set("instance", "") + } + + return nil +} + +func resourceAliyunEipUpdate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + d.Partial(true) + + if d.HasChange("bandwidth") { + err := conn.ModifyEipAddressAttribute(d.Id(), d.Get("bandwidth").(int)) + if err != nil { + return err + } + + d.SetPartial("bandwidth") + } + + d.Partial(false) + + return nil +} + +func resourceAliyunEipDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.ReleaseEipAddress(d.Id()) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == EipIncorrectStatus { + return resource.RetryableError(fmt.Errorf("EIP in use - trying again while it is deleted.")) + } + } + + args := &ecs.DescribeEipAddressesArgs{ + RegionId: getRegion(d, meta), + AllocationId: d.Id(), + } + + eips, _, descErr := conn.DescribeEipAddresses(args) + if descErr != nil { + return resource.NonRetryableError(descErr) + } else if eips == nil || len(eips) < 1 { + return nil + } + return resource.RetryableError(fmt.Errorf("EIP in use - trying again while it is deleted.")) + }) +} + +func buildAliyunEipArgs(d *schema.ResourceData, meta interface{}) (*ecs.AllocateEipAddressArgs, error) { + + args := &ecs.AllocateEipAddressArgs{ + RegionId: getRegion(d, meta), + Bandwidth: d.Get("bandwidth").(int), + InternetChargeType: common.InternetChargeType(d.Get("internet_charge_type").(string)), + } + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_eip_association.go b/builtin/providers/alicloud/resource_alicloud_eip_association.go new file mode 100644 index 0000000000..a9d419ce15 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_eip_association.go @@ -0,0 +1,131 @@ +package alicloud + +import ( + "fmt" + "strings" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAliyunEipAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunEipAssociationCreate, + Read: resourceAliyunEipAssociationRead, + Delete: resourceAliyunEipAssociationDelete, + + Schema: map[string]*schema.Schema{ + "allocation_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "instance_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + } +} + +func resourceAliyunEipAssociationCreate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + allocationId := d.Get("allocation_id").(string) + instanceId := d.Get("instance_id").(string) + + if err := conn.AssociateEipAddress(allocationId, instanceId); err != nil { + return err + } + + d.SetId(allocationId + ":" + instanceId) + + return resourceAliyunEipAssociationRead(d, meta) +} + +func resourceAliyunEipAssociationRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + + allocationId, instanceId, err := getAllocationIdAndInstanceId(d, meta) + if err != nil { + return err + } + + eip, err := client.DescribeEipAddress(allocationId) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return err + } + + if eip.InstanceId != instanceId { + d.SetId("") + return nil + } + + d.Set("instance_id", eip.InstanceId) + d.Set("allocation_id", allocationId) + return nil +} + +func resourceAliyunEipAssociationDelete(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + allocationId, instanceId, err := getAllocationIdAndInstanceId(d, meta) + if err != nil { + return err + } + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.UnassociateEipAddress(allocationId, instanceId) + + if err != nil { + e, _ := err.(*common.Error) + errCode := e.ErrorResponse.Code + if errCode == InstanceIncorrectStatus || errCode == HaVipIncorrectStatus { + return resource.RetryableError(fmt.Errorf("Eip in use - trying again while make it unassociated.")) + } + } + + args := &ecs.DescribeEipAddressesArgs{ + RegionId: getRegion(d, meta), + AllocationId: allocationId, + } + + eips, _, descErr := conn.DescribeEipAddresses(args) + + if descErr != nil { + return resource.NonRetryableError(descErr) + } else if eips == nil || len(eips) < 1 { + return nil + } + for _, eip := range eips { + if eip.Status != ecs.EipStatusAvailable { + return resource.RetryableError(fmt.Errorf("Eip in use - trying again while make it unassociated.")) + } + } + + return nil + }) +} + +func getAllocationIdAndInstanceId(d *schema.ResourceData, meta interface{}) (string, string, error) { + parts := strings.Split(d.Id(), ":") + + if len(parts) != 2 { + return "", "", fmt.Errorf("invalid resource id") + } + return parts[0], parts[1], nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_eip_association_test.go b/builtin/providers/alicloud/resource_alicloud_eip_association_test.go new file mode 100644 index 0000000000..b039248af8 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_eip_association_test.go @@ -0,0 +1,150 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "time" +) + +func TestAccAlicloudEIPAssociation(t *testing.T) { + var asso ecs.EipAddressSetType + var inst ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_eip_association.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckEIPAssociationDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccEIPAssociationConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.instance", &inst), + testAccCheckEIPExists( + "alicloud_eip.eip", &asso), + testAccCheckEIPAssociationExists( + "alicloud_eip_association.foo", &inst, &asso), + ), + }, + }, + }) + +} + +func testAccCheckEIPAssociationExists(n string, instance *ecs.InstanceAttributesType, eip *ecs.EipAddressSetType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EIP Association ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + return resource.Retry(3*time.Minute, func() *resource.RetryError { + d, err := client.DescribeEipAddress(rs.Primary.Attributes["allocation_id"]) + + if err != nil { + return resource.NonRetryableError(err) + } + + if d != nil { + if d.Status != ecs.EipStatusInUse { + return resource.RetryableError(fmt.Errorf("Eip is in associating - trying again while it associates")) + } else if d.InstanceId == instance.InstanceId { + *eip = *d + return nil + } + } + + return resource.NonRetryableError(fmt.Errorf("EIP Association not found")) + }) + } +} + +func testAccCheckEIPAssociationDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_eip_association" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EIP Association ID is set") + } + + // Try to find the EIP + eips, _, err := client.ecsconn.DescribeEipAddresses(&ecs.DescribeEipAddressesArgs{ + RegionId: client.Region, + AllocationId: rs.Primary.Attributes["allocation_id"], + }) + + for _, eip := range eips { + if eip.Status != ecs.EipStatusAvailable { + return fmt.Errorf("Error EIP Association still exist") + } + } + + // Verify the error is what we want + if err != nil { + return err + } + } + + return nil +} + +const testAccEIPAssociationConfig = ` +resource "alicloud_vpc" "main" { + cidr_block = "10.1.0.0/21" +} + +resource "alicloud_vswitch" "main" { + vpc_id = "${alicloud_vpc.main.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "cn-beijing-a" + depends_on = [ + "alicloud_vpc.main"] +} + +resource "alicloud_instance" "instance" { + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_type = "ecs.s1.small" + availability_zone = "cn-beijing-a" + security_groups = ["${alicloud_security_group.group.id}"] + vswitch_id = "${alicloud_vswitch.main.id}" + instance_name = "hello" + io_optimized = "none" + + tags { + Name = "TerraformTest-instance" + } +} + +resource "alicloud_eip" "eip" { +} + +resource "alicloud_eip_association" "foo" { + allocation_id = "${alicloud_eip.eip.id}" + instance_id = "${alicloud_instance.instance.id}" +} + +resource "alicloud_security_group" "group" { + name = "terraform-test-group" + description = "New security group" + vpc_id = "${alicloud_vpc.main.id}" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_eip_test.go b/builtin/providers/alicloud/resource_alicloud_eip_test.go new file mode 100644 index 0000000000..560f426bab --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_eip_test.go @@ -0,0 +1,131 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudEIP_basic(t *testing.T) { + var eip ecs.EipAddressSetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_eip.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckEIPDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccEIPConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckEIPExists( + "alicloud_eip.foo", &eip), + testAccCheckEIPAttributes(&eip), + ), + }, + resource.TestStep{ + Config: testAccEIPConfigTwo, + Check: resource.ComposeTestCheckFunc( + testAccCheckEIPExists( + "alicloud_eip.foo", &eip), + testAccCheckEIPAttributes(&eip), + resource.TestCheckResourceAttr( + "alicloud_eip.foo", + "bandwidth", + "10"), + ), + }, + }, + }) + +} + +func testAccCheckEIPExists(n string, eip *ecs.EipAddressSetType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EIP ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + d, err := client.DescribeEipAddress(rs.Primary.ID) + + log.Printf("[WARN] eip id %#v", rs.Primary.ID) + + if err != nil { + return err + } + + if d == nil || d.IpAddress == "" { + return fmt.Errorf("EIP not found") + } + + *eip = *d + return nil + } +} + +func testAccCheckEIPAttributes(eip *ecs.EipAddressSetType) resource.TestCheckFunc { + return func(s *terraform.State) error { + if eip.IpAddress == "" { + return fmt.Errorf("Empty Ip address") + } + + return nil + } +} + +func testAccCheckEIPDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_eip" { + continue + } + + // Try to find the EIP + conn := client.ecsconn + + args := &ecs.DescribeEipAddressesArgs{ + RegionId: client.Region, + AllocationId: rs.Primary.ID, + } + d, _, err := conn.DescribeEipAddresses(args) + + if d != nil && len(d) > 0 { + return fmt.Errorf("Error EIP still exist") + } + + // Verify the error is what we want + if err != nil { + return err + } + } + + return nil +} + +const testAccEIPConfig = ` +resource "alicloud_eip" "foo" { +} +` + +const testAccEIPConfigTwo = ` +resource "alicloud_eip" "foo" { + bandwidth = "10" + internet_charge_type = "PayByBandwidth" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_instance.go b/builtin/providers/alicloud/resource_alicloud_instance.go new file mode 100644 index 0000000000..aeac4b3af1 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_instance.go @@ -0,0 +1,534 @@ +package alicloud + +import ( + "fmt" + "log" + + "encoding/base64" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func resourceAliyunInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunInstanceCreate, + Read: resourceAliyunInstanceRead, + Update: resourceAliyunInstanceUpdate, + Delete: resourceAliyunInstanceDelete, + + Schema: map[string]*schema.Schema{ + "availability_zone": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "image_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "instance_type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "security_groups": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + + "allocate_public_ip": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "instance_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "ECS-Instance", + ValidateFunc: validateInstanceName, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateInstanceDescription, + }, + + "instance_network_type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "internet_charge_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateInternetChargeType, + }, + "internet_max_bandwidth_in": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "internet_max_bandwidth_out": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: validateInternetMaxBandWidthOut, + }, + "host_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "password": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "io_optimized": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateIoOptimized, + }, + + "system_disk_category": &schema.Schema{ + Type: schema.TypeString, + Default: "cloud", + Optional: true, + ForceNew: true, + }, + "system_disk_size": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + + //subnet_id and vswitch_id both exists, cause compatible old version, and aws habit. + "subnet_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, //add this schema cause subnet_id not used enter parameter, will different, so will be ForceNew + }, + + "vswitch_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "instance_charge_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateInstanceChargeType, + }, + "period": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + + "public_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "private_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "user_data": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + args, err := buildAliyunInstanceArgs(d, meta) + if err != nil { + return err + } + + instanceID, err := conn.CreateInstance(args) + if err != nil { + return fmt.Errorf("Error creating Aliyun ecs instance: %#v", err) + } + + d.SetId(instanceID) + + d.Set("password", d.Get("password")) + d.Set("system_disk_category", d.Get("system_disk_category")) + + if d.Get("allocate_public_ip").(bool) { + _, err := conn.AllocatePublicIpAddress(d.Id()) + if err != nil { + log.Printf("[DEBUG] AllocatePublicIpAddress for instance got error: %#v", err) + } + } + + // after instance created, its status is pending, + // so we need to wait it become to stopped and then start it + if err := conn.WaitForInstance(d.Id(), ecs.Stopped, defaultTimeout); err != nil { + log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Stopped, err) + } + + if err := conn.StartInstance(d.Id()); err != nil { + return fmt.Errorf("Start instance got error: %#v", err) + } + + if err := conn.WaitForInstance(d.Id(), ecs.Running, defaultTimeout); err != nil { + log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Running, err) + } + + return resourceAliyunInstanceUpdate(d, meta) +} + +func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + conn := client.ecsconn + + instance, err := client.QueryInstancesById(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error DescribeInstanceAttribute: %#v", err) + } + + log.Printf("[DEBUG] DescribeInstanceAttribute for instance: %#v", instance) + + d.Set("instance_name", instance.InstanceName) + d.Set("description", instance.Description) + d.Set("status", instance.Status) + d.Set("availability_zone", instance.ZoneId) + d.Set("host_name", instance.HostName) + d.Set("image_id", instance.ImageId) + d.Set("instance_type", instance.InstanceType) + + // In Classic network, internet_charge_type is valid in any case, and its default value is 'PayByBanwidth'. + // In VPC network, internet_charge_type is valid when instance has public ip, and its default value is 'PayByBanwidth'. + d.Set("internet_charge_type", instance.InternetChargeType) + + if d.Get("allocate_public_ip").(bool) { + d.Set("public_ip", instance.PublicIpAddress.IpAddress[0]) + } + + if ecs.StringOrBool(instance.IoOptimized).Value { + d.Set("io_optimized", "optimized") + } else { + d.Set("io_optimized", "none") + } + + log.Printf("instance.InternetChargeType: %#v", instance.InternetChargeType) + + d.Set("instance_network_type", instance.InstanceNetworkType) + + if d.Get("subnet_id").(string) != "" || d.Get("vswitch_id").(string) != "" { + ipAddress := instance.VpcAttributes.PrivateIpAddress.IpAddress[0] + d.Set("private_ip", ipAddress) + d.Set("subnet_id", instance.VpcAttributes.VSwitchId) + d.Set("vswitch_id", instance.VpcAttributes.VSwitchId) + } else { + ipAddress := strings.Join(ecs.IpAddressSetType(instance.InnerIpAddress).IpAddress, ",") + d.Set("private_ip", ipAddress) + } + + if d.Get("user_data").(string) != "" { + ud, err := conn.DescribeUserdata(&ecs.DescribeUserdataArgs{ + RegionId: getRegion(d, meta), + InstanceId: d.Id(), + }) + + if err != nil { + log.Printf("[ERROR] DescribeUserData for instance got error: %#v", err) + } + d.Set("user_data", userDataHashSum(ud.UserData)) + } + + tags, _, err := conn.DescribeTags(&ecs.DescribeTagsArgs{ + RegionId: getRegion(d, meta), + ResourceType: ecs.TagResourceInstance, + ResourceId: d.Id(), + }) + + if err != nil { + log.Printf("[ERROR] DescribeTags for instance got error: %#v", err) + } + d.Set("tags", tagsToMap(tags)) + + return nil +} + +func resourceAliyunInstanceUpdate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*AliyunClient) + conn := client.ecsconn + + d.Partial(true) + + if err := setTags(client, ecs.TagResourceInstance, d); err != nil { + log.Printf("[DEBUG] Set tags for instance got error: %#v", err) + return fmt.Errorf("Set tags for instance got error: %#v", err) + } else { + d.SetPartial("tags") + } + + attributeUpdate := false + args := &ecs.ModifyInstanceAttributeArgs{ + InstanceId: d.Id(), + } + + if d.HasChange("instance_name") { + log.Printf("[DEBUG] ModifyInstanceAttribute instance_name") + d.SetPartial("instance_name") + args.InstanceName = d.Get("instance_name").(string) + + attributeUpdate = true + } + + if d.HasChange("description") { + log.Printf("[DEBUG] ModifyInstanceAttribute description") + d.SetPartial("description") + args.Description = d.Get("description").(string) + + attributeUpdate = true + } + + if d.HasChange("host_name") { + log.Printf("[DEBUG] ModifyInstanceAttribute host_name") + d.SetPartial("host_name") + args.HostName = d.Get("host_name").(string) + + attributeUpdate = true + } + + passwordUpdate := false + if d.HasChange("password") { + log.Printf("[DEBUG] ModifyInstanceAttribute password") + d.SetPartial("password") + args.Password = d.Get("password").(string) + + attributeUpdate = true + passwordUpdate = true + } + + if attributeUpdate { + if err := conn.ModifyInstanceAttribute(args); err != nil { + return fmt.Errorf("Modify instance attribute got error: %#v", err) + } + } + + if passwordUpdate { + if v, ok := d.GetOk("status"); ok && v.(string) != "" { + if ecs.InstanceStatus(d.Get("status").(string)) == ecs.Running { + log.Printf("[DEBUG] RebootInstance after change password") + if err := conn.RebootInstance(d.Id(), false); err != nil { + return fmt.Errorf("RebootInstance got error: %#v", err) + } + + if err := conn.WaitForInstance(d.Id(), ecs.Running, defaultTimeout); err != nil { + return fmt.Errorf("WaitForInstance got error: %#v", err) + } + } + } + } + + if d.HasChange("security_groups") { + o, n := d.GetChange("security_groups") + os := o.(*schema.Set) + ns := n.(*schema.Set) + + rl := expandStringList(os.Difference(ns).List()) + al := expandStringList(ns.Difference(os).List()) + + if len(al) > 0 { + err := client.JoinSecurityGroups(d.Id(), al) + if err != nil { + return err + } + } + if len(rl) > 0 { + err := client.LeaveSecurityGroups(d.Id(), rl) + if err != nil { + return err + } + } + + d.SetPartial("security_groups") + } + + d.Partial(false) + return resourceAliyunInstanceRead(d, meta) +} + +func resourceAliyunInstanceDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + conn := client.ecsconn + + instance, err := client.QueryInstancesById(d.Id()) + if err != nil { + if notFoundError(err) { + return nil + } + return fmt.Errorf("Error DescribeInstanceAttribute: %#v", err) + } + + if instance.Status != ecs.Stopped { + if err := conn.StopInstance(d.Id(), true); err != nil { + return err + } + + if err := conn.WaitForInstance(d.Id(), ecs.Stopped, defaultTimeout); err != nil { + return err + } + } + + if err := conn.DeleteInstance(d.Id()); err != nil { + return err + } + + return nil +} + +func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateInstanceArgs, error) { + client := meta.(*AliyunClient) + + args := &ecs.CreateInstanceArgs{ + RegionId: getRegion(d, meta), + InstanceType: d.Get("instance_type").(string), + PrivateIpAddress: d.Get("private_ip").(string), + } + + imageID := d.Get("image_id").(string) + + args.ImageId = imageID + + zoneID := d.Get("availability_zone").(string) + + zone, err := client.DescribeZone(zoneID) + if err != nil { + return nil, err + } + + if err := client.ResourceAvailable(zone, ecs.ResourceTypeInstance); err != nil { + return nil, err + } + + args.ZoneId = zoneID + + sgs, ok := d.GetOk("security_groups") + + if ok { + sgList := expandStringList(sgs.(*schema.Set).List()) + sg0 := sgList[0] + // check security group instance exist + _, err := client.DescribeSecurity(sg0) + if err == nil { + args.SecurityGroupId = sg0 + } + + } + + systemDiskCategory := ecs.DiskCategory(d.Get("system_disk_category").(string)) + + if err := client.DiskAvailable(zone, systemDiskCategory); err != nil { + return nil, err + } + + args.SystemDisk = ecs.SystemDiskType{ + Category: systemDiskCategory, + } + + if v := d.Get("instance_name").(string); v != "" { + args.InstanceName = v + } + + if v := d.Get("description").(string); v != "" { + args.Description = v + } + + log.Printf("[DEBUG] internet_charge_type is %s", d.Get("internet_charge_type").(string)) + if v := d.Get("internet_charge_type").(string); v != "" { + args.InternetChargeType = common.InternetChargeType(v) + } + + if v := d.Get("internet_max_bandwidth_out").(int); v != 0 { + args.InternetMaxBandwidthOut = v + } + + if v := d.Get("host_name").(string); v != "" { + args.HostName = v + } + + if v := d.Get("password").(string); v != "" { + args.Password = v + } + + if v := d.Get("io_optimized").(string); v != "" { + args.IoOptimized = ecs.IoOptimized(v) + } + + vswitchValue := d.Get("subnet_id").(string) + if vswitchValue == "" { + vswitchValue = d.Get("vswitch_id").(string) + } + if vswitchValue != "" { + args.VSwitchId = vswitchValue + if d.Get("allocate_public_ip").(bool) && args.InternetMaxBandwidthOut <= 0 { + return nil, fmt.Errorf("Invalid internet_max_bandwidth_out result in allocation public ip failed in the VPC.") + } + } + + if v := d.Get("instance_charge_type").(string); v != "" { + args.InstanceChargeType = common.InstanceChargeType(v) + } + + log.Printf("[DEBUG] period is %d", d.Get("period").(int)) + if v := d.Get("period").(int); v != 0 { + args.Period = v + } else if args.InstanceChargeType == common.PrePaid { + return nil, fmt.Errorf("period is required for instance_charge_type is PrePaid") + } + + if v := d.Get("user_data").(string); v != "" { + args.UserData = v + } + + return args, nil +} + +func userDataHashSum(user_data string) string { + // Check whether the user_data is not Base64 encoded. + // Always calculate hash of base64 decoded value since we + // check against double-encoding when setting it + v, base64DecodeError := base64.StdEncoding.DecodeString(user_data) + if base64DecodeError != nil { + v = []byte(user_data) + } + return string(v) +} diff --git a/builtin/providers/alicloud/resource_alicloud_instance_test.go b/builtin/providers/alicloud/resource_alicloud_instance_test.go new file mode 100644 index 0000000000..2fb232f3cd --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_instance_test.go @@ -0,0 +1,1106 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudInstance_basic(t *testing.T) { + var instance ecs.InstanceAttributesType + + testCheck := func(*terraform.State) error { + log.Printf("[WARN] instances: %#v", instance) + if instance.ZoneId == "" { + return fmt.Errorf("bad availability zone") + } + if len(instance.SecurityGroupIds.SecurityGroupId) == 0 { + return fmt.Errorf("no security group: %#v", instance.SecurityGroupIds.SecurityGroupId) + } + + return nil + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck, + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "internet_charge_type", + "PayByBandwidth"), + ), + }, + + // test for multi steps + resource.TestStep{ + Config: testAccInstanceConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck, + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + ), + }, + }, + }) + +} + +func TestAccAlicloudInstance_vpc(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigVPC, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "system_disk_category", + "cloud_efficiency"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "internet_charge_type", + "PayByTraffic"), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_userData(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigUserData, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "system_disk_category", + "cloud_efficiency"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "internet_charge_type", + "PayByTraffic"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "user_data", + "echo 'net.ipv4.ip_forward=1'>> /etc/sysctl.conf"), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_multipleRegions(t *testing.T) { + var instance ecs.InstanceAttributesType + + // multi provideris + var providers []*schema.Provider + providerFactories := map[string]terraform.ResourceProviderFactory{ + "alicloud": func() (terraform.ResourceProvider, error) { + p := Provider() + providers = append(providers, p.(*schema.Provider)) + return p, nil + }, + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckInstanceDestroyWithProviders(&providers), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigMultipleRegions, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExistsWithProviders( + "alicloud_instance.foo", &instance, &providers), + testAccCheckInstanceExistsWithProviders( + "alicloud_instance.bar", &instance, &providers), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_multiSecurityGroup(t *testing.T) { + var instance ecs.InstanceAttributesType + + testCheck := func(sgCount int) resource.TestCheckFunc { + return func(*terraform.State) error { + if len(instance.SecurityGroupIds.SecurityGroupId) < 0 { + return fmt.Errorf("no security group: %#v", instance.SecurityGroupIds.SecurityGroupId) + } + + if len(instance.SecurityGroupIds.SecurityGroupId) < sgCount { + return fmt.Errorf("less security group: %#v", instance.SecurityGroupIds.SecurityGroupId) + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfig_multiSecurityGroup, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck(2), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + ), + }, + resource.TestStep{ + Config: testAccInstanceConfig_multiSecurityGroup_add, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck(3), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + ), + }, + resource.TestStep{ + Config: testAccInstanceConfig_multiSecurityGroup_remove, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck(1), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + ), + }, + }, + }) + +} + +func TestAccAlicloudInstance_multiSecurityGroupByCount(t *testing.T) { + var instance ecs.InstanceAttributesType + + testCheck := func(sgCount int) resource.TestCheckFunc { + return func(*terraform.State) error { + if len(instance.SecurityGroupIds.SecurityGroupId) < 0 { + return fmt.Errorf("no security group: %#v", instance.SecurityGroupIds.SecurityGroupId) + } + + if len(instance.SecurityGroupIds.SecurityGroupId) < sgCount { + return fmt.Errorf("less security group: %#v", instance.SecurityGroupIds.SecurityGroupId) + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfig_multiSecurityGroupByCount, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + testCheck(2), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "image_id", + "ubuntu_140405_32_40G_cloudinit_20161115.vhd"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "test_foo"), + ), + }, + }, + }) + +} + +func TestAccAlicloudInstance_NetworkInstanceSecurityGroups(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceNetworkInstanceSecurityGroups, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "alicloud_instance.foo", &instance), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_tags(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckInstanceConfigTags, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "tags.foo", + "bar"), + ), + }, + + resource.TestStep{ + Config: testAccCheckInstanceConfigTagsUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "tags.foo", + ""), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "tags.bar", + "zzz"), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_update(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckInstanceConfigOrigin, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "instance_foo"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "host_name", + "host-foo"), + ), + }, + + resource.TestStep{ + Config: testAccCheckInstanceConfigOriginUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "instance_name", + "instance_bar"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "host_name", + "host-bar"), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_privateIP(t *testing.T) { + var instance ecs.InstanceAttributesType + + testCheckPrivateIP := func() resource.TestCheckFunc { + return func(*terraform.State) error { + privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0] + if privateIP != "172.16.0.229" { + return fmt.Errorf("bad private IP: %s", privateIP) + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigPrivateIP, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + testCheckPrivateIP(), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_associatePublicIPAndPrivateIP(t *testing.T) { + var instance ecs.InstanceAttributesType + + testCheckPrivateIP := func() resource.TestCheckFunc { + return func(*terraform.State) error { + privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0] + if privateIP != "172.16.0.229" { + return fmt.Errorf("bad private IP: %s", privateIP) + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigAssociatePublicIPAndPrivateIP, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + testCheckPrivateIP(), + ), + }, + }, + }) +} + +func TestAccAlicloudInstance_vpcRule(t *testing.T) { + var instance ecs.InstanceAttributesType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + IDRefreshName: "alicloud_instance.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccVpcInstanceWithSecurityRule, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("alicloud_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "internet_charge_type", + "PayByBandwidth"), + resource.TestCheckResourceAttr( + "alicloud_instance.foo", + "internet_max_bandwidth_out", + "5"), + ), + }, + }, + }) +} + +func testAccCheckInstanceExists(n string, i *ecs.InstanceAttributesType) resource.TestCheckFunc { + providers := []*schema.Provider{testAccProvider} + return testAccCheckInstanceExistsWithProviders(n, i, &providers) +} + +func testAccCheckInstanceExistsWithProviders(n string, i *ecs.InstanceAttributesType, providers *[]*schema.Provider) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + for _, provider := range *providers { + // Ignore if Meta is empty, this can happen for validation providers + if provider.Meta() == nil { + continue + } + + client := provider.Meta().(*AliyunClient) + instance, err := client.QueryInstancesById(rs.Primary.ID) + log.Printf("[WARN]get ecs instance %#v", instance) + if err == nil && instance != nil { + *i = *instance + return nil + } + + // Verify the error is what we want + e, _ := err.(*common.Error) + if e.ErrorResponse.Message == InstanceNotfound { + continue + } + if err != nil { + return err + + } + } + + return fmt.Errorf("Instance not found") + } +} + +func testAccCheckInstanceDestroy(s *terraform.State) error { + return testAccCheckInstanceDestroyWithProvider(s, testAccProvider) +} + +func testAccCheckInstanceDestroyWithProviders(providers *[]*schema.Provider) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, provider := range *providers { + if provider.Meta() == nil { + continue + } + if err := testAccCheckInstanceDestroyWithProvider(s, provider); err != nil { + return err + } + } + return nil + } +} + +func testAccCheckInstanceDestroyWithProvider(s *terraform.State, provider *schema.Provider) error { + client := provider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_instance" { + continue + } + + // Try to find the resource + instance, err := client.QueryInstancesById(rs.Primary.ID) + if err == nil { + if instance.Status != "" && instance.Status != "Stopped" { + return fmt.Errorf("Found unstopped instance: %s", instance.InstanceId) + } + } + + // Verify the error is what we want + e, _ := err.(*common.Error) + if e.ErrorResponse.Message == InstanceNotfound { + continue + } + + return err + } + + return nil +} + +const testAccInstanceConfig = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_security_group" "tf_test_bar" { + name = "tf_test_bar" + description = "bar" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + system_disk_category = "cloud_ssd" + + instance_type = "ecs.n1.small" + internet_charge_type = "PayByBandwidth" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + io_optimized = "optimized" + + tags { + foo = "bar" + work = "test" + } +} +` +const testAccInstanceConfigVPC = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + vswitch_id = "${alicloud_vswitch.foo.id}" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + + internet_charge_type = "PayByTraffic" + internet_max_bandwidth_out = 5 + allocate_public_ip = true + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" +} + +` + +const testAccInstanceConfigUserData = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + vswitch_id = "${alicloud_vswitch.foo.id}" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + internet_charge_type = "PayByTraffic" + internet_max_bandwidth_out = 5 + allocate_public_ip = true + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + user_data = "echo 'net.ipv4.ip_forward=1'>> /etc/sysctl.conf" +} +` + +const testAccInstanceConfigMultipleRegions = ` +provider "alicloud" { + alias = "beijing" + region = "cn-beijing" +} + +provider "alicloud" { + alias = "shanghai" + region = "cn-shanghai" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + provider = "alicloud.beijing" + description = "foo" +} + +resource "alicloud_security_group" "tf_test_bar" { + name = "tf_test_bar" + provider = "alicloud.shanghai" + description = "bar" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + provider = "alicloud.beijing" + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + internet_charge_type = "PayByBandwidth" + + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" +} + +resource "alicloud_instance" "bar" { + # cn-shanghai + provider = "alicloud.shanghai" + availability_zone = "cn-shanghai-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + internet_charge_type = "PayByBandwidth" + + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + security_groups = ["${alicloud_security_group.tf_test_bar.id}"] + instance_name = "test_bar" +} +` + +const testAccInstanceConfig_multiSecurityGroup = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_security_group" "tf_test_bar" { + name = "tf_test_bar" + description = "bar" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + instance_type = "ecs.s2.large" + internet_charge_type = "PayByBandwidth" + security_groups = ["${alicloud_security_group.tf_test_foo.id}", "${alicloud_security_group.tf_test_bar.id}"] + instance_name = "test_foo" + io_optimized = "optimized" +}` + +const testAccInstanceConfig_multiSecurityGroup_add = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_security_group" "tf_test_bar" { + name = "tf_test_bar" + description = "bar" +} + +resource "alicloud_security_group" "tf_test_add_sg" { + name = "tf_test_add_sg" + description = "sg" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + instance_type = "ecs.s2.large" + internet_charge_type = "PayByBandwidth" + security_groups = ["${alicloud_security_group.tf_test_foo.id}", "${alicloud_security_group.tf_test_bar.id}", + "${alicloud_security_group.tf_test_add_sg.id}"] + instance_name = "test_foo" + io_optimized = "optimized" +} +` + +const testAccInstanceConfig_multiSecurityGroup_remove = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + instance_type = "ecs.s2.large" + internet_charge_type = "PayByBandwidth" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + io_optimized = "optimized" +} +` + +const testAccInstanceConfig_multiSecurityGroupByCount = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + count = 2 + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + instance_type = "ecs.s2.large" + internet_charge_type = "PayByBandwidth" + security_groups = ["${alicloud_security_group.tf_test_foo.*.id}"] + instance_name = "test_foo" + io_optimized = "none" +} +` + +const testAccInstanceNetworkInstanceSecurityGroups = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + vswitch_id = "${alicloud_vswitch.foo.id}" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + + internet_max_bandwidth_out = 5 + allocate_public_ip = "true" + internet_charge_type = "PayByBandwidth" +} +` +const testAccCheckInstanceConfigTags = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + internet_charge_type = "PayByBandwidth" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + + tags { + foo = "bar" + } +} +` + +const testAccCheckInstanceConfigTagsUpdate = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + internet_charge_type = "PayByBandwidth" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" + + tags { + bar = "zzz" + } +} +` +const testAccCheckInstanceConfigOrigin = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + internet_charge_type = "PayByBandwidth" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + instance_name = "instance_foo" + host_name = "host-foo" +} +` + +const testAccCheckInstanceConfigOriginUpdate = ` +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + internet_charge_type = "PayByBandwidth" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + instance_name = "instance_bar" + host_name = "host-bar" +} +` + +const testAccInstanceConfigPrivateIP = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/24" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + vswitch_id = "${alicloud_vswitch.foo.id}" + private_ip = "172.16.0.229" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" +} +` +const testAccInstanceConfigAssociatePublicIPAndPrivateIP = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/24" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + vswitch_id = "${alicloud_vswitch.foo.id}" + private_ip = "172.16.0.229" + allocate_public_ip = "true" + internet_max_bandwidth_out = 5 + internet_charge_type = "PayByBandwidth" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" +} +` +const testAccVpcInstanceWithSecurityRule = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "10.1.0.0/21" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "cn-beijing-c" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_security_group_rule" "ingress" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-c" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + vswitch_id = "${alicloud_vswitch.foo.id}" + allocate_public_ip = true + + # series II + instance_charge_type = "PostPaid" + instance_type = "ecs.n1.small" + internet_charge_type = "PayByBandwidth" + internet_max_bandwidth_out = 5 + + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" + io_optimized = "optimized" +} + +` diff --git a/builtin/providers/alicloud/resource_alicloud_nat_gateway.go b/builtin/providers/alicloud/resource_alicloud_nat_gateway.go new file mode 100644 index 0000000000..51622d86eb --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_nat_gateway.go @@ -0,0 +1,282 @@ +package alicloud + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "log" + "strings" + "time" +) + +func resourceAliyunNatGateway() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunNatGatewayCreate, + Read: resourceAliyunNatGatewayRead, + Update: resourceAliyunNatGatewayUpdate, + Delete: resourceAliyunNatGatewayDelete, + + Schema: map[string]*schema.Schema{ + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "spec": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "bandwidth_package_ids": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "bandwidth_packages": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip_count": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + "bandwidth": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + "zone": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Required: true, + MaxItems: 4, + }, + }, + } +} + +func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).vpcconn + + args := &CreateNatGatewayArgs{ + RegionId: getRegion(d, meta), + VpcId: d.Get("vpc_id").(string), + Spec: d.Get("spec").(string), + } + + bandwidthPackages := d.Get("bandwidth_packages").([]interface{}) + + bandwidthPackageTypes := []BandwidthPackageType{} + + for _, e := range bandwidthPackages { + pack := e.(map[string]interface{}) + bandwidthPackage := BandwidthPackageType{ + IpCount: pack["ip_count"].(int), + Bandwidth: pack["bandwidth"].(int), + } + if pack["zone"].(string) != "" { + bandwidthPackage.Zone = pack["zone"].(string) + } + + bandwidthPackageTypes = append(bandwidthPackageTypes, bandwidthPackage) + } + + args.BandwidthPackage = bandwidthPackageTypes + + var name string + if v, ok := d.GetOk("name"); ok { + name = v.(string) + } + + args.Name = name + + if v, ok := d.GetOk("description"); ok { + args.Description = v.(string) + } + + resp, err := CreateNatGateway(conn, args) + if err != nil { + return fmt.Errorf("CreateNatGateway got error: %#v", err) + } + + d.SetId(resp.NatGatewayId) + + return resourceAliyunNatGatewayRead(d, meta) +} + +func resourceAliyunNatGatewayRead(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*AliyunClient) + + natGateway, err := client.DescribeNatGateway(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return err + } + + d.Set("name", natGateway.Name) + d.Set("spec", natGateway.Spec) + d.Set("bandwidth_package_ids", strings.Join(natGateway.BandwidthPackageIds.BandwidthPackageId, ",")) + d.Set("description", natGateway.Description) + d.Set("vpc_id", natGateway.VpcId) + + return nil +} + +func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*AliyunClient) + + natGateway, err := client.DescribeNatGateway(d.Id()) + if err != nil { + return err + } + + d.Partial(true) + attributeUpdate := false + args := &ModifyNatGatewayAttributeArgs{ + RegionId: natGateway.RegionId, + NatGatewayId: natGateway.NatGatewayId, + } + + if d.HasChange("name") { + d.SetPartial("name") + var name string + if v, ok := d.GetOk("name"); ok { + name = v.(string) + } else { + return fmt.Errorf("cann't change name to empty string") + } + args.Name = name + + attributeUpdate = true + } + + if d.HasChange("description") { + d.SetPartial("description") + var description string + if v, ok := d.GetOk("description"); ok { + description = v.(string) + } else { + return fmt.Errorf("can to change description to empty string") + } + + args.Description = description + + attributeUpdate = true + } + + if attributeUpdate { + if err := ModifyNatGatewayAttribute(client.vpcconn, args); err != nil { + return err + } + } + + if d.HasChange("spec") { + d.SetPartial("spec") + var spec NatGatewaySpec + if v, ok := d.GetOk("spec"); ok { + spec = NatGatewaySpec(v.(string)) + } else { + // set default to small spec + spec = NatGatewaySmallSpec + } + + args := &ModifyNatGatewaySpecArgs{ + RegionId: natGateway.RegionId, + NatGatewayId: natGateway.NatGatewayId, + Spec: spec, + } + + err := ModifyNatGatewaySpec(client.vpcconn, args) + if err != nil { + return fmt.Errorf("%#v %#v", err, *args) + } + + } + d.Partial(false) + + return resourceAliyunNatGatewayRead(d, meta) +} + +func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*AliyunClient) + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + + packages, err := DescribeBandwidthPackages(client.vpcconn, &DescribeBandwidthPackagesArgs{ + RegionId: getRegion(d, meta), + NatGatewayId: d.Id(), + }) + if err != nil { + log.Printf("[ERROR] Describe bandwidth package is failed, natGateway Id: %s", d.Id()) + return resource.NonRetryableError(err) + } + + retry := false + for _, pack := range packages { + err = DeleteBandwidthPackage(client.vpcconn, &DeleteBandwidthPackageArgs{ + RegionId: getRegion(d, meta), + BandwidthPackageId: pack.BandwidthPackageId, + }) + + if err != nil { + er, _ := err.(*common.Error) + if er.ErrorResponse.Code == NatGatewayInvalidRegionId { + log.Printf("[ERROR] Delete bandwidth package is failed, bandwidthPackageId: %#v", pack.BandwidthPackageId) + return resource.NonRetryableError(err) + } + retry = true + } + } + + if retry { + return resource.RetryableError(fmt.Errorf("Bandwidth package in use - trying again while it is deleted.")) + } + + args := &DeleteNatGatewayArgs{ + RegionId: client.Region, + NatGatewayId: d.Id(), + } + + err = DeleteNatGateway(client.vpcconn, args) + if err != nil { + er, _ := err.(*common.Error) + if er.ErrorResponse.Code == DependencyViolationBandwidthPackages { + return resource.RetryableError(fmt.Errorf("NatGateway in use - trying again while it is deleted.")) + } + } + + describeArgs := &DescribeNatGatewaysArgs{ + RegionId: client.Region, + NatGatewayId: d.Id(), + } + gw, _, gwErr := DescribeNatGateways(client.vpcconn, describeArgs) + + if gwErr != nil { + log.Printf("[ERROR] Describe NatGateways failed.") + return resource.NonRetryableError(gwErr) + } else if gw == nil || len(gw) < 1 { + return nil + } + + return resource.RetryableError(fmt.Errorf("NatGateway in use - trying again while it is deleted.")) + }) +} diff --git a/builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go b/builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go new file mode 100644 index 0000000000..ad8fba1662 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go @@ -0,0 +1,241 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "testing" +) + +func TestAccAlicloudNatGateway_basic(t *testing.T) { + var nat NatGatewaySetType + + testCheck := func(*terraform.State) error { + if nat.BusinessStatus != "Normal" { + return fmt.Errorf("abnormal instance status") + } + + if len(nat.BandwidthPackageIds.BandwidthPackageId) == 0 { + return fmt.Errorf("no bandwidth package: %#v", nat.BandwidthPackageIds.BandwidthPackageId) + } + + return nil + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_nat_gateway.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckNatGatewayDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNatGatewayConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckNatGatewayExists( + "alicloud_nat_gateway.foo", &nat), + testCheck, + resource.TestCheckResourceAttr( + "alicloud_nat_gateway.foo", + "spec", + "Small"), + resource.TestCheckResourceAttr( + "alicloud_nat_gateway.foo", + "name", + "test_foo"), + ), + }, + }, + }) + +} + +func TestAccAlicloudNatGateway_spec(t *testing.T) { + var nat NatGatewaySetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_nat_gateway.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckNatGatewayDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNatGatewayConfigSpec, + Check: resource.ComposeTestCheckFunc( + testAccCheckNatGatewayExists( + "alicloud_nat_gateway.foo", &nat), + resource.TestCheckResourceAttr( + "alicloud_nat_gateway.foo", + "spec", + "Middle"), + ), + }, + + resource.TestStep{ + Config: testAccNatGatewayConfigSpecUpgrade, + Check: resource.ComposeTestCheckFunc( + testAccCheckNatGatewayExists( + "alicloud_nat_gateway.foo", &nat), + resource.TestCheckResourceAttr( + "alicloud_nat_gateway.foo", + "spec", + "Large"), + ), + }, + }, + }) + +} + +func testAccCheckNatGatewayExists(n string, nat *NatGatewaySetType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Gateway ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + instance, err := client.DescribeNatGateway(rs.Primary.ID) + + if err != nil { + return err + } + if instance == nil { + return fmt.Errorf("Nat gateway not found") + } + + *nat = *instance + return nil + } +} + +func testAccCheckNatGatewayDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_nat_gateway" { + continue + } + + // Try to find the Nat gateway + instance, err := client.DescribeNatGateway(rs.Primary.ID) + + if instance != nil { + return fmt.Errorf("Nat gateway still exist") + } + + if err != nil { + // Verify the error is what we want + e, _ := err.(*common.Error) + + if !notFoundError(e) { + return err + } + } + + } + + return nil +} + +const testAccNatGatewayConfig = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_nat_gateway" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + spec = "Small" + name = "test_foo" + bandwidth_packages = [{ + ip_count = 1 + bandwidth = 5 + zone = "cn-beijing-b" + }, { + ip_count = 2 + bandwidth = 10 + zone = "cn-beijing-b" + }] + depends_on = [ + "alicloud_vswitch.foo"] +} +` + +const testAccNatGatewayConfigSpec = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_nat_gateway" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + spec = "Middle" + name = "test_foo" + bandwidth_packages = [{ + ip_count = 1 + bandwidth = 5 + zone = "cn-beijing-b" + }, { + ip_count = 2 + bandwidth = 10 + zone = "cn-beijing-b" + }] + depends_on = [ + "alicloud_vswitch.foo"] +} +` + +const testAccNatGatewayConfigSpecUpgrade = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_nat_gateway" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + spec = "Large" + name = "test_foo" + bandwidth_packages = [{ + ip_count = 1 + bandwidth = 5 + zone = "cn-beijing-b" + }, { + ip_count = 2 + bandwidth = 10 + zone = "cn-beijing-b" + }] + depends_on = [ + "alicloud_vswitch.foo"] +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_security_group.go b/builtin/providers/alicloud/resource_alicloud_security_group.go new file mode 100644 index 0000000000..f21ae4b270 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_security_group.go @@ -0,0 +1,169 @@ +package alicloud + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAliyunSecurityGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunSecurityGroupCreate, + Read: resourceAliyunSecurityGroupRead, + Update: resourceAliyunSecurityGroupUpdate, + Delete: resourceAliyunSecurityGroupDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateSecurityGroupName, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateSecurityGroupDescription, + }, + + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceAliyunSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + args, err := buildAliyunSecurityGroupArgs(d, meta) + if err != nil { + return err + } + + securityGroupID, err := conn.CreateSecurityGroup(args) + if err != nil { + return err + } + + d.SetId(securityGroupID) + + return resourceAliyunSecurityGroupRead(d, meta) +} + +func resourceAliyunSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + args := &ecs.DescribeSecurityGroupAttributeArgs{ + SecurityGroupId: d.Id(), + RegionId: getRegion(d, meta), + } + + sg, err := conn.DescribeSecurityGroupAttribute(args) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error DescribeSecurityGroupAttribute: %#v", err) + } + + d.Set("name", sg.SecurityGroupName) + d.Set("description", sg.Description) + + return nil +} + +func resourceAliyunSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + d.Partial(true) + attributeUpdate := false + args := &ecs.ModifySecurityGroupAttributeArgs{ + SecurityGroupId: d.Id(), + RegionId: getRegion(d, meta), + } + + if d.HasChange("name") { + d.SetPartial("name") + args.SecurityGroupName = d.Get("name").(string) + + attributeUpdate = true + } + + if d.HasChange("description") { + d.SetPartial("description") + args.Description = d.Get("description").(string) + + attributeUpdate = true + } + if attributeUpdate { + if err := conn.ModifySecurityGroupAttribute(args); err != nil { + return err + } + } + + return nil +} + +func resourceAliyunSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteSecurityGroup(getRegion(d, meta), d.Id()) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == SgDependencyViolation { + return resource.RetryableError(fmt.Errorf("Security group in use - trying again while it is deleted.")) + } + } + + sg, err := conn.DescribeSecurityGroupAttribute(&ecs.DescribeSecurityGroupAttributeArgs{ + RegionId: getRegion(d, meta), + SecurityGroupId: d.Id(), + }) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == InvalidSecurityGroupIdNotFound { + return nil + } + return resource.NonRetryableError(err) + } else if sg == nil { + return nil + } + + return resource.RetryableError(fmt.Errorf("Security group in use - trying again while it is deleted.")) + }) +} + +func buildAliyunSecurityGroupArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateSecurityGroupArgs, error) { + + args := &ecs.CreateSecurityGroupArgs{ + RegionId: getRegion(d, meta), + } + + if v := d.Get("name").(string); v != "" { + args.SecurityGroupName = v + } + + if v := d.Get("description").(string); v != "" { + args.Description = v + } + + if v := d.Get("vpc_id").(string); v != "" { + args.VpcId = v + } + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_security_group_rule.go b/builtin/providers/alicloud/resource_alicloud_security_group_rule.go new file mode 100644 index 0000000000..4627d8e2b6 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_security_group_rule.go @@ -0,0 +1,285 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "log" + "strings" +) + +func resourceAliyunSecurityGroupRule() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunSecurityGroupRuleCreate, + Read: resourceAliyunSecurityGroupRuleRead, + Delete: resourceAliyunSecurityGroupRuleDelete, + + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateSecurityRuleType, + Description: "Type of rule, ingress (inbound) or egress (outbound).", + }, + + "ip_protocol": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateSecurityRuleIpProtocol, + }, + + "nic_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateSecurityRuleNicType, + }, + + "policy": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateSecurityRulePolicy, + }, + + "port_range": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "priority": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: validateSecurityPriority, + }, + + "security_group_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "cidr_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "0.0.0.0/0", + }, + + "source_security_group_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "source_group_owner_account": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + ruleType := d.Get("type").(string) + sgId := d.Get("security_group_id").(string) + ptl := d.Get("ip_protocol").(string) + port := d.Get("port_range").(string) + + var autherr error + switch GroupRuleDirection(ruleType) { + case GroupRuleIngress: + args, err := buildAliyunSecurityIngressArgs(d, meta) + if err != nil { + return err + } + autherr = conn.AuthorizeSecurityGroup(args) + case GroupRuleEgress: + args, err := buildAliyunSecurityEgressArgs(d, meta) + if err != nil { + return err + } + autherr = conn.AuthorizeSecurityGroupEgress(args) + default: + return fmt.Errorf("Security Group Rule must be type 'ingress' or type 'egress'") + } + + if autherr != nil { + return fmt.Errorf( + "Error authorizing security group rule type %s: %s", + ruleType, autherr) + } + + d.SetId(sgId + ":" + ruleType + ":" + ptl + ":" + port) + return resourceAliyunSecurityGroupRuleRead(d, meta) +} + +func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + parts := strings.Split(d.Id(), ":") + sgId := parts[0] + types := parts[1] + ip_protocol := parts[2] + port_range := parts[3] + rule, err := client.DescribeSecurityGroupRule(sgId, types, ip_protocol, port_range) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error SecurityGroup rule: %#v", err) + } + log.Printf("[WARN]sg %s, type %s, protocol %s, port %s, rule %#v", sgId, types, ip_protocol, port_range, rule) + d.Set("type", rule.Direction) + d.Set("ip_protocol", strings.ToLower(string(rule.IpProtocol))) + d.Set("nic_type", rule.NicType) + d.Set("policy", strings.ToLower(string(rule.Policy))) + d.Set("port_range", rule.PortRange) + d.Set("priority", rule.Priority) + d.Set("security_group_id", sgId) + //support source and desc by type + if GroupRuleDirection(types) == GroupRuleIngress { + d.Set("cidr_ip", rule.SourceCidrIp) + d.Set("source_security_group_id", rule.SourceGroupId) + d.Set("source_group_owner_account", rule.SourceGroupOwnerAccount) + } else { + d.Set("cidr_ip", rule.DestCidrIp) + d.Set("source_security_group_id", rule.DestGroupId) + d.Set("source_group_owner_account", rule.DestGroupOwnerAccount) + } + + return nil +} + +func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + args, err := buildAliyunSecurityIngressArgs(d, meta) + + if err != nil { + return err + } + revokeArgs := &ecs.RevokeSecurityGroupArgs{ + AuthorizeSecurityGroupArgs: *args, + } + return client.RevokeSecurityGroup(revokeArgs) +} + +func buildAliyunSecurityIngressArgs(d *schema.ResourceData, meta interface{}) (*ecs.AuthorizeSecurityGroupArgs, error) { + conn := meta.(*AliyunClient).ecsconn + + args := &ecs.AuthorizeSecurityGroupArgs{ + RegionId: getRegion(d, meta), + } + + if v := d.Get("ip_protocol").(string); v != "" { + args.IpProtocol = ecs.IpProtocol(v) + } + + if v := d.Get("port_range").(string); v != "" { + args.PortRange = v + } + + if v := d.Get("policy").(string); v != "" { + args.Policy = ecs.PermissionPolicy(v) + } + + if v := d.Get("priority").(int); v != 0 { + args.Priority = v + } + + if v := d.Get("nic_type").(string); v != "" { + args.NicType = ecs.NicType(v) + } + + if v := d.Get("cidr_ip").(string); v != "" { + args.SourceCidrIp = v + } + + if v := d.Get("source_security_group_id").(string); v != "" { + args.SourceGroupId = v + } + + if v := d.Get("source_group_owner_account").(string); v != "" { + args.SourceGroupOwnerAccount = v + } + + sgId := d.Get("security_group_id").(string) + + sgArgs := &ecs.DescribeSecurityGroupAttributeArgs{ + SecurityGroupId: sgId, + RegionId: getRegion(d, meta), + } + + _, err := conn.DescribeSecurityGroupAttribute(sgArgs) + if err != nil { + return nil, fmt.Errorf("Error get security group %s error: %#v", sgId, err) + } + + args.SecurityGroupId = sgId + + return args, nil +} + +func buildAliyunSecurityEgressArgs(d *schema.ResourceData, meta interface{}) (*ecs.AuthorizeSecurityGroupEgressArgs, error) { + conn := meta.(*AliyunClient).ecsconn + + args := &ecs.AuthorizeSecurityGroupEgressArgs{ + RegionId: getRegion(d, meta), + } + + if v := d.Get("ip_protocol").(string); v != "" { + args.IpProtocol = ecs.IpProtocol(v) + } + + if v := d.Get("port_range").(string); v != "" { + args.PortRange = v + } + + if v := d.Get("policy").(string); v != "" { + args.Policy = ecs.PermissionPolicy(v) + } + + if v := d.Get("priority").(int); v != 0 { + args.Priority = v + } + + if v := d.Get("nic_type").(string); v != "" { + args.NicType = ecs.NicType(v) + } + + if v := d.Get("cidr_ip").(string); v != "" { + args.DestCidrIp = v + } + + if v := d.Get("source_security_group_id").(string); v != "" { + args.DestGroupId = v + } + + if v := d.Get("source_group_owner_account").(string); v != "" { + args.DestGroupOwnerAccount = v + } + + sgId := d.Get("security_group_id").(string) + + sgArgs := &ecs.DescribeSecurityGroupAttributeArgs{ + SecurityGroupId: sgId, + RegionId: getRegion(d, meta), + } + + _, err := conn.DescribeSecurityGroupAttribute(sgArgs) + if err != nil { + return nil, fmt.Errorf("Error get security group %s error: %#v", sgId, err) + } + + args.SecurityGroupId = sgId + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go b/builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go new file mode 100644 index 0000000000..7eb267fcba --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go @@ -0,0 +1,263 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" + "strings" + "testing" +) + +func TestAccAlicloudSecurityGroupRule_Ingress(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.ingress", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRuleIngress, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "priority", + "1"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "nic_type", + "internet"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "ip_protocol", + "tcp"), + ), + }, + }, + }) + +} + +func TestAccAlicloudSecurityGroupRule_Egress(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.egress", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRuleEgress, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.egress", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.egress", + "port_range", + "80/80"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.egress", + "ip_protocol", + "udp"), + ), + }, + }, + }) + +} + +func TestAccAlicloudSecurityGroupRule_Vpc_Ingress(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.ingress", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRuleVpcIngress, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "port_range", + "1/200"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "ip_protocol", + "udp"), + ), + }, + }, + }) + +} + +func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No SecurityGroup Rule ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + log.Printf("[WARN]get sg rule %s", rs.Primary.ID) + parts := strings.Split(rs.Primary.ID, ":") + rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3]) + + if err != nil { + return err + } + + if rule == nil { + return fmt.Errorf("SecurityGroup not found") + } + + *m = *rule + return nil + } +} + +func testAccCheckSecurityGroupRuleDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_security_group_rule" { + continue + } + + parts := strings.Split(rs.Primary.ID, ":") + rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3]) + + if rule != nil { + return fmt.Errorf("Error SecurityGroup Rule still exist") + } + + // Verify the error is what we want + if err != nil { + // Verify the error is what we want + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == InvalidSecurityGroupIdNotFound { + continue + } + return err + } + } + + return nil +} + +const testAccSecurityGroupRuleIngress = ` +resource "alicloud_security_group" "foo" { + name = "sg_foo" +} + +resource "alicloud_security_group_rule" "ingress" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "1/200" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "10.159.6.18/12" +} + + +` + +const testAccSecurityGroupRuleEgress = ` +resource "alicloud_security_group" "foo" { + name = "sg_foo" +} + + +resource "alicloud_security_group_rule" "egress" { + type = "egress" + ip_protocol = "udp" + nic_type = "internet" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "10.159.6.18/12" +} + +` + +const testAccSecurityGroupRuleVpcIngress = ` +resource "alicloud_security_group" "foo" { + vpc_id = "${alicloud_vpc.vpc.id}" + name = "sg_foo" +} + +resource "alicloud_vpc" "vpc" { + cidr_block = "10.1.0.0/21" +} + +resource "alicloud_security_group_rule" "ingress" { + type = "ingress" + ip_protocol = "udp" + nic_type = "intranet" + policy = "accept" + port_range = "1/200" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "10.159.6.18/12" +} + +` + +const testAccSecurityGroupRuleMultiIngress = ` +resource "alicloud_security_group" "foo" { + name = "sg_foo" +} + +resource "alicloud_security_group_rule" "ingress1" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "1/200" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "10.159.6.18/12" +} + +resource "alicloud_security_group_rule" "ingress2" { + type = "ingress" + ip_protocol = "gre" + nic_type = "internet" + policy = "accept" + port_range = "-1/-1" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "127.0.1.18/16" +} + +` diff --git a/builtin/providers/alicloud/resource_alicloud_security_group_test.go b/builtin/providers/alicloud/resource_alicloud_security_group_test.go new file mode 100644 index 0000000000..19211bc1fd --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_security_group_test.go @@ -0,0 +1,151 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudSecurityGroup_basic(t *testing.T) { + var sg ecs.DescribeSecurityGroupAttributeResponse + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupExists( + "alicloud_security_group.foo", &sg), + resource.TestCheckResourceAttr( + "alicloud_security_group.foo", + "name", + "sg_test"), + ), + }, + }, + }) + +} + +func TestAccAlicloudSecurityGroup_withVpc(t *testing.T) { + var sg ecs.DescribeSecurityGroupAttributeResponse + var vpc ecs.VpcSetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupConfig_withVpc, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupExists( + "alicloud_security_group.foo", &sg), + testAccCheckVpcExists( + "alicloud_vpc.vpc", &vpc), + ), + }, + }, + }) + +} + +func testAccCheckSecurityGroupExists(n string, sg *ecs.DescribeSecurityGroupAttributeResponse) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No SecurityGroup ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + args := &ecs.DescribeSecurityGroupAttributeArgs{ + RegionId: client.Region, + SecurityGroupId: rs.Primary.ID, + } + d, err := conn.DescribeSecurityGroupAttribute(args) + + log.Printf("[WARN] security group id %#v", rs.Primary.ID) + + if err != nil { + return err + } + + if d == nil { + return fmt.Errorf("SecurityGroup not found") + } + + *sg = *d + return nil + } +} + +func testAccCheckSecurityGroupDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ecsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_security_group" { + continue + } + + // Try to find the SecurityGroup + args := &ecs.DescribeSecurityGroupsArgs{ + RegionId: client.Region, + } + + groups, _, err := conn.DescribeSecurityGroups(args) + + for _, sg := range groups { + if sg.SecurityGroupId == rs.Primary.ID { + return fmt.Errorf("Error SecurityGroup still exist") + } + } + + // Verify the error is what we want + if err != nil { + return err + } + } + + return nil +} + +const testAccSecurityGroupConfig = ` +resource "alicloud_security_group" "foo" { + name = "sg_test" +} +` + +const testAccSecurityGroupConfig_withVpc = ` +resource "alicloud_security_group" "foo" { + vpc_id = "${alicloud_vpc.vpc.id}" +} + +resource "alicloud_vpc" "vpc" { + cidr_block = "10.1.0.0/21" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_slb.go b/builtin/providers/alicloud/resource_alicloud_slb.go new file mode 100644 index 0000000000..8bc787b3d6 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_slb.go @@ -0,0 +1,420 @@ +package alicloud + +import ( + "bytes" + "fmt" + "strings" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/slb" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAliyunSlb() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunSlbCreate, + Read: resourceAliyunSlbRead, + Update: resourceAliyunSlbUpdate, + Delete: resourceAliyunSlbDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateSlbName, + Computed: true, + }, + + "internet": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "vswitch_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "internet_charge_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "paybytraffic", + ValidateFunc: validateSlbInternetChargeType, + }, + + "bandwidth": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateSlbBandwidth, + Computed: true, + }, + + "listener": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "instance_port": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateInstancePort, + Required: true, + }, + + "lb_port": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateInstancePort, + Required: true, + }, + + "lb_protocol": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateInstanceProtocol, + Required: true, + }, + + "bandwidth": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateSlbListenerBandwidth, + Required: true, + }, + //http + "scheduler": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSlbListenerScheduler, + Optional: true, + Default: "wrr", + }, + + "sticky_session": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSlbListenerStickySession, + Optional: true, + }, + "sticky_session_type": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSlbListenerStickySessionType, + Optional: true, + }, + "cookie": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSlbListenerCookie, + Optional: true, + }, + "PersistenceTimeout": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateSlbListenerPersistenceTimeout, + Optional: true, + Default: 0, + }, + //https + "ssl_certificate_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Set: resourceAliyunSlbListenerHash, + }, + + //deprecated + "instances": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Set: schema.HashString, + }, + + "address": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAliyunSlbCreate(d *schema.ResourceData, meta interface{}) error { + + slbconn := meta.(*AliyunClient).slbconn + + var slbName string + if v, ok := d.GetOk("name"); ok { + slbName = v.(string) + } else { + slbName = resource.PrefixedUniqueId("tf-lb-") + d.Set("name", slbName) + } + + slbArgs := &slb.CreateLoadBalancerArgs{ + RegionId: getRegion(d, meta), + LoadBalancerName: slbName, + } + + if internet, ok := d.GetOk("internet"); ok && internet.(bool) { + slbArgs.AddressType = slb.InternetAddressType + d.Set("internet", true) + } else { + slbArgs.AddressType = slb.IntranetAddressType + d.Set("internet", false) + } + + if v, ok := d.GetOk("internet_charge_type"); ok && v.(string) != "" { + slbArgs.InternetChargeType = slb.InternetChargeType(v.(string)) + } + + if v, ok := d.GetOk("bandwidth"); ok && v.(int) != 0 { + slbArgs.Bandwidth = v.(int) + } + + if v, ok := d.GetOk("vswitch_id"); ok && v.(string) != "" { + slbArgs.VSwitchId = v.(string) + } + slb, err := slbconn.CreateLoadBalancer(slbArgs) + if err != nil { + return err + } + + d.SetId(slb.LoadBalancerId) + + return resourceAliyunSlbUpdate(d, meta) +} + +func resourceAliyunSlbRead(d *schema.ResourceData, meta interface{}) error { + slbconn := meta.(*AliyunClient).slbconn + loadBalancer, err := slbconn.DescribeLoadBalancerAttribute(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + + return err + } + + d.Set("name", loadBalancer.LoadBalancerName) + + if loadBalancer.AddressType == slb.InternetAddressType { + d.Set("internal", true) + } else { + d.Set("internal", false) + } + d.Set("internet_charge_type", loadBalancer.InternetChargeType) + d.Set("bandwidth", loadBalancer.Bandwidth) + d.Set("vswitch_id", loadBalancer.VSwitchId) + d.Set("address", loadBalancer.Address) + + return nil +} + +func resourceAliyunSlbUpdate(d *schema.ResourceData, meta interface{}) error { + + slbconn := meta.(*AliyunClient).slbconn + + d.Partial(true) + + if d.HasChange("name") { + err := slbconn.SetLoadBalancerName(d.Id(), d.Get("name").(string)) + if err != nil { + return err + } + + d.SetPartial("name") + } + + if d.Get("internet") == true && d.Get("internet_charge_type") == "paybybandwidth" { + //don't intranet web and paybybandwidth, then can modify bandwidth + if d.HasChange("bandwidth") { + args := &slb.ModifyLoadBalancerInternetSpecArgs{ + LoadBalancerId: d.Id(), + Bandwidth: d.Get("bandwidth").(int), + } + err := slbconn.ModifyLoadBalancerInternetSpec(args) + if err != nil { + return err + } + + d.SetPartial("bandwidth") + } + } + + if d.HasChange("listener") { + o, n := d.GetChange("listener") + os := o.(*schema.Set) + ns := n.(*schema.Set) + + remove, _ := expandListeners(os.Difference(ns).List()) + add, _ := expandListeners(ns.Difference(os).List()) + + if len(remove) > 0 { + for _, listener := range remove { + err := slbconn.DeleteLoadBalancerListener(d.Id(), listener.LoadBalancerPort) + if err != nil { + return fmt.Errorf("Failure removing outdated SLB listeners: %#v", err) + } + } + } + + if len(add) > 0 { + for _, listener := range add { + err := createListener(slbconn, d.Id(), listener) + if err != nil { + return fmt.Errorf("Failure add SLB listeners: %#v", err) + } + } + } + + d.SetPartial("listener") + } + + // If we currently have instances, or did have instances, + // we want to figure out what to add and remove from the load + // balancer + if d.HasChange("instances") { + o, n := d.GetChange("instances") + os := o.(*schema.Set) + ns := n.(*schema.Set) + remove := expandBackendServers(os.Difference(ns).List()) + add := expandBackendServers(ns.Difference(os).List()) + + if len(add) > 0 { + _, err := slbconn.AddBackendServers(d.Id(), add) + if err != nil { + return err + } + } + if len(remove) > 0 { + removeBackendServers := make([]string, 0, len(remove)) + for _, e := range remove { + removeBackendServers = append(removeBackendServers, e.ServerId) + } + _, err := slbconn.RemoveBackendServers(d.Id(), removeBackendServers) + if err != nil { + return err + } + } + + d.SetPartial("instances") + } + + d.Partial(false) + + return resourceAliyunSlbRead(d, meta) +} + +func resourceAliyunSlbDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).slbconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteLoadBalancer(d.Id()) + + if err != nil { + return resource.NonRetryableError(err) + } + + loadBalancer, err := conn.DescribeLoadBalancerAttribute(d.Id()) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == LoadBalancerNotFound { + return nil + } + return resource.NonRetryableError(err) + } + if loadBalancer != nil { + return resource.RetryableError(fmt.Errorf("LoadBalancer in use - trying again while it deleted.")) + } + return nil + }) +} + +func resourceAliyunSlbListenerHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int))) + buf.WriteString(fmt.Sprintf("%s-", + strings.ToLower(m["lb_protocol"].(string)))) + + buf.WriteString(fmt.Sprintf("%d-", m["bandwidth"].(int))) + + if v, ok := m["ssl_certificate_id"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + return hashcode.String(buf.String()) +} + +func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) error { + if listener.Protocol == strings.ToLower("tcp") { + args := &slb.CreateLoadBalancerTCPListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + } + if err := conn.CreateLoadBalancerTCPListener(args); err != nil { + return err + } + } + + if listener.Protocol == strings.ToLower("http") { + args := &slb.CreateLoadBalancerHTTPListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + StickySession: slb.OffFlag, + HealthCheck: slb.OffFlag, + } + + if err := conn.CreateLoadBalancerHTTPListener(args); err != nil { + return err + } + } + + if listener.Protocol == strings.ToLower("https") { + args := &slb.CreateLoadBalancerHTTPSListenerArgs{ + + HTTPListenerType: slb.HTTPListenerType{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + StickySession: slb.OffFlag, + HealthCheck: slb.OffFlag, + }, + } + if listener.SSLCertificateId == "" { + return fmt.Errorf("Server Certificated Id cann't be null") + } + + args.ServerCertificateId = listener.SSLCertificateId + + if err := conn.CreateLoadBalancerHTTPSListener(args); err != nil { + return err + } + } + + if listener.Protocol == strings.ToLower("udp") { + args := &slb.CreateLoadBalancerUDPListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + } + + if err := conn.CreateLoadBalancerUDPListener(args); err != nil { + return err + } + } + + if err := conn.StartLoadBalancerListener(loadBalancerId, listener.LoadBalancerPort); err != nil { + return err + } + + return nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_slb_attachment.go b/builtin/providers/alicloud/resource_alicloud_slb_attachment.go new file mode 100644 index 0000000000..6a9163c072 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_slb_attachment.go @@ -0,0 +1,144 @@ +package alicloud + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func resourceAliyunSlbAttachment() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunSlbAttachmentCreate, + Read: resourceAliyunSlbAttachmentRead, + Update: resourceAliyunSlbAttachmentUpdate, + Delete: resourceAliyunSlbAttachmentDelete, + + Schema: map[string]*schema.Schema{ + + "slb_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "instances": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + Set: schema.HashString, + }, + + "backend_servers": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceAliyunSlbAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + + slbId := d.Get("slb_id").(string) + + slbconn := meta.(*AliyunClient).slbconn + + loadBalancer, err := slbconn.DescribeLoadBalancerAttribute(slbId) + if err != nil { + if notFoundError(err) { + d.SetId("") + return fmt.Errorf("Special SLB Id not found: %#v", err) + } + + return err + } + + d.SetId(loadBalancer.LoadBalancerId) + + return resourceAliyunSlbAttachmentUpdate(d, meta) +} + +func resourceAliyunSlbAttachmentRead(d *schema.ResourceData, meta interface{}) error { + + slbconn := meta.(*AliyunClient).slbconn + loadBalancer, err := slbconn.DescribeLoadBalancerAttribute(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return fmt.Errorf("Read special SLB Id not found: %#v", err) + } + + return err + } + + backendServerType := loadBalancer.BackendServers + servers := backendServerType.BackendServer + instanceIds := make([]string, 0, len(servers)) + if len(servers) > 0 { + for _, e := range servers { + instanceIds = append(instanceIds, e.ServerId) + } + if err != nil { + return err + } + } + + d.Set("slb_id", d.Id()) + d.Set("instances", instanceIds) + d.Set("backend_servers", strings.Join(instanceIds, ",")) + + return nil +} + +func resourceAliyunSlbAttachmentUpdate(d *schema.ResourceData, meta interface{}) error { + + slbconn := meta.(*AliyunClient).slbconn + if d.HasChange("instances") { + o, n := d.GetChange("instances") + os := o.(*schema.Set) + ns := n.(*schema.Set) + remove := expandBackendServers(os.Difference(ns).List()) + add := expandBackendServers(ns.Difference(os).List()) + + if len(add) > 0 { + _, err := slbconn.AddBackendServers(d.Id(), add) + if err != nil { + return err + } + } + if len(remove) > 0 { + removeBackendServers := make([]string, 0, len(remove)) + for _, e := range remove { + removeBackendServers = append(removeBackendServers, e.ServerId) + } + _, err := slbconn.RemoveBackendServers(d.Id(), removeBackendServers) + if err != nil { + return err + } + } + + } + + return resourceAliyunSlbAttachmentRead(d, meta) + +} + +func resourceAliyunSlbAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + + slbconn := meta.(*AliyunClient).slbconn + o := d.Get("instances") + os := o.(*schema.Set) + remove := expandBackendServers(os.List()) + + if len(remove) > 0 { + removeBackendServers := make([]string, 0, len(remove)) + for _, e := range remove { + removeBackendServers = append(removeBackendServers, e.ServerId) + } + _, err := slbconn.RemoveBackendServers(d.Id(), removeBackendServers) + if err != nil { + return err + } + } + + return nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go b/builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go new file mode 100644 index 0000000000..90a70ead8b --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go @@ -0,0 +1,110 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/slb" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" + "testing" +) + +func TestAccAlicloudSlbAttachment_basic(t *testing.T) { + var slb slb.LoadBalancerType + + testCheckAttr := func() resource.TestCheckFunc { + return func(*terraform.State) error { + log.Printf("testCheckAttr slb BackendServers is: %#v", slb.BackendServers) + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_slb_attachment.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckSlbDestroy, + Steps: []resource.TestStep{ + //test internet_charge_type is paybybandwidth + resource.TestStep{ + Config: testAccSlbAttachment, + Check: resource.ComposeTestCheckFunc( + testAccCheckSlbExists("alicloud_slb_attachment.foo", &slb), + testCheckAttr(), + testAccCheckAttachment("alicloud_instance.foo", &slb), + ), + }, + }, + }) +} + +func testAccCheckAttachment(n string, slb *slb.LoadBalancerType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ECS ID is set") + } + + ecsInstanceId := rs.Primary.ID + + backendServers := slb.BackendServers.BackendServer + + if len(backendServers) == 0 { + return fmt.Errorf("no SLB backendServer: %#v", backendServers) + } + + log.Printf("slb bacnendservers: %#v", backendServers) + + backendServersInstanceId := backendServers[0].ServerId + + if ecsInstanceId != backendServersInstanceId { + return fmt.Errorf("SLB attachment check invalid: ECS instance %s is not equal SLB backendServer %s", + ecsInstanceId, backendServersInstanceId) + } + return nil + } +} + +const testAccSlbAttachment = ` +resource "alicloud_security_group" "foo" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + internet_charge_type = "PayByBandwidth" + internet_max_bandwidth_out = "5" + system_disk_category = "cloud_efficiency" + io_optimized = "optimized" + + security_groups = ["${alicloud_security_group.foo.id}"] + instance_name = "test_foo" +} + +resource "alicloud_slb" "foo" { + name = "tf_test_slb_bind" + internet_charge_type = "paybybandwidth" + bandwidth = "5" + internet = "true" +} + +resource "alicloud_slb_attachment" "foo" { + slb_id = "${alicloud_slb.foo.id}" + instances = ["${alicloud_instance.foo.id}"] +} + +` diff --git a/builtin/providers/alicloud/resource_alicloud_slb_test.go b/builtin/providers/alicloud/resource_alicloud_slb_test.go new file mode 100644 index 0000000000..3e68a4c149 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_slb_test.go @@ -0,0 +1,294 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/slb" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" + "testing" +) + +func TestAccAlicloudSlb_basic(t *testing.T) { + var slb slb.LoadBalancerType + + testCheckAttr := func() resource.TestCheckFunc { + return func(*terraform.State) error { + log.Printf("testCheckAttr slb AddressType is: %s", slb.AddressType) + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_slb.bindwidth", + + Providers: testAccProviders, + CheckDestroy: testAccCheckSlbDestroy, + Steps: []resource.TestStep{ + //test internet_charge_type is paybybandwidth + resource.TestStep{ + Config: testAccSlbBindWidth, + Check: resource.ComposeTestCheckFunc( + testAccCheckSlbExists("alicloud_slb.bindwidth", &slb), + testCheckAttr(), + resource.TestCheckResourceAttr( + "alicloud_slb.bindwidth", "internet_charge_type", "paybybandwidth"), + ), + }, + }, + }) +} + +func TestAccAlicloudSlb_traffic(t *testing.T) { + var slb slb.LoadBalancerType + + testCheckAttr := func() resource.TestCheckFunc { + return func(*terraform.State) error { + log.Printf("testCheckAttr slb AddressType is: %s", slb.AddressType) + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_slb.traffic", + Providers: testAccProviders, + CheckDestroy: testAccCheckSlbDestroy, + Steps: []resource.TestStep{ + //test internet_charge_type is paybytraffic + resource.TestStep{ + Config: testAccSlbTraffic, + Check: resource.ComposeTestCheckFunc( + testAccCheckSlbExists("alicloud_slb.traffic", &slb), + testCheckAttr(), + resource.TestCheckResourceAttr( + "alicloud_slb.traffic", "name", "tf_test_slb_classic"), + ), + }, + }, + }) +} + +func TestAccAlicloudSlb_listener(t *testing.T) { + var slb slb.LoadBalancerType + + testListener := func() resource.TestCheckFunc { + return func(*terraform.State) error { + listenerPorts := slb.ListenerPorts.ListenerPort[0] + if listenerPorts != 161 { + return fmt.Errorf("bad loadbalancer listener: %#v", listenerPorts) + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_slb.listener", + Providers: testAccProviders, + CheckDestroy: testAccCheckSlbDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSlbListener, + Check: resource.ComposeTestCheckFunc( + testAccCheckSlbExists("alicloud_slb.listener", &slb), + resource.TestCheckResourceAttr( + "alicloud_slb.listener", "name", "tf_test_slb"), + testAccCheckListenersExists("alicloud_slb.listener", &slb, "http"), + testListener(), + ), + }, + }, + }) +} + +func TestAccAlicloudSlb_vpc(t *testing.T) { + var slb slb.LoadBalancerType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_slb.vpc", + Providers: testAccProviders, + CheckDestroy: testAccCheckSlbDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSlb4Vpc, + Check: resource.ComposeTestCheckFunc( + testAccCheckSlbExists("alicloud_slb.vpc", &slb), + resource.TestCheckResourceAttr( + "alicloud_slb.vpc", "name", "tf_test_slb_vpc"), + ), + }, + }, + }) +} + +func testAccCheckSlbExists(n string, slb *slb.LoadBalancerType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No SLB ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + instance, err := client.DescribeLoadBalancerAttribute(rs.Primary.ID) + + if err != nil { + return err + } + if instance == nil { + return fmt.Errorf("SLB not found") + } + + *slb = *instance + return nil + } +} + +func testAccCheckListenersExists(n string, slb *slb.LoadBalancerType, p string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No SLB ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + instance, err := client.DescribeLoadBalancerAttribute(rs.Primary.ID) + + if err != nil { + return err + } + if instance == nil { + return fmt.Errorf("SLB not found") + } + + exist := false + for _, listener := range instance.ListenerPortsAndProtocol.ListenerPortAndProtocol { + if listener.ListenerProtocol == p { + exist = true + break + } + } + + if !exist { + return fmt.Errorf("The %s protocol Listener not found.", p) + } + return nil + } +} + +func testAccCheckSlbDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_slb" { + continue + } + + // Try to find the Slb + instance, err := client.DescribeLoadBalancerAttribute(rs.Primary.ID) + + if instance != nil { + return fmt.Errorf("SLB still exist") + } + + if err != nil { + e, _ := err.(*common.Error) + // Verify the error is what we want + if e.ErrorResponse.Code != LoadBalancerNotFound { + return err + } + + } + + } + + return nil +} + +const testAccSlbBindWidth = ` +resource "alicloud_slb" "bindwidth" { + name = "tf_test_slb_bindwidth" + internet_charge_type = "paybybandwidth" + bandwidth = 5 + internet = true +} +` + +const testAccSlbTraffic = ` +resource "alicloud_slb" "traffic" { + name = "tf_test_slb_classic" +} +` + +const testAccSlbListener = ` +resource "alicloud_slb" "listener" { + name = "tf_test_slb" + internet_charge_type = "paybybandwidth" + bandwidth = 5 + internet = true + listener = [ + { + "instance_port" = "2111" + "lb_port" = "21" + "lb_protocol" = "tcp" + "bandwidth" = 1 + },{ + "instance_port" = "8000" + "lb_port" = "80" + "lb_protocol" = "http" + "bandwidth" = 1 + },{ + "instance_port" = "1611" + "lb_port" = "161" + "lb_protocol" = "udp" + "bandwidth" = 1 + }] +} +` + +const testAccSlb4Vpc = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_slb" "vpc" { + name = "tf_test_slb_vpc" + //internet_charge_type = "paybybandwidth" + vswitch_id = "${alicloud_vswitch.foo.id}" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_vpc.go b/builtin/providers/alicloud/resource_alicloud_vpc.go new file mode 100644 index 0000000000..e59e3b53b8 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vpc.go @@ -0,0 +1,190 @@ +package alicloud + +import ( + "fmt" + "strings" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAliyunVpc() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunVpcCreate, + Read: resourceAliyunVpcRead, + Update: resourceAliyunVpcUpdate, + Delete: resourceAliyunVpcDelete, + + Schema: map[string]*schema.Schema{ + "cidr_block": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateCIDRNetworkAddress, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 128 { + errors = append(errors, fmt.Errorf("%s cannot be longer than 128 characters", k)) + } + + if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") { + errors = append(errors, fmt.Errorf("%s cannot starts with http:// or https://", k)) + } + + return + }, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%s cannot be longer than 256 characters", k)) + + } + return + }, + }, + "router_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "router_table_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAliyunVpcCreate(d *schema.ResourceData, meta interface{}) error { + + args, err := buildAliyunVpcArgs(d, meta) + if err != nil { + return err + } + + ecsconn := meta.(*AliyunClient).ecsconn + + vpc, err := ecsconn.CreateVpc(args) + if err != nil { + return err + } + + d.SetId(vpc.VpcId) + d.Set("router_table_id", vpc.RouteTableId) + + err = ecsconn.WaitForVpcAvailable(args.RegionId, vpc.VpcId, 60) + if err != nil { + return fmt.Errorf("Timeout when WaitForVpcAvailable") + } + + return resourceAliyunVpcRead(d, meta) +} + +func resourceAliyunVpcRead(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*AliyunClient) + + vpc, err := client.DescribeVpc(d.Id()) + if err != nil { + return err + } + + if vpc == nil { + d.SetId("") + return nil + } + + d.Set("cidr_block", vpc.CidrBlock) + d.Set("name", vpc.VpcName) + d.Set("description", vpc.Description) + d.Set("router_id", vpc.VRouterId) + + return nil +} + +func resourceAliyunVpcUpdate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + d.Partial(true) + + attributeUpdate := false + args := &ecs.ModifyVpcAttributeArgs{ + VpcId: d.Id(), + } + + if d.HasChange("name") { + d.SetPartial("name") + args.VpcName = d.Get("name").(string) + + attributeUpdate = true + } + + if d.HasChange("description") { + d.SetPartial("description") + args.Description = d.Get("description").(string) + + attributeUpdate = true + } + + if attributeUpdate { + if err := conn.ModifyVpcAttribute(args); err != nil { + return err + } + } + + d.Partial(false) + + return nil +} + +func resourceAliyunVpcDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteVpc(d.Id()) + + if err != nil { + return resource.RetryableError(fmt.Errorf("Vpc in use - trying again while it is deleted.")) + } + + args := &ecs.DescribeVpcsArgs{ + RegionId: getRegion(d, meta), + VpcId: d.Id(), + } + vpc, _, descErr := conn.DescribeVpcs(args) + if descErr != nil { + return resource.NonRetryableError(err) + } else if vpc == nil || len(vpc) < 1 { + return nil + } + + return resource.RetryableError(fmt.Errorf("Vpc in use - trying again while it is deleted.")) + }) +} + +func buildAliyunVpcArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateVpcArgs, error) { + args := &ecs.CreateVpcArgs{ + RegionId: getRegion(d, meta), + CidrBlock: d.Get("cidr_block").(string), + } + + if v := d.Get("name").(string); v != "" { + args.VpcName = v + } + + if v := d.Get("description").(string); v != "" { + args.Description = v + } + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_vpc_test.go b/builtin/providers/alicloud/resource_alicloud_vpc_test.go new file mode 100644 index 0000000000..b67c650013 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vpc_test.go @@ -0,0 +1,140 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAlicloudVpc_basic(t *testing.T) { + var vpc ecs.VpcSetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_vpc.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckVpcDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccVpcConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcExists("alicloud_vpc.foo", &vpc), + resource.TestCheckResourceAttr( + "alicloud_vpc.foo", "cidr_block", "172.16.0.0/12"), + resource.TestCheckResourceAttrSet( + "alicloud_vpc.foo", "router_id"), + resource.TestCheckResourceAttrSet( + "alicloud_vpc.foo", "router_table_id"), + ), + }, + }, + }) + +} + +func TestAccAlicloudVpc_update(t *testing.T) { + var vpc ecs.VpcSetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVpcDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccVpcConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcExists("alicloud_vpc.foo", &vpc), + resource.TestCheckResourceAttr( + "alicloud_vpc.foo", "cidr_block", "172.16.0.0/12"), + ), + }, + resource.TestStep{ + Config: testAccVpcConfigUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcExists("alicloud_vpc.foo", &vpc), + resource.TestCheckResourceAttr( + "alicloud_vpc.foo", "name", "tf_test_bar"), + ), + }, + }, + }) +} + +func testAccCheckVpcExists(n string, vpc *ecs.VpcSetType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No VPC ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + instance, err := client.DescribeVpc(rs.Primary.ID) + + if err != nil { + return err + } + if instance == nil { + return fmt.Errorf("VPC not found") + } + + *vpc = *instance + return nil + } +} + +func testAccCheckVpcDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_vpc" { + continue + } + + // Try to find the VPC + instance, err := client.DescribeVpc(rs.Primary.ID) + + if instance != nil { + return fmt.Errorf("VPCs still exist") + } + + if err != nil { + // Verify the error is what we want + e, _ := err.(*common.Error) + + if e.ErrorResponse.Code != "InvalidVpcID.NotFound" { + return err + } + } + + } + + return nil +} + +const testAccVpcConfig = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} +` + +const testAccVpcConfigUpdate = ` +resource "alicloud_vpc" "foo" { + cidr_block = "172.16.0.0/12" + name = "tf_test_bar" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_vroute_entry.go b/builtin/providers/alicloud/resource_alicloud_vroute_entry.go new file mode 100644 index 0000000000..b71e92ffeb --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vroute_entry.go @@ -0,0 +1,145 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func resourceAliyunRouteEntry() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunRouteEntryCreate, + Read: resourceAliyunRouteEntryRead, + Delete: resourceAliyunRouteEntryDelete, + + Schema: map[string]*schema.Schema{ + "router_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "route_table_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "destination_cidrblock": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "nexthop_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateRouteEntryNextHopType, + }, + "nexthop_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceAliyunRouteEntryCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + rtId := d.Get("route_table_id").(string) + rId := d.Get("router_id").(string) + cidr := d.Get("destination_cidrblock").(string) + nt := d.Get("nexthop_type").(string) + ni := d.Get("nexthop_id").(string) + + args, err := buildAliyunRouteEntryArgs(d, meta) + if err != nil { + return err + } + err = conn.CreateRouteEntry(args) + + if err != nil { + return err + } + // route_table_id:router_id:destination_cidrblock:nexthop_type:nexthop_id + d.SetId(rtId + ":" + rId + ":" + cidr + ":" + nt + ":" + ni) + d.Set("router_id", rId) + + if err := conn.WaitForAllRouteEntriesAvailable(rId, rtId, defaultTimeout); err != nil { + return fmt.Errorf("WaitFor route entry got error: %#v", err) + } + return resourceAliyunRouteEntryRead(d, meta) +} + +func resourceAliyunRouteEntryRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + parts := strings.Split(d.Id(), ":") + rtId := parts[0] + //rId := parts[1] + cidr := parts[2] + nexthop_type := parts[3] + nexthop_id := parts[4] + + en, err := client.QueryRouteEntry(rtId, cidr, nexthop_type, nexthop_id) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error route entry: %#v", err) + } + + d.Set("route_table_id", en.RouteTableId) + d.Set("destination_cidrblock", en.DestinationCidrBlock) + d.Set("nexthop_type", en.NextHopType) + d.Set("nexthop_id", en.InstanceId) + return nil +} + +func resourceAliyunRouteEntryDelete(d *schema.ResourceData, meta interface{}) error { + con := meta.(*AliyunClient).ecsconn + args, err := buildAliyunRouteEntryDeleteArgs(d, meta) + + if err != nil { + return err + } + return con.DeleteRouteEntry(args) +} + +func buildAliyunRouteEntryArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateRouteEntryArgs, error) { + + args := &ecs.CreateRouteEntryArgs{ + RouteTableId: d.Get("route_table_id").(string), + DestinationCidrBlock: d.Get("destination_cidrblock").(string), + } + + if v := d.Get("nexthop_type").(string); v != "" { + args.NextHopType = ecs.NextHopType(v) + } + + if v := d.Get("nexthop_id").(string); v != "" { + args.NextHopId = v + } + + return args, nil +} + +func buildAliyunRouteEntryDeleteArgs(d *schema.ResourceData, meta interface{}) (*ecs.DeleteRouteEntryArgs, error) { + + args := &ecs.DeleteRouteEntryArgs{ + RouteTableId: d.Get("route_table_id").(string), + DestinationCidrBlock: d.Get("destination_cidrblock").(string), + } + + if v := d.Get("destination_cidrblock").(string); v != "" { + args.DestinationCidrBlock = v + } + + if v := d.Get("nexthop_id").(string); v != "" { + args.NextHopId = v + } + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go b/builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go new file mode 100644 index 0000000000..cbdb59bef1 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go @@ -0,0 +1,183 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "strings" + "testing" +) + +func TestAccAlicloudRouteEntry_Basic(t *testing.T) { + var rt ecs.RouteTableSetType + var rn ecs.RouteEntrySetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_route_entry.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckRouteEntryDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRouteEntryConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRouteTableEntryExists( + "alicloud_route_entry.foo", &rt, &rn), + resource.TestCheckResourceAttrSet( + "alicloud_route_entry.foo", "nexthop_id"), + ), + }, + }, + }) + +} + +func testAccCheckRouteTableExists(rtId string, t *ecs.RouteTableSetType) error { + client := testAccProvider.Meta().(*AliyunClient) + //query route table + rt, terr := client.QueryRouteTableById(rtId) + + if terr != nil { + return terr + } + + if rt == nil { + return fmt.Errorf("Route Table not found") + } + + *t = *rt + return nil +} + +func testAccCheckRouteEntryExists(routeTableId, cidrBlock, nextHopType, nextHopId string, e *ecs.RouteEntrySetType) error { + client := testAccProvider.Meta().(*AliyunClient) + //query route table entry + re, rerr := client.QueryRouteEntry(routeTableId, cidrBlock, nextHopType, nextHopId) + + if rerr != nil { + return rerr + } + + if re == nil { + return fmt.Errorf("Route Table Entry not found") + } + + *e = *re + return nil +} + +func testAccCheckRouteTableEntryExists(n string, t *ecs.RouteTableSetType, e *ecs.RouteEntrySetType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Route Entry ID is set") + } + + parts := strings.Split(rs.Primary.ID, ":") + + //query route table + err := testAccCheckRouteTableExists(parts[0], t) + + if err != nil { + return err + } + //query route table entry + err = testAccCheckRouteEntryExists(parts[0], parts[2], parts[3], parts[4], e) + return err + } +} + +func testAccCheckRouteEntryDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_route_entry" { + continue + } + + parts := strings.Split(rs.Primary.ID, ":") + re, err := client.QueryRouteEntry(parts[0], parts[2], parts[3], parts[4]) + + if re != nil { + return fmt.Errorf("Error Route Entry still exist") + } + + // Verify the error is what we want + if err != nil { + if notFoundError(err) { + return nil + } + return err + } + } + + return nil +} + +const testAccRouteEntryConfig = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "10.1.0.0/21" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "cn-beijing-c" +} + +resource "alicloud_route_entry" "foo" { + router_id = "${alicloud_vpc.foo.router_id}" + route_table_id = "${alicloud_vpc.foo.router_table_id}" + destination_cidrblock = "172.11.1.1/32" + nexthop_type = "Instance" + nexthop_id = "${alicloud_instance.foo.id}" +} + +resource "alicloud_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" +} + +resource "alicloud_security_group_rule" "ingress" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_instance" "foo" { + # cn-beijing + availability_zone = "cn-beijing-c" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + + vswitch_id = "${alicloud_vswitch.foo.id}" + allocate_public_ip = true + + # series II + instance_charge_type = "PostPaid" + instance_type = "ecs.n1.small" + internet_charge_type = "PayByTraffic" + internet_max_bandwidth_out = 5 + io_optimized = "optimized" + + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" +} + +` diff --git a/builtin/providers/alicloud/resource_alicloud_vswitch.go b/builtin/providers/alicloud/resource_alicloud_vswitch.go new file mode 100644 index 0000000000..74d4c6a888 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vswitch.go @@ -0,0 +1,220 @@ +package alicloud + +import ( + "fmt" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "log" + "time" +) + +func resourceAliyunSubnet() *schema.Resource { + return &schema.Resource{ + Create: resourceAliyunSwitchCreate, + Read: resourceAliyunSwitchRead, + Update: resourceAliyunSwitchUpdate, + Delete: resourceAliyunSwitchDelete, + + Schema: map[string]*schema.Schema{ + "availability_zone": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "cidr_block": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateSwitchCIDRNetworkAddress, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func resourceAliyunSwitchCreate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + args, err := buildAliyunSwitchArgs(d, meta) + if err != nil { + return err + } + + vswitchID, err := conn.CreateVSwitch(args) + if err != nil { + return fmt.Errorf("Create subnet got a error :%s", err) + } + + d.SetId(vswitchID) + + err = conn.WaitForVSwitchAvailable(args.VpcId, vswitchID, 60) + if err != nil { + return fmt.Errorf("WaitForVSwitchAvailable got a error: %s", err) + } + + return resourceAliyunSwitchRead(d, meta) +} + +func resourceAliyunSwitchRead(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + args := &ecs.DescribeVSwitchesArgs{ + VpcId: d.Get("vpc_id").(string), + VSwitchId: d.Id(), + } + + vswitches, _, err := conn.DescribeVSwitches(args) + + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return err + } + + if len(vswitches) == 0 { + d.SetId("") + return nil + } + + vswitch := vswitches[0] + + d.Set("availability_zone", vswitch.ZoneId) + d.Set("vpc_id", vswitch.VpcId) + d.Set("cidr_block", vswitch.CidrBlock) + d.Set("name", vswitch.VSwitchName) + d.Set("description", vswitch.Description) + + return nil +} + +func resourceAliyunSwitchUpdate(d *schema.ResourceData, meta interface{}) error { + + conn := meta.(*AliyunClient).ecsconn + + d.Partial(true) + + attributeUpdate := false + args := &ecs.ModifyVSwitchAttributeArgs{ + VSwitchId: d.Id(), + } + + if d.HasChange("name") { + d.SetPartial("name") + args.VSwitchName = d.Get("name").(string) + + attributeUpdate = true + } + + if d.HasChange("description") { + d.SetPartial("description") + args.Description = d.Get("description").(string) + + attributeUpdate = true + } + if attributeUpdate { + if err := conn.ModifyVSwitchAttribute(args); err != nil { + return err + } + + } + + d.Partial(false) + + return nil +} + +func resourceAliyunSwitchDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteVSwitch(d.Id()) + + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == VswitcInvalidRegionId { + log.Printf("[ERROR] Delete Switch is failed.") + return resource.NonRetryableError(err) + } + + return resource.RetryableError(fmt.Errorf("Switch in use. -- trying again while it is deleted.")) + } + + vsw, _, vswErr := conn.DescribeVSwitches(&ecs.DescribeVSwitchesArgs{ + VpcId: d.Get("vpc_id").(string), + VSwitchId: d.Id(), + }) + + if vswErr != nil { + return resource.NonRetryableError(vswErr) + } else if vsw == nil || len(vsw) < 1 { + return nil + } + + return resource.RetryableError(fmt.Errorf("Switch in use. -- trying again while it is deleted.")) + }) +} + +func buildAliyunSwitchArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateVSwitchArgs, error) { + + client := meta.(*AliyunClient) + + vpcID := d.Get("vpc_id").(string) + + vpc, err := client.DescribeVpc(vpcID) + if err != nil { + return nil, err + } + + if vpc == nil { + return nil, fmt.Errorf("vpc_id not found") + } + + zoneID := d.Get("availability_zone").(string) + + zone, err := client.DescribeZone(zoneID) + if err != nil { + return nil, err + } + + err = client.ResourceAvailable(zone, ecs.ResourceTypeVSwitch) + if err != nil { + return nil, err + } + + cidrBlock := d.Get("cidr_block").(string) + + args := &ecs.CreateVSwitchArgs{ + VpcId: vpcID, + ZoneId: zoneID, + CidrBlock: cidrBlock, + } + + if v, ok := d.GetOk("name"); ok && v != "" { + args.VSwitchName = v.(string) + } + + if v, ok := d.GetOk("description"); ok && v != "" { + args.Description = v.(string) + } + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_vswitch_test.go b/builtin/providers/alicloud/resource_alicloud_vswitch_test.go new file mode 100644 index 0000000000..bcd70a2cc1 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_vswitch_test.go @@ -0,0 +1,105 @@ +package alicloud + +import ( + "testing" + + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAlicloudVswitch_basic(t *testing.T) { + var vsw ecs.VSwitchSetType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_vswitch.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckVswitchDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccVswitchConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVswitchExists("alicloud_vswitch.foo", &vsw), + resource.TestCheckResourceAttr( + "alicloud_vswitch.foo", "cidr_block", "172.16.0.0/21"), + ), + }, + }, + }) + +} + +func testAccCheckVswitchExists(n string, vpc *ecs.VSwitchSetType) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Vswitch ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + instance, err := client.QueryVswitchById(rs.Primary.Attributes["vpc_id"], rs.Primary.ID) + + if err != nil { + return err + } + if instance == nil { + return fmt.Errorf("Vswitch not found") + } + + *vpc = *instance + return nil + } +} + +func testAccCheckVswitchDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_vswitch" { + continue + } + + // Try to find the Vswitch + instance, err := client.QueryVswitchById(rs.Primary.Attributes["vpc_id"], rs.Primary.ID) + + if instance != nil { + return fmt.Errorf("Vswitch still exist") + } + + if err != nil { + // Verify the error is what we want + e, _ := err.(*common.Error) + + if e.ErrorResponse.Code != "InvalidVswitchID.NotFound" { + return err + } + } + + } + + return nil +} + +const testAccVswitchConfig = ` +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} +` diff --git a/builtin/providers/alicloud/service_alicloud_ecs.go b/builtin/providers/alicloud/service_alicloud_ecs.go new file mode 100644 index 0000000000..2c892ce242 --- /dev/null +++ b/builtin/providers/alicloud/service_alicloud_ecs.go @@ -0,0 +1,208 @@ +package alicloud + +import ( + "encoding/json" + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "strings" +) + +func (client *AliyunClient) DescribeImage(imageId string) (*ecs.ImageType, error) { + + pagination := common.Pagination{ + PageNumber: 1, + } + args := ecs.DescribeImagesArgs{ + Pagination: pagination, + RegionId: client.Region, + Status: ecs.ImageStatusAvailable, + } + + var allImages []ecs.ImageType + + for { + images, _, err := client.ecsconn.DescribeImages(&args) + if err != nil { + break + } + + if len(images) == 0 { + break + } + + allImages = append(allImages, images...) + + args.Pagination.PageNumber++ + } + + if len(allImages) == 0 { + return nil, common.GetClientErrorFromString("Not found") + } + + var image *ecs.ImageType + imageIds := []string{} + for _, im := range allImages { + if im.ImageId == imageId { + image = &im + } + imageIds = append(imageIds, im.ImageId) + } + + if image == nil { + return nil, fmt.Errorf("image_id %s not exists in range %s, all images are %s", imageId, client.Region, imageIds) + } + + return image, nil +} + +// DescribeZone validate zoneId is valid in region +func (client *AliyunClient) DescribeZone(zoneID string) (*ecs.ZoneType, error) { + zones, err := client.ecsconn.DescribeZones(client.Region) + if err != nil { + return nil, fmt.Errorf("error to list zones not found") + } + + var zone *ecs.ZoneType + zoneIds := []string{} + for _, z := range zones { + if z.ZoneId == zoneID { + zone = &ecs.ZoneType{ + ZoneId: z.ZoneId, + LocalName: z.LocalName, + AvailableResourceCreation: z.AvailableResourceCreation, + AvailableDiskCategories: z.AvailableDiskCategories, + } + } + zoneIds = append(zoneIds, z.ZoneId) + } + + if zone == nil { + return nil, fmt.Errorf("availability_zone not exists in range %s, all zones are %s", client.Region, zoneIds) + } + + return zone, nil +} + +func (client *AliyunClient) QueryInstancesByIds(ids []string) (instances []ecs.InstanceAttributesType, err error) { + idsStr, jerr := json.Marshal(ids) + if jerr != nil { + return nil, jerr + } + + args := ecs.DescribeInstancesArgs{ + RegionId: client.Region, + InstanceIds: string(idsStr), + } + + instances, _, errs := client.ecsconn.DescribeInstances(&args) + + if errs != nil { + return nil, errs + } + + return instances, nil +} + +func (client *AliyunClient) QueryInstancesById(id string) (instance *ecs.InstanceAttributesType, err error) { + ids := []string{id} + + instances, errs := client.QueryInstancesByIds(ids) + if errs != nil { + return nil, errs + } + + if len(instances) == 0 { + return nil, common.GetClientErrorFromString(InstanceNotfound) + } + + return &instances[0], nil +} + +// ResourceAvailable check resource available for zone +func (client *AliyunClient) ResourceAvailable(zone *ecs.ZoneType, resourceType ecs.ResourceType) error { + available := false + for _, res := range zone.AvailableResourceCreation.ResourceTypes { + if res == resourceType { + available = true + } + } + if !available { + return fmt.Errorf("%s is not available in %s zone of %s region", resourceType, zone.ZoneId, client.Region) + } + + return nil +} + +func (client *AliyunClient) DiskAvailable(zone *ecs.ZoneType, diskCategory ecs.DiskCategory) error { + available := false + for _, dist := range zone.AvailableDiskCategories.DiskCategories { + if dist == diskCategory { + available = true + } + } + if !available { + return fmt.Errorf("%s is not available in %s zone of %s region", diskCategory, zone.ZoneId, client.Region) + } + return nil +} + +// todo: support syc +func (client *AliyunClient) JoinSecurityGroups(instanceId string, securityGroupIds []string) error { + for _, sid := range securityGroupIds { + err := client.ecsconn.JoinSecurityGroup(instanceId, sid) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code != InvalidInstanceIdAlreadyExists { + return err + } + } + } + + return nil +} + +func (client *AliyunClient) LeaveSecurityGroups(instanceId string, securityGroupIds []string) error { + for _, sid := range securityGroupIds { + err := client.ecsconn.LeaveSecurityGroup(instanceId, sid) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code != InvalidSecurityGroupIdNotFound { + return err + } + } + } + + return nil +} + +func (client *AliyunClient) DescribeSecurity(securityGroupId string) (*ecs.DescribeSecurityGroupAttributeResponse, error) { + + args := &ecs.DescribeSecurityGroupAttributeArgs{ + RegionId: client.Region, + SecurityGroupId: securityGroupId, + } + + return client.ecsconn.DescribeSecurityGroupAttribute(args) +} + +func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, types, ip_protocol, port_range string) (*ecs.PermissionType, error) { + + sg, err := client.DescribeSecurity(securityGroupId) + if err != nil { + return nil, err + } + + for _, p := range sg.Permissions.Permission { + if strings.ToLower(string(p.IpProtocol)) == ip_protocol && p.PortRange == port_range { + return &p, nil + } + } + return nil, nil + +} + +func (client *AliyunClient) RevokeSecurityGroup(args *ecs.RevokeSecurityGroupArgs) error { + //todo: handle the specal err + return client.ecsconn.RevokeSecurityGroup(args) +} diff --git a/builtin/providers/alicloud/service_alicloud_slb.go b/builtin/providers/alicloud/service_alicloud_slb.go new file mode 100644 index 0000000000..9fc4730183 --- /dev/null +++ b/builtin/providers/alicloud/service_alicloud_slb.go @@ -0,0 +1,21 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/slb" +) + +func (client *AliyunClient) DescribeLoadBalancerAttribute(slbId string) (*slb.LoadBalancerType, error) { + loadBalancer, err := client.slbconn.DescribeLoadBalancerAttribute(slbId) + if err != nil { + if notFoundError(err) { + return nil, nil + } + return nil, err + } + + if loadBalancer != nil { + return loadBalancer, nil + } + + return nil, nil +} diff --git a/builtin/providers/alicloud/service_alicloud_vpc.go b/builtin/providers/alicloud/service_alicloud_vpc.go new file mode 100644 index 0000000000..102c611653 --- /dev/null +++ b/builtin/providers/alicloud/service_alicloud_vpc.go @@ -0,0 +1,134 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "strings" +) + +func (client *AliyunClient) DescribeEipAddress(allocationId string) (*ecs.EipAddressSetType, error) { + + args := ecs.DescribeEipAddressesArgs{ + RegionId: client.Region, + AllocationId: allocationId, + } + + eips, _, err := client.ecsconn.DescribeEipAddresses(&args) + if err != nil { + return nil, err + } + if len(eips) == 0 { + return nil, common.GetClientErrorFromString("Not found") + } + + return &eips[0], nil +} + +func (client *AliyunClient) DescribeNatGateway(natGatewayId string) (*NatGatewaySetType, error) { + + args := &DescribeNatGatewaysArgs{ + RegionId: client.Region, + NatGatewayId: natGatewayId, + } + + natGateways, _, err := DescribeNatGateways(client.ecsconn, args) + if err != nil { + return nil, err + } + + if len(natGateways) == 0 { + return nil, common.GetClientErrorFromString("Not found") + } + + return &natGateways[0], nil +} + +func (client *AliyunClient) DescribeVpc(vpcId string) (*ecs.VpcSetType, error) { + args := ecs.DescribeVpcsArgs{ + RegionId: client.Region, + VpcId: vpcId, + } + + vpcs, _, err := client.ecsconn.DescribeVpcs(&args) + if err != nil { + if notFoundError(err) { + return nil, nil + } + return nil, err + } + + if len(vpcs) == 0 { + return nil, nil + } + + return &vpcs[0], nil +} + +// describe vswitch by param filters +func (client *AliyunClient) QueryVswitches(args *ecs.DescribeVSwitchesArgs) (vswitches []ecs.VSwitchSetType, err error) { + vsws, _, err := client.ecsconn.DescribeVSwitches(args) + if err != nil { + if notFoundError(err) { + return nil, nil + } + return nil, err + } + + return vsws, nil +} + +func (client *AliyunClient) QueryVswitchById(vpcId, vswitchId string) (vsw *ecs.VSwitchSetType, err error) { + args := &ecs.DescribeVSwitchesArgs{ + VpcId: vpcId, + VSwitchId: vswitchId, + } + vsws, err := client.QueryVswitches(args) + if err != nil { + return nil, err + } + + if len(vsws) == 0 { + return nil, nil + } + + return &vsws[0], nil +} + +func (client *AliyunClient) QueryRouteTables(args *ecs.DescribeRouteTablesArgs) (routeTables []ecs.RouteTableSetType, err error) { + rts, _, err := client.ecsconn.DescribeRouteTables(args) + if err != nil { + return nil, err + } + + return rts, nil +} + +func (client *AliyunClient) QueryRouteTableById(routeTableId string) (rt *ecs.RouteTableSetType, err error) { + args := &ecs.DescribeRouteTablesArgs{ + RouteTableId: routeTableId, + } + rts, err := client.QueryRouteTables(args) + if err != nil { + return nil, err + } + + if len(rts) == 0 { + return nil, &common.Error{ErrorResponse: common.ErrorResponse{Message: Notfound}} + } + + return &rts[0], nil +} + +func (client *AliyunClient) QueryRouteEntry(routeTableId, cidrBlock, nextHopType, nextHopId string) (rn *ecs.RouteEntrySetType, err error) { + rt, errs := client.QueryRouteTableById(routeTableId) + if errs != nil { + return nil, errs + } + + for _, e := range rt.RouteEntrys.RouteEntry { + if strings.ToLower(string(e.DestinationCidrBlock)) == cidrBlock { + return &e, nil + } + } + return nil, nil +} diff --git a/builtin/providers/alicloud/struct_security_groups.go b/builtin/providers/alicloud/struct_security_groups.go new file mode 100644 index 0000000000..678f68f7d9 --- /dev/null +++ b/builtin/providers/alicloud/struct_security_groups.go @@ -0,0 +1,11 @@ +package alicloud + +// Takes the result of flatmap.Expand for an array of strings +// and returns a []string +func expandStringList(configured []interface{}) []string { + vs := make([]string, 0, len(configured)) + for _, v := range configured { + vs = append(vs, v.(string)) + } + return vs +} diff --git a/builtin/providers/alicloud/tags.go b/builtin/providers/alicloud/tags.go new file mode 100644 index 0000000000..a2d906ab97 --- /dev/null +++ b/builtin/providers/alicloud/tags.go @@ -0,0 +1,126 @@ +package alicloud + +import ( + "fmt" + "log" + + "github.com/denverdino/aliyungo/ecs" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func String(v string) *string { + return &v +} + +// tagsSchema returns the schema to use for tags. +// +func tagsSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeMap, + //Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + } +} + +// setTags is a helper to set the tags for a resource. It expects the +// tags field to be named "tags" +func setTags(client *AliyunClient, resourceType ecs.TagResourceType, d *schema.ResourceData) error { + + conn := client.ecsconn + + if d.HasChange("tags") { + oraw, nraw := d.GetChange("tags") + o := oraw.(map[string]interface{}) + n := nraw.(map[string]interface{}) + create, remove := diffTags(tagsFromMap(o), tagsFromMap(n)) + + // Set tags + if len(remove) > 0 { + log.Printf("[DEBUG] Removing tags: %#v from %s", remove, d.Id()) + err := RemoveTags(conn, &RemoveTagsArgs{ + RegionId: client.Region, + ResourceId: d.Id(), + ResourceType: resourceType, + Tag: remove, + }) + if err != nil { + return fmt.Errorf("Remove tags got error: %s", err) + } + } + + if len(create) > 0 { + log.Printf("[DEBUG] Creating tags: %s for %s", create, d.Id()) + err := AddTags(conn, &AddTagsArgs{ + RegionId: client.Region, + ResourceId: d.Id(), + ResourceType: resourceType, + Tag: create, + }) + if err != nil { + return fmt.Errorf("Creating tags got error: %s", err) + } + } + } + + return nil +} + +// diffTags takes our tags locally and the ones remotely and returns +// the set of tags that must be created, and the set of tags that must +// be destroyed. +func diffTags(oldTags, newTags []Tag) ([]Tag, []Tag) { + // First, we're creating everything we have + create := make(map[string]interface{}) + for _, t := range newTags { + create[t.Key] = t.Value + } + + // Build the list of what to remove + var remove []Tag + for _, t := range oldTags { + old, ok := create[t.Key] + if !ok || old != t.Value { + // Delete it! + remove = append(remove, t) + } + } + + return tagsFromMap(create), remove +} + +// tagsFromMap returns the tags for the given map of data. +func tagsFromMap(m map[string]interface{}) []Tag { + result := make([]Tag, 0, len(m)) + for k, v := range m { + result = append(result, Tag{ + Key: k, + Value: v.(string), + }) + } + + return result +} + +func tagsToMap(tags []ecs.TagItemType) map[string]string { + result := make(map[string]string) + for _, t := range tags { + result[t.TagKey] = t.TagValue + } + + return result +} + +func tagsToString(tags []ecs.TagItemType) string { + result := make([]string, 0, len(tags)) + + for _, tag := range tags { + ecsTags := ecs.TagItemType{ + TagKey: tag.TagKey, + TagValue: tag.TagValue, + } + result = append(result, ecsTags.TagKey+":"+ecsTags.TagValue) + } + + return strings.Join(result, ",") +} diff --git a/builtin/providers/alicloud/validators.go b/builtin/providers/alicloud/validators.go new file mode 100644 index 0000000000..7eb85ed431 --- /dev/null +++ b/builtin/providers/alicloud/validators.go @@ -0,0 +1,453 @@ +package alicloud + +import ( + "fmt" + "net" + "strconv" + "strings" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" + "github.com/denverdino/aliyungo/slb" + "regexp" +) + +// common +func validateInstancePort(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 1 || value > 65535 { + errors = append(errors, fmt.Errorf( + "%q must be a valid instance port between 1 and 65535", + k)) + return + } + return +} + +func validateInstanceProtocol(v interface{}, k string) (ws []string, errors []error) { + protocal := v.(string) + if !isProtocalValid(protocal) { + errors = append(errors, fmt.Errorf( + "%q is an invalid value. Valid values are either http, https, tcp or udp", + k)) + return + } + return +} + +// ecs +func validateDiskCategory(v interface{}, k string) (ws []string, errors []error) { + category := ecs.DiskCategory(v.(string)) + if category != ecs.DiskCategoryCloud && category != ecs.DiskCategoryCloudEfficiency && category != ecs.DiskCategoryCloudSSD { + errors = append(errors, fmt.Errorf("%s must be one of %s %s %s", k, ecs.DiskCategoryCloud, ecs.DiskCategoryCloudEfficiency, ecs.DiskCategoryCloudSSD)) + } + + return +} + +func validateInstanceName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 128 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 128 characters", k)) + } + + if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") { + errors = append(errors, fmt.Errorf("%s cannot starts with http:// or https://", k)) + } + + return +} + +func validateInstanceDescription(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k)) + + } + return +} + +func validateDiskName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if value == "" { + return + } + + if len(value) < 2 || len(value) > 128 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 128 characters", k)) + } + + if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") { + errors = append(errors, fmt.Errorf("%s cannot starts with http:// or https://", k)) + } + + return +} + +func validateDiskDescription(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k)) + + } + return +} + +//security group +func validateSecurityGroupName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 128 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 128 characters", k)) + } + + if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") { + errors = append(errors, fmt.Errorf("%s cannot starts with http:// or https://", k)) + } + + return +} + +func validateSecurityGroupDescription(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k)) + + } + return +} + +func validateSecurityRuleType(v interface{}, k string) (ws []string, errors []error) { + rt := GroupRuleDirection(v.(string)) + if rt != GroupRuleIngress && rt != GroupRuleEgress { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleIngress, GroupRuleEgress)) + } + + return +} + +func validateSecurityRuleIpProtocol(v interface{}, k string) (ws []string, errors []error) { + pt := GroupRuleIpProtocol(v.(string)) + if pt != GroupRuleTcp && pt != GroupRuleUdp && pt != GroupRuleIcmp && pt != GroupRuleGre && pt != GroupRuleAll { + errors = append(errors, fmt.Errorf("%s must be one of %s %s %s %s %s", k, + GroupRuleTcp, GroupRuleUdp, GroupRuleIcmp, GroupRuleGre, GroupRuleAll)) + } + + return +} + +func validateSecurityRuleNicType(v interface{}, k string) (ws []string, errors []error) { + pt := GroupRuleNicType(v.(string)) + if pt != GroupRuleInternet && pt != GroupRuleIntranet { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleInternet, GroupRuleIntranet)) + } + + return +} + +func validateSecurityRulePolicy(v interface{}, k string) (ws []string, errors []error) { + pt := GroupRulePolicy(v.(string)) + if pt != GroupRulePolicyAccept && pt != GroupRulePolicyDrop { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRulePolicyAccept, GroupRulePolicyDrop)) + } + + return +} + +func validateSecurityPriority(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 1 || value > 100 { + errors = append(errors, fmt.Errorf( + "%q must be a valid authorization policy priority between 1 and 100", + k)) + return + } + return +} + +// validateCIDRNetworkAddress ensures that the string value is a valid CIDR that +// represents a network address - it adds an error otherwise +func validateCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + _, ipnet, err := net.ParseCIDR(value) + if err != nil { + errors = append(errors, fmt.Errorf( + "%q must contain a valid CIDR, got error parsing: %s", k, err)) + return + } + + if ipnet == nil || value != ipnet.String() { + errors = append(errors, fmt.Errorf( + "%q must contain a valid network CIDR, expected %q, got %q", + k, ipnet, value)) + } + + return +} + +func validateRouteEntryNextHopType(v interface{}, k string) (ws []string, errors []error) { + nht := ecs.NextHopType(v.(string)) + if nht != ecs.NextHopIntance && nht != ecs.NextHopTunnel { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, + ecs.NextHopIntance, ecs.NextHopTunnel)) + } + + return +} + +func validateSwitchCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + _, ipnet, err := net.ParseCIDR(value) + if err != nil { + errors = append(errors, fmt.Errorf( + "%q must contain a valid CIDR, got error parsing: %s", k, err)) + return + } + + if ipnet == nil || value != ipnet.String() { + errors = append(errors, fmt.Errorf( + "%q must contain a valid network CIDR, expected %q, got %q", + k, ipnet, value)) + return + } + + mark, _ := strconv.Atoi(strings.Split(ipnet.String(), "/")[1]) + if mark < 16 || mark > 29 { + errors = append(errors, fmt.Errorf( + "%q must contain a network CIDR which mark between 16 and 29", + k)) + } + + return +} + +// validateIoOptimized ensures that the string value is a valid IoOptimized that +// represents a IoOptimized - it adds an error otherwise +func validateIoOptimized(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + ioOptimized := ecs.IoOptimized(value) + if ioOptimized != ecs.IoOptimizedNone && + ioOptimized != ecs.IoOptimizedOptimized { + errors = append(errors, fmt.Errorf( + "%q must contain a valid IoOptimized, expected %s or %s, got %q", + k, ecs.IoOptimizedNone, ecs.IoOptimizedOptimized, ioOptimized)) + } + } + + return +} + +// validateInstanceNetworkType ensures that the string value is a classic or vpc +func validateInstanceNetworkType(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + network := InstanceNetWork(value) + if network != ClassicNet && + network != VpcNet { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceNetworkType, expected %s or %s, go %q", + k, ClassicNet, VpcNet, network)) + } + } + return +} + +func validateInstanceChargeType(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + chargeType := common.InstanceChargeType(value) + if chargeType != common.PrePaid && + chargeType != common.PostPaid { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceChargeType, expected %s or %s, got %q", + k, common.PrePaid, common.PostPaid, chargeType)) + } + } + + return +} + +func validateInternetChargeType(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + chargeType := common.InternetChargeType(value) + if chargeType != common.PayByBandwidth && + chargeType != common.PayByTraffic { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceChargeType, expected %s or %s, got %q", + k, common.PayByBandwidth, common.PayByTraffic, chargeType)) + } + } + + return +} + +func validateInternetMaxBandWidthOut(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 1 || value > 100 { + errors = append(errors, fmt.Errorf( + "%q must be a valid internet bandwidth out between 1 and 1000", + k)) + return + } + return +} + +// SLB +func validateSlbName(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + if len(value) < 1 || len(value) > 80 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer name characters between 1 and 80", + k)) + return + } + } + + return +} + +func validateSlbInternetChargeType(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + chargeType := common.InternetChargeType(value) + + if chargeType != "paybybandwidth" && + chargeType != "paybytraffic" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceChargeType, expected %s or %s, got %q", + k, "paybybandwidth", "paybytraffic", value)) + } + } + + return +} + +func validateSlbBandwidth(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 1 || value > 1000 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer bandwidth between 1 and 1000", + k)) + return + } + return +} + +func validateSlbListenerBandwidth(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if (value < 1 || value > 1000) && value != -1 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer bandwidth between 1 and 1000 or -1", + k)) + return + } + return +} + +func validateSlbListenerScheduler(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + scheduler := slb.SchedulerType(value) + + if scheduler != "wrr" && scheduler != "wlc" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid SchedulerType, expected %s or %s, got %q", + k, "wrr", "wlc", value)) + } + } + + return +} + +func validateSlbListenerStickySession(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + flag := slb.FlagType(value) + + if flag != "on" && flag != "off" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid StickySession, expected %s or %s, got %q", + k, "on", "off", value)) + } + } + return +} + +func validateSlbListenerStickySessionType(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + flag := slb.StickySessionType(value) + + if flag != "insert" && flag != "server" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid StickySessionType, expected %s or %s, got %q", + k, "insert", "server", value)) + } + } + return +} + +func validateSlbListenerCookie(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + flag := slb.StickySessionType(value) + + if flag != "insert" && flag != "server" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid StickySessionType, expected %s or %s, got %q", + k, "insert", "server", value)) + } + } + return +} + +func validateSlbListenerPersistenceTimeout(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 0 || value > 86400 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer persistence timeout between 0 and 86400", + k)) + return + } + return +} + +//data source validate func +//data_source_alicloud_image +func validateNameRegex(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if _, err := regexp.Compile(value); err != nil { + errors = append(errors, fmt.Errorf( + "%q contains an invalid regular expression: %s", + k, err)) + } + return +} + +func validateImageOwners(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + owners := ecs.ImageOwnerAlias(value) + if owners != ecs.ImageOwnerSystem && + owners != ecs.ImageOwnerSelf && + owners != ecs.ImageOwnerOthers && + owners != ecs.ImageOwnerMarketplace && + owners != ecs.ImageOwnerDefault { + errors = append(errors, fmt.Errorf( + "%q must contain a valid Image owner , expected %s, %s, %s, %s or %s, got %q", + k, ecs.ImageOwnerSystem, ecs.ImageOwnerSelf, ecs.ImageOwnerOthers, ecs.ImageOwnerMarketplace, ecs.ImageOwnerDefault, owners)) + } + } + return +} + +func validateRegion(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + region := common.Region(value) + var valid string + for _, re := range common.ValidRegions { + if region == re { + return + } + valid = valid + ", " + string(re) + } + errors = append(errors, fmt.Errorf( + "%q must contain a valid Region ID , expected %#v, got %q", + k, valid, value)) + + } + return +} diff --git a/builtin/providers/alicloud/validators_test.go b/builtin/providers/alicloud/validators_test.go new file mode 100644 index 0000000000..fa5a8aed00 --- /dev/null +++ b/builtin/providers/alicloud/validators_test.go @@ -0,0 +1,429 @@ +package alicloud + +import "testing" + +func TestValidateInstancePort(t *testing.T) { + validPorts := []int{1, 22, 80, 100, 8088, 65535} + for _, v := range validPorts { + _, errors := validateInstancePort(v, "instance_port") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance port number between 1 and 65535: %q", v, errors) + } + } + + invalidPorts := []int{-10, -1, 0} + for _, v := range invalidPorts { + _, errors := validateInstancePort(v, "instance_port") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance port number", v) + } + } +} + +func TestValidateInstanceProtocol(t *testing.T) { + validProtocals := []string{"http", "tcp", "https", "udp"} + for _, v := range validProtocals { + _, errors := validateInstanceProtocol(v, "instance_protocal") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance protocol: %q", v, errors) + } + } + + invalidProtocals := []string{"HTTP", "abc", "ecmp", "dubbo"} + for _, v := range invalidProtocals { + _, errors := validateInstanceProtocol(v, "instance_protocal") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance protocol", v) + } + } +} + +func TestValidateInstanceDiskCategory(t *testing.T) { + validDiskCategory := []string{"cloud", "cloud_efficiency", "cloud_ssd"} + for _, v := range validDiskCategory { + _, errors := validateDiskCategory(v, "instance_disk_category") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance disk category: %q", v, errors) + } + } + + invalidDiskCategory := []string{"all", "ephemeral", "ephemeral_ssd", "ALL", "efficiency"} + for _, v := range invalidDiskCategory { + _, errors := validateDiskCategory(v, "instance_disk_category") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance disk category", v) + } + } +} + +func TestValidateInstanceName(t *testing.T) { + validInstanceName := []string{"hi", "hi http://", "some word + any word &", "http", "中文"} + for _, v := range validInstanceName { + _, errors := validateInstanceName(v, "instance_name") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance name: %q", v, errors) + } + } + + invalidInstanceName := []string{"y", "http://", "https://", "+"} + for _, v := range invalidInstanceName { + _, errors := validateInstanceName(v, "instance_name") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance name", v) + } + } +} + +func TestValidateInstanceDescription(t *testing.T) { + validInstanceDescription := []string{"hi", "hi http://", "some word + any word &", "http://", "中文"} + for _, v := range validInstanceDescription { + _, errors := validateInstanceDescription(v, "instance_description") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance description: %q", v, errors) + } + } + + invalidvalidInstanceDescription := []string{"y", ""} + for _, v := range invalidvalidInstanceDescription { + _, errors := validateInstanceName(v, "instance_description") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance description", v) + } + } +} + +func TestValidateSecurityGroupName(t *testing.T) { + validSecurityGroupName := []string{"hi", "hi http://", "some word + any word &", "http", "中文", "12345"} + for _, v := range validSecurityGroupName { + _, errors := validateSecurityGroupName(v, "security_group_name") + if len(errors) != 0 { + t.Fatalf("%q should be a valid security group name: %q", v, errors) + } + } + + invalidSecurityGroupName := []string{"y", "http://", "https://", "+"} + for _, v := range invalidSecurityGroupName { + _, errors := validateSecurityGroupName(v, "security_group_name") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid security group name", v) + } + } +} + +func TestValidateSecurityGroupDescription(t *testing.T) { + validSecurityGroupDescription := []string{"hi", "hi http://", "some word + any word &", "http://", "中文"} + for _, v := range validSecurityGroupDescription { + _, errors := validateSecurityGroupDescription(v, "security_group_description") + if len(errors) != 0 { + t.Fatalf("%q should be a valid security group description: %q", v, errors) + } + } + + invalidSecurityGroupDescription := []string{"y", ""} + for _, v := range invalidSecurityGroupDescription { + _, errors := validateSecurityGroupDescription(v, "security_group_description") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid security group description", v) + } + } +} + +func TestValidateSecurityRuleType(t *testing.T) { + validSecurityRuleType := []string{"ingress", "egress"} + for _, v := range validSecurityRuleType { + _, errors := validateSecurityRuleType(v, "security_rule_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid security rule type: %q", v, errors) + } + } + + invalidSecurityRuleType := []string{"y", "gress", "in", "out"} + for _, v := range invalidSecurityRuleType { + _, errors := validateSecurityRuleType(v, "security_rule_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid security rule type", v) + } + } +} + +func TestValidateSecurityRuleIpProtocol(t *testing.T) { + validIpProtocol := []string{"tcp", "udp", "icmp", "gre", "all"} + for _, v := range validIpProtocol { + _, errors := validateSecurityRuleIpProtocol(v, "security_rule_ip_protocol") + if len(errors) != 0 { + t.Fatalf("%q should be a valid ip protocol: %q", v, errors) + } + } + + invalidIpProtocol := []string{"y", "ecmp", "http", "https"} + for _, v := range invalidIpProtocol { + _, errors := validateSecurityRuleIpProtocol(v, "security_rule_ip_protocol") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid ip protocol", v) + } + } +} + +func TestValidateSecurityRuleNicType(t *testing.T) { + validRuleNicType := []string{"intranet", "internet"} + for _, v := range validRuleNicType { + _, errors := validateSecurityRuleNicType(v, "security_rule_nic_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid nic type: %q", v, errors) + } + } + + invalidRuleNicType := []string{"inter", "ecmp", "http", "https"} + for _, v := range invalidRuleNicType { + _, errors := validateSecurityRuleNicType(v, "security_rule_nic_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid nic type", v) + } + } +} + +func TestValidateSecurityRulePolicy(t *testing.T) { + validRulePolicy := []string{"accept", "drop"} + for _, v := range validRulePolicy { + _, errors := validateSecurityRulePolicy(v, "security_rule_policy") + if len(errors) != 0 { + t.Fatalf("%q should be a valid security rule policy: %q", v, errors) + } + } + + invalidRulePolicy := []string{"inter", "ecmp", "http", "https"} + for _, v := range invalidRulePolicy { + _, errors := validateSecurityRulePolicy(v, "security_rule_policy") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid security rule policy", v) + } + } +} + +func TestValidateSecurityRulePriority(t *testing.T) { + validPriority := []int{1, 50, 100} + for _, v := range validPriority { + _, errors := validateSecurityPriority(v, "security_rule_priority") + if len(errors) != 0 { + t.Fatalf("%q should be a valid security rule priority: %q", v, errors) + } + } + + invalidPriority := []int{-1, 0, 101} + for _, v := range invalidPriority { + _, errors := validateSecurityPriority(v, "security_rule_priority") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid security rule priority", v) + } + } +} + +func TestValidateCIDRNetworkAddress(t *testing.T) { + validCIDRNetworkAddress := []string{"192.168.10.0/24", "0.0.0.0/0", "10.121.10.0/24"} + for _, v := range validCIDRNetworkAddress { + _, errors := validateCIDRNetworkAddress(v, "cidr_network_address") + if len(errors) != 0 { + t.Fatalf("%q should be a valid cidr network address: %q", v, errors) + } + } + + invalidCIDRNetworkAddress := []string{"1.2.3.4", "0x38732/21"} + for _, v := range invalidCIDRNetworkAddress { + _, errors := validateCIDRNetworkAddress(v, "cidr_network_address") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid cidr network address", v) + } + } +} + +func TestValidateRouteEntryNextHopType(t *testing.T) { + validNexthopType := []string{"Instance", "Tunnel"} + for _, v := range validNexthopType { + _, errors := validateRouteEntryNextHopType(v, "route_entry_nexthop_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid route entry nexthop type: %q", v, errors) + } + } + + invalidNexthopType := []string{"ri", "vpc"} + for _, v := range invalidNexthopType { + _, errors := validateRouteEntryNextHopType(v, "route_entry_nexthop_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid route entry nexthop type", v) + } + } +} + +func TestValidateSwitchCIDRNetworkAddress(t *testing.T) { + validSwitchCIDRNetworkAddress := []string{"192.168.10.0/24", "0.0.0.0/16", "127.0.0.0/29", "10.121.10.0/24"} + for _, v := range validSwitchCIDRNetworkAddress { + _, errors := validateSwitchCIDRNetworkAddress(v, "switch_cidr_network_address") + if len(errors) != 0 { + t.Fatalf("%q should be a valid switch cidr network address: %q", v, errors) + } + } + + invalidSwitchCIDRNetworkAddress := []string{"1.2.3.4", "0x38732/21", "10.121.10.0/15", "10.121.10.0/30", "256.121.10.0/22"} + for _, v := range invalidSwitchCIDRNetworkAddress { + _, errors := validateSwitchCIDRNetworkAddress(v, "switch_cidr_network_address") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid switch cidr network address", v) + } + } +} + +func TestValidateIoOptimized(t *testing.T) { + validIoOptimized := []string{"", "none", "optimized"} + for _, v := range validIoOptimized { + _, errors := validateIoOptimized(v, "ioOptimized") + if len(errors) != 0 { + t.Fatalf("%q should be a valid IoOptimized value: %q", v, errors) + } + } + + invalidIoOptimized := []string{"true", "ioOptimized"} + for _, v := range invalidIoOptimized { + _, errors := validateIoOptimized(v, "ioOptimized") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid IoOptimized value", v) + } + } +} + +func TestValidateInstanceNetworkType(t *testing.T) { + validInstanceNetworkType := []string{"", "classic", "vpc"} + for _, v := range validInstanceNetworkType { + _, errors := validateInstanceNetworkType(v, "instance_network_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance network type value: %q", v, errors) + } + } + + invalidInstanceNetworkType := []string{"Classic", "vswitch", "123"} + for _, v := range invalidInstanceNetworkType { + _, errors := validateInstanceNetworkType(v, "instance_network_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance network type value", v) + } + } +} + +func TestValidateInstanceChargeType(t *testing.T) { + validInstanceChargeType := []string{"", "PrePaid", "PostPaid"} + for _, v := range validInstanceChargeType { + _, errors := validateInstanceChargeType(v, "instance_charge_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid instance charge type value: %q", v, errors) + } + } + + invalidInstanceChargeType := []string{"prepay", "yearly", "123"} + for _, v := range invalidInstanceChargeType { + _, errors := validateInstanceChargeType(v, "instance_charge_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid instance charge type value", v) + } + } +} + +func TestValidateInternetChargeType(t *testing.T) { + validInternetChargeType := []string{"", "PayByBandwidth", "PayByTraffic"} + for _, v := range validInternetChargeType { + _, errors := validateInternetChargeType(v, "internet_charge_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid internet charge type value: %q", v, errors) + } + } + + invalidInternetChargeType := []string{"paybybandwidth", "paybytraffic", "123"} + for _, v := range invalidInternetChargeType { + _, errors := validateInternetChargeType(v, "internet_charge_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid internet charge type value", v) + } + } +} + +func TestValidateInternetMaxBandWidthOut(t *testing.T) { + validInternetMaxBandWidthOut := []int{1, 22, 100} + for _, v := range validInternetMaxBandWidthOut { + _, errors := validateInternetMaxBandWidthOut(v, "internet_max_bandwidth_out") + if len(errors) != 0 { + t.Fatalf("%q should be a valid internet max bandwidth out value: %q", v, errors) + } + } + + invalidInternetMaxBandWidthOut := []int{-2, 0, 101, 123} + for _, v := range invalidInternetMaxBandWidthOut { + _, errors := validateInternetMaxBandWidthOut(v, "internet_max_bandwidth_out") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid internet max bandwidth out value", v) + } + } +} + +func TestValidateSlbName(t *testing.T) { + validSlbName := []string{"h", "http://", "123", "hello, aliyun! "} + for _, v := range validSlbName { + _, errors := validateSlbName(v, "slb_name") + if len(errors) != 0 { + t.Fatalf("%q should be a valid slb name: %q", v, errors) + } + } + + // todo: add invalid case +} + +func TestValidateSlbInternetChargeType(t *testing.T) { + validSlbInternetChargeType := []string{"paybybandwidth", "paybytraffic"} + for _, v := range validSlbInternetChargeType { + _, errors := validateSlbInternetChargeType(v, "slb_internet_charge_type") + if len(errors) != 0 { + t.Fatalf("%q should be a valid slb internet charge type value: %q", v, errors) + } + } + + invalidSlbInternetChargeType := []string{"PayByBandwidth", "PayByTraffic"} + for _, v := range invalidSlbInternetChargeType { + _, errors := validateSlbInternetChargeType(v, "slb_internet_charge_type") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid slb internet charge type value", v) + } + } +} + +func TestValidateSlbBandwidth(t *testing.T) { + validSlbBandwidth := []int{1, 22, 1000} + for _, v := range validSlbBandwidth { + _, errors := validateSlbBandwidth(v, "slb_bandwidth") + if len(errors) != 0 { + t.Fatalf("%q should be a valid slb bandwidth value: %q", v, errors) + } + } + + invalidSlbBandwidth := []int{-2, 0, 1001} + for _, v := range invalidSlbBandwidth { + _, errors := validateSlbBandwidth(v, "slb_bandwidth") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid slb bandwidth value", v) + } + } +} + +func TestValidateSlbListenerBandwidth(t *testing.T) { + validSlbListenerBandwidth := []int{-1, 1, 22, 1000} + for _, v := range validSlbListenerBandwidth { + _, errors := validateSlbListenerBandwidth(v, "slb_bandwidth") + if len(errors) != 0 { + t.Fatalf("%q should be a valid slb listener bandwidth value: %q", v, errors) + } + } + + invalidSlbListenerBandwidth := []int{-2, 0, -10, 1001} + for _, v := range invalidSlbListenerBandwidth { + _, errors := validateSlbListenerBandwidth(v, "slb_bandwidth") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid slb listener bandwidth value", v) + } + } +} diff --git a/examples/alicloud-ecs-image/README.md b/examples/alicloud-ecs-image/README.md new file mode 100644 index 0000000000..a6a76d8e5a --- /dev/null +++ b/examples/alicloud-ecs-image/README.md @@ -0,0 +1,27 @@ +### ECS Example + +The example gains image info and use it to launche ECS instance, disk, and attached the disk on ECS. the count parameter in variables.tf can let you gain specify image and use it to create specify number ECS instances. + +### Get up and running + +* Planning phase + + terraform plan + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + .... + +* Apply phase + + terraform apply + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + .... + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-image/main.tf b/examples/alicloud-ecs-image/main.tf new file mode 100644 index 0000000000..743e6b9e1d --- /dev/null +++ b/examples/alicloud-ecs-image/main.tf @@ -0,0 +1,55 @@ +data "alicloud_images" "ecs_image" { + most_recent = "${var.most_recent}" + owners = "${var.image_owners}" + name_regex = "${var.name_regex}" +} + +resource "alicloud_security_group" "group" { + name = "${var.short_name}" + description = "New security group" +} + + +resource "alicloud_disk" "disk" { + availability_zone = "${var.availability_zones}" + category = "${var.disk_category}" + size = "${var.disk_size}" + count = "${var.count}" +} + +resource "alicloud_instance" "instance" { + instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + image_id = "${data.alicloud_images.ecs_image.images.0.id}" + instance_type = "${var.ecs_type}" + count = "${var.count}" + availability_zone = "${var.availability_zones}" + security_groups = ["${alicloud_security_group.group.*.id}"] + + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" + + io_optimized = "${var.io_optimized}" + + password = "${var.ecs_password}" + + allocate_public_ip = "${var.allocate_public_ip}" + + instance_charge_type = "PostPaid" + system_disk_category = "cloud_efficiency" + + + tags { + role = "${var.role}" + dc = "${var.datacenter}" + } + +} + +resource "alicloud_disk_attachment" "instance-attachment" { + count = "${var.count}" + disk_id = "${element(alicloud_disk.disk.*.id, count.index)}" + instance_id = "${element(alicloud_instance.instance.*.id, count.index)}" + device_name = "${var.device_name}" +} + diff --git a/examples/alicloud-ecs-image/outputs.tf b/examples/alicloud-ecs-image/outputs.tf new file mode 100644 index 0000000000..104f2389fc --- /dev/null +++ b/examples/alicloud-ecs-image/outputs.tf @@ -0,0 +1,15 @@ +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} + +output "ecs_public_ip" { + value = "${join(",", alicloud_instance.instance.*.public_ip)}" +} + +output "tags" { + value = "${jsonencode(alicloud_instance.instance.tags)}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-image/variables.tf b/examples/alicloud-ecs-image/variables.tf new file mode 100644 index 0000000000..35007e8c80 --- /dev/null +++ b/examples/alicloud-ecs-image/variables.tf @@ -0,0 +1,57 @@ +variable "count" { + default = "1" +} +variable "count_format" { + default = "%02d" +} +variable "most_recent" { + default = true +} + +variable "image_owners" { + default = "" +} + +variable "name_regex" { + default = "^centos_6\\w{1,5}[64].*" +} + +variable "role" { + default = "work" +} +variable "datacenter" { + default = "beijing" +} +variable "short_name" { + default = "hi" +} +variable "ecs_type" { + default = "ecs.n1.small" +} +variable "ecs_password" { + default = "Test12345" +} +variable "availability_zones" { + default = "cn-beijing-b" +} +variable "allocate_public_ip" { + default = true +} +variable "internet_charge_type" { + default = "PayByTraffic" +} +variable "internet_max_bandwidth_out" { + default = 5 +} +variable "io_optimized" { + default = "optimized" +} +variable "disk_category" { + default = "cloud_ssd" +} +variable "disk_size" { + default = "40" +} +variable "device_name" { + default = "/dev/xvdb" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-slb/README.md b/examples/alicloud-ecs-slb/README.md new file mode 100644 index 0000000000..db4b4631e3 --- /dev/null +++ b/examples/alicloud-ecs-slb/README.md @@ -0,0 +1,18 @@ +### ECS With SLB Example + +The example launches ECS, disk, and attached the disk on ECS. It also creates an SLB, and addition the ECS to backendServer. The variables.tf can let you create specify parameter instances, such as image_id, ecs_type etc. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-slb/main.tf b/examples/alicloud-ecs-slb/main.tf new file mode 100644 index 0000000000..fad5c77682 --- /dev/null +++ b/examples/alicloud-ecs-slb/main.tf @@ -0,0 +1,53 @@ +resource "alicloud_security_group" "group" { + name = "${var.short_name}" + description = "New security group" +} + +resource "alicloud_instance" "instance" { + instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + image_id = "${var.image_id}" + instance_type = "${var.ecs_type}" + count = "${var.count}" + availability_zone = "${var.availability_zones}" + security_groups = ["${alicloud_security_group.group.*.id}"] + + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" + + io_optimized = "${var.io_optimized}" + + password = "${var.ecs_password}" + + allocate_public_ip = "${var.allocate_public_ip}" + + instance_charge_type = "PostPaid" + system_disk_category = "cloud_efficiency" + + + tags { + role = "${var.role}" + dc = "${var.datacenter}" + } + +} + +resource "alicloud_slb" "instance" { + name = "${var.slb_name}" + internet_charge_type = "${var.slb_internet_charge_type}" + internet = "${var.internet}" + + listener = [ + { + "instance_port" = "2111" + "lb_port" = "21" + "lb_protocol" = "tcp" + "bandwidth" = "5" + }] +} + + +resource "alicloud_slb_attachment" "default" { + slb_id = "${alicloud_slb.instance.id}" + instances = ["${alicloud_instance.instance.*.id}"] +} \ No newline at end of file diff --git a/examples/alicloud-ecs-slb/outputs.tf b/examples/alicloud-ecs-slb/outputs.tf new file mode 100644 index 0000000000..9c7d3800ee --- /dev/null +++ b/examples/alicloud-ecs-slb/outputs.tf @@ -0,0 +1,20 @@ +output "slb_id" { + value = "${alicloud_slb.instance.id}" +} + +output "slbname" { + value = "${alicloud_slb.instance.name}" +} + +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} + +output "slb_backendserver" { + value = "${alicloud_slb_attachment.default.backend_servers}" +} + diff --git a/examples/alicloud-ecs-slb/variables.tf b/examples/alicloud-ecs-slb/variables.tf new file mode 100644 index 0000000000..20896631a2 --- /dev/null +++ b/examples/alicloud-ecs-slb/variables.tf @@ -0,0 +1,58 @@ +variable "count" { + default = "1" +} +variable "count_format" { + default = "%02d" +} +variable "image_id" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} + +variable "role" { + default = "worder" +} +variable "datacenter" { + default = "beijing" +} +variable "short_name" { + default = "hi" +} +variable "ecs_type" { + default = "ecs.n1.small" +} +variable "ecs_password" { + default = "Test12345" +} +variable "availability_zones" { + default = "cn-beijing-b" +} +variable "ssh_username" { + default = "root" +} + +variable "allocate_public_ip" { + default = true +} + +variable "internet_charge_type" { + default = "PayByTraffic" +} + +variable "slb_internet_charge_type" { + default = "paybytraffic" +} +variable "internet_max_bandwidth_out" { + default = 5 +} + +variable "io_optimized" { + default = "optimized" +} + +variable "slb_name" { + default = "slb_worder" +} + +variable "internet" { + default = true +} diff --git a/examples/alicloud-ecs-special-sg/README.md b/examples/alicloud-ecs-special-sg/README.md new file mode 100644 index 0000000000..6e9e482a9f --- /dev/null +++ b/examples/alicloud-ecs-special-sg/README.md @@ -0,0 +1,20 @@ +### ECS With special SLB and SecurityGroup Example + +The example launches 6 ECS and create it on special SLB and securityGroup. +Also additional first and second instance to the SLB backend server. +The variables.tf can let you create specify parameter instances, such as image_id, ecs_type etc. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-special-sg/main.tf b/examples/alicloud-ecs-special-sg/main.tf new file mode 100644 index 0000000000..ba92486a6d --- /dev/null +++ b/examples/alicloud-ecs-special-sg/main.tf @@ -0,0 +1,39 @@ +provider "alicloud" { + alias = "bj" + region = "cn-beijing" +} + +resource "alicloud_instance" "instance" { + provider = "alicloud.bj" + instance_name = "website-${format(var.count_format, count.index+1)}" + host_name = "website-${format(var.count_format, count.index+1)}" + image_id = "centos7u2_64_40G_cloudinit_20160728.raw" + instance_type = "ecs.s2.large" + count = "6" + availability_zone = "cn-beijing-b" + security_groups = "${var.security_groups}" + + internet_charge_type = "PayByBandwidth" + + io_optimized = "none" + + password = "${var.ecs_password}" + + allocate_public_ip = "false" + + instance_charge_type = "PostPaid" + system_disk_category = "cloud" + + + tags { + env = "prod" + product = "website" + dc = "beijing" + } + +} + +resource "alicloud_slb_attachment" "foo" { + slb_id = "${var.slb_id}" + instances = ["${alicloud_instance.instance.0.id}", "${alicloud_instance.instance.1.id}"] +} \ No newline at end of file diff --git a/examples/alicloud-ecs-special-sg/outputs.tf b/examples/alicloud-ecs-special-sg/outputs.tf new file mode 100644 index 0000000000..33cb4a7e8f --- /dev/null +++ b/examples/alicloud-ecs-special-sg/outputs.tf @@ -0,0 +1,7 @@ +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} diff --git a/examples/alicloud-ecs-special-sg/variables.tf b/examples/alicloud-ecs-special-sg/variables.tf new file mode 100644 index 0000000000..694b8000fd --- /dev/null +++ b/examples/alicloud-ecs-special-sg/variables.tf @@ -0,0 +1,17 @@ +variable "count" { + default = "6" +} +variable "count_format" { + default = "%02d" +} + +variable "security_groups" { + type = "list" + default = ["sg-2zecd09tw30jo1c7ekdi"] +} +variable "ecs_password" { + default = "Test12345" +} +variable "slb_id"{ + default = "lb-2zel5fjqk1qgmwud7t3xb" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-userdata/README.md b/examples/alicloud-ecs-userdata/README.md new file mode 100644 index 0000000000..f58f16c0d9 --- /dev/null +++ b/examples/alicloud-ecs-userdata/README.md @@ -0,0 +1,17 @@ +### ECS with UserData Example + +Pass shell scripts to Ecs Instance by user_data parameter. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-userdata/main.tf b/examples/alicloud-ecs-userdata/main.tf new file mode 100644 index 0000000000..99376325f3 --- /dev/null +++ b/examples/alicloud-ecs-userdata/main.tf @@ -0,0 +1,37 @@ + +resource "alicloud_vpc" "default" { + name = "tf-vpc" + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vswitch" "vsw" { + vpc_id = "${alicloud_vpc.default.id}" + cidr_block = "${var.vswitch_cidr}" + availability_zone = "${var.zone}" +} + +resource "alicloud_security_group" "sg" { + name = "tf-sg" + description = "sg" + vpc_id = "${alicloud_vpc.default.id}" +} + +resource "alicloud_instance" "website" { + # cn-beijing + availability_zone = "${var.zone}" + vswitch_id = "${alicloud_vswitch.vsw.id}" + image_id = "${var.image}" + + # series II + instance_type = "${var.ecs_type}" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + + internet_charge_type = "PayByTraffic" + internet_max_bandwidth_out = 5 + allocate_public_ip = true + security_groups = ["${alicloud_security_group.sg.id}"] + instance_name = "test_foo" + + user_data = "${file("userdata.sh")}" +} diff --git a/examples/alicloud-ecs-userdata/outputs.tf b/examples/alicloud-ecs-userdata/outputs.tf new file mode 100644 index 0000000000..7115e9247f --- /dev/null +++ b/examples/alicloud-ecs-userdata/outputs.tf @@ -0,0 +1,7 @@ +output "hostname" { + value = "${alicloud_instance.website.instance_name}" +} + +output "ecs_id" { + value = "${alicloud_instance.website.id}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-userdata/userdata.sh b/examples/alicloud-ecs-userdata/userdata.sh new file mode 100644 index 0000000000..dba9b88914 --- /dev/null +++ b/examples/alicloud-ecs-userdata/userdata.sh @@ -0,0 +1,6 @@ +#!/bin/bash -v +apt-get update -y +apt-get install -y nginx > /tmp/nginx.log + +cd / +mkdir -p alicloud/go \ No newline at end of file diff --git a/examples/alicloud-ecs-userdata/variables.tf b/examples/alicloud-ecs-userdata/variables.tf new file mode 100644 index 0000000000..d8809ad838 --- /dev/null +++ b/examples/alicloud-ecs-userdata/variables.tf @@ -0,0 +1,19 @@ +variable "vpc_cidr" { + default = "172.16.0.0/12" +} + +variable "vswitch_cidr" { + default = "172.16.0.0/21" +} + +variable "zone" { + default = "cn-beijing-b" +} + +variable "image" { + default = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" +} + +variable "ecs_type" { + default = "ecs.n1.medium" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc-cluster/README.md b/examples/alicloud-ecs-vpc-cluster/README.md new file mode 100644 index 0000000000..62fec8cc0b --- /dev/null +++ b/examples/alicloud-ecs-vpc-cluster/README.md @@ -0,0 +1,19 @@ +### VPC Cluster Example + +The example launches VPC cluster, include VPC, VSwitch, Nategateway, ECS, SecurityGroups. the example used the "module" to create instances. The variables.tf can let you create specify parameter instances, such as image_id, ecs_type, count etc. + +### Get up and running + +* Planning phase + + terraform plan + + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc-cluster/main.tf b/examples/alicloud-ecs-vpc-cluster/main.tf new file mode 100644 index 0000000000..0ec8bf8a00 --- /dev/null +++ b/examples/alicloud-ecs-vpc-cluster/main.tf @@ -0,0 +1,62 @@ +provider "alicloud" { + region = "${var.region}" +} + +module "vpc" { + availability_zones = "${var.availability_zones}" + source = "../alicloud-vpc" + short_name = "${var.short_name}" + region = "${var.region}" +} + +module "security-groups" { + source = "../alicloud-vpc-cluster-sg" + short_name = "${var.short_name}" + vpc_id = "${module.vpc.vpc_id}" +} + +module "control-nodes" { + source = "../alicloud-ecs-vpc" + count = "${var.control_count}" + role = "control" + datacenter = "${var.datacenter}" + ecs_type = "${var.control_ecs_type}" + ecs_password = "${var.ecs_password}" + disk_size = "${var.control_disk_size}" + ssh_username = "${var.ssh_username}" + short_name = "${var.short_name}" + availability_zones = "${module.vpc.availability_zones}" + security_groups = ["${module.security-groups.control_security_group}"] + vswitch_id = "${module.vpc.vswitch_ids}" + internet_charge_type = "${var.internet_charge_type}" +} + +module "edge-nodes" { + source = "../alicloud-ecs-vpc" + count = "${var.edge_count}" + role = "edge" + datacenter = "${var.datacenter}" + ecs_type = "${var.edge_ecs_type}" + ecs_password = "${var.ecs_password}" + ssh_username = "${var.ssh_username}" + short_name = "${var.short_name}" + availability_zones = "${module.vpc.availability_zones}" + security_groups = ["${module.security-groups.worker_security_group}"] + vswitch_id = "${module.vpc.vswitch_ids}" + internet_charge_type = "${var.internet_charge_type}" +} + +module "worker-nodes" { + source = "../alicloud-ecs-vpc" + count = "${var.worker_count}" + role = "worker" + datacenter = "${var.datacenter}" + ecs_type = "${var.worker_ecs_type}" + ecs_password = "${var.ecs_password}" + ssh_username = "${var.ssh_username}" + short_name = "${var.short_name}" + availability_zones = "${module.vpc.availability_zones}" + security_groups = ["${module.security-groups.worker_security_group}"] + vswitch_id = "${module.vpc.vswitch_ids}" + internet_charge_type = "${var.internet_charge_type}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc-cluster/variables.tf b/examples/alicloud-ecs-vpc-cluster/variables.tf new file mode 100644 index 0000000000..7af6118624 --- /dev/null +++ b/examples/alicloud-ecs-vpc-cluster/variables.tf @@ -0,0 +1,59 @@ +variable "ecs_password" { + default = "Test12345" +} + +variable "control_count" { + default = "3" +} +variable "control_count_format" { + default = "%02d" +} +variable "control_ecs_type" { + default = "ecs.n1.medium" +} +variable "control_disk_size" { + default = "100" +} + +variable "edge_count" { + default = "2" +} +variable "edge_count_format" { + default = "%02d" +} +variable "edge_ecs_type" { + default = "ecs.n1.small" +} + +variable "worker_count" { + default = "1" +} +variable "worker_count_format" { + default = "%03d" +} +variable "worker_ecs_type" { + default = "ecs.n1.small" +} + +variable "short_name" { + default = "ali" +} +variable "ssh_username" { + default = "root" +} + +variable "region" { + default = "cn-beijing" +} + +variable "availability_zones" { + default = "cn-beijing-c" +} + +variable "internet_charge_type" { + default = "" +} + +variable "datacenter" { + default = "beijing" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc/README.md b/examples/alicloud-ecs-vpc/README.md new file mode 100644 index 0000000000..bfc92c494c --- /dev/null +++ b/examples/alicloud-ecs-vpc/README.md @@ -0,0 +1,31 @@ +### ECS In VPC Example + +The example launches ECS in VPC, vswitch_id parameter is the vswitch id from VPC. It also create disk, and attached the disk on ECS. The variables.tf can let you create specify parameter instances, such as image_id, ecs_type, count etc. + +### Get up and running + +* Planning phase + + terraform plan + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + var.vswitch_id + Enter a value: {vswitch_id} + .... + +* Apply phase + + terraform apply + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + var.vswitch_id + Enter a value: {vswitch_id} + .... + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc/main.tf b/examples/alicloud-ecs-vpc/main.tf new file mode 100644 index 0000000000..a4283ccd99 --- /dev/null +++ b/examples/alicloud-ecs-vpc/main.tf @@ -0,0 +1,45 @@ +resource "alicloud_disk" "disk" { + availability_zone = "${var.availability_zones}" + category = "${var.disk_category}" + size = "${var.disk_size}" + count = "${var.count}" +} + +resource "alicloud_instance" "instance" { + instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + image_id = "${var.image_id}" + instance_type = "${var.ecs_type}" + count = "${var.count}" + availability_zone = "${var.availability_zones}" + security_groups = ["${var.security_groups}"] + vswitch_id = "${var.vswitch_id}" + + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" + + io_optimized = "${var.io_optimized}" + + allocate_public_ip = "${var.allocate_public_ip}" + + password = "${var.ecs_password}" + + instance_charge_type = "${var.instance_charge_type}" + system_disk_category = "${var.system_disk_category}" + + + tags { + role = "${var.role}" + dc = "${var.datacenter}" + } + +} + +resource "alicloud_disk_attachment" "instance-attachment" { + count = "${var.count}" + disk_id = "${element(alicloud_disk.disk.*.id, count.index)}" + instance_id = "${element(alicloud_instance.instance.*.id, count.index)}" + device_name = "${var.device_name}" +} + + diff --git a/examples/alicloud-ecs-vpc/outputs.tf b/examples/alicloud-ecs-vpc/outputs.tf new file mode 100644 index 0000000000..d1b7c8a19d --- /dev/null +++ b/examples/alicloud-ecs-vpc/outputs.tf @@ -0,0 +1,7 @@ +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc/variables.tf b/examples/alicloud-ecs-vpc/variables.tf new file mode 100644 index 0000000000..67664e4255 --- /dev/null +++ b/examples/alicloud-ecs-vpc/variables.tf @@ -0,0 +1,67 @@ +variable "count" { + default = "1" +} +variable "count_format" { + default = "%02d" +} +variable "image_id" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} + +variable "role" { +} +variable "datacenter" { +} +variable "short_name" { + default = "hi" +} +variable "ecs_type" { +} +variable "ecs_password" { +} +variable "availability_zones" { +} +variable "security_groups" { + type = "list" +} +variable "ssh_username" { + default = "root" +} + +//if instance_charge_type is "PrePaid", then must be set period, the value is 1 to 30, unit is month +variable "instance_charge_type" { + default = "PostPaid" +} + +variable "system_disk_category" { + default = "cloud_efficiency" +} + +variable "internet_charge_type" { + default = "PayByTraffic" +} +variable "internet_max_bandwidth_out" { + default = 5 +} + +variable "io_optimized" { + default = "optimized" +} + +variable "allocate_public_ip" { + default = true +} + +variable "disk_category" { + default = "cloud_ssd" +} +variable "disk_size" { + default = "40" +} +variable "device_name" { + default = "/dev/xvdb" +} + +variable "vswitch_id" { + default = "" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-zone-type/README.md b/examples/alicloud-ecs-zone-type/README.md new file mode 100644 index 0000000000..6f0b462218 --- /dev/null +++ b/examples/alicloud-ecs-zone-type/README.md @@ -0,0 +1,19 @@ +### Ecs Instance Type Data Source Example + +The example launches Ecs instance type Data Resource. Then set ecs parameter instance_type refer to the Data Resource config above. + +### Get up and running + +* Planning phase + + terraform plan + + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-zone-type/main.tf b/examples/alicloud-ecs-zone-type/main.tf new file mode 100644 index 0000000000..c3c21bc9ae --- /dev/null +++ b/examples/alicloud-ecs-zone-type/main.tf @@ -0,0 +1,42 @@ +data "alicloud_instance_types" "1c2g" { + cpu_core_count = 1 + memory_size = 2 + instance_type_family = "ecs.n1" +} + +data "alicloud_zones" "default" { + "available_instance_type"= "${data.alicloud_instance_types.4c8g.instance_types.0.id}" + "available_disk_category"= "${var.disk_category}" +} + +resource "alicloud_security_group" "group" { + name = "${var.short_name}" + description = "New security group" +} + +resource "alicloud_instance" "instance" { + instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + image_id = "${var.image_id}" + instance_type = "${data.alicloud_instance_types.1c2g.instance_types.0.id}" + count = "${var.count}" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" + security_groups = ["${alicloud_security_group.group.*.id}"] + + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" + + io_optimized = "${var.io_optimized}" + + password = "${var.ecs_password}" + + instance_charge_type = "PostPaid" + system_disk_category = "${var.disk_category}" + + + tags { + role = "${var.role}" + dc = "${var.datacenter}" + } + +} diff --git a/examples/alicloud-ecs-zone-type/outputs.tf b/examples/alicloud-ecs-zone-type/outputs.tf new file mode 100644 index 0000000000..104f2389fc --- /dev/null +++ b/examples/alicloud-ecs-zone-type/outputs.tf @@ -0,0 +1,15 @@ +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} + +output "ecs_public_ip" { + value = "${join(",", alicloud_instance.instance.*.public_ip)}" +} + +output "tags" { + value = "${jsonencode(alicloud_instance.instance.tags)}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-zone-type/variables.tf b/examples/alicloud-ecs-zone-type/variables.tf new file mode 100644 index 0000000000..1ba1cb5cc5 --- /dev/null +++ b/examples/alicloud-ecs-zone-type/variables.tf @@ -0,0 +1,35 @@ +variable "count" { + default = "1" +} +variable "count_format" { + default = "%02d" +} + +variable "image_id" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} + +variable "disk_category" { + default = "cloud_ssd" +} +variable "role" { + default = "work" +} +variable "datacenter" { + default = "beijing" +} +variable "short_name" { + default = "hi" +} +variable "ecs_password" { + default = "Test12345" +} +variable "internet_charge_type" { + default = "PayByTraffic" +} +variable "internet_max_bandwidth_out" { + default = 5 +} +variable "io_optimized" { + default = "optimized" +} \ No newline at end of file diff --git a/examples/alicloud-ecs/README.md b/examples/alicloud-ecs/README.md new file mode 100644 index 0000000000..88d2887a34 --- /dev/null +++ b/examples/alicloud-ecs/README.md @@ -0,0 +1,27 @@ +### ECS Example + +The example launches ECS instance, disk, and attached the disk on ECS. the count parameter in variables.tf can let you create specify number ECS instances. + +### Get up and running + +* Planning phase + + terraform plan + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + .... + +* Apply phase + + terraform apply + var.availability_zones + Enter a value: {var.availability_zones} /*cn-beijing-b*/ + var.datacenter + Enter a value: {datacenter} + .... + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs/main.tf b/examples/alicloud-ecs/main.tf new file mode 100644 index 0000000000..a6d39a059e --- /dev/null +++ b/examples/alicloud-ecs/main.tf @@ -0,0 +1,49 @@ +resource "alicloud_security_group" "group" { + name = "${var.short_name}" + description = "New security group" +} + + +resource "alicloud_disk" "disk" { + availability_zone = "${var.availability_zones}" + category = "${var.disk_category}" + size = "${var.disk_size}" + count = "${var.count}" +} + +resource "alicloud_instance" "instance" { + instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" + image_id = "${var.image_id}" + instance_type = "${var.ecs_type}" + count = "${var.count}" + availability_zone = "${var.availability_zones}" + security_groups = ["${alicloud_security_group.group.*.id}"] + + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" + + password = "${var.ecs_password}" + + allocate_public_ip = "${var.allocate_public_ip}" + + io_optimized = "${var.io_optimized}" + + instance_charge_type = "PostPaid" + system_disk_category = "cloud_efficiency" + + + tags { + role = "${var.role}" + dc = "${var.datacenter}" + } + +} + +resource "alicloud_disk_attachment" "instance-attachment" { + count = "${var.count}" + disk_id = "${element(alicloud_disk.disk.*.id, count.index)}" + instance_id = "${element(alicloud_instance.instance.*.id, count.index)}" + device_name = "${var.device_name}" +} + diff --git a/examples/alicloud-ecs/outputs.tf b/examples/alicloud-ecs/outputs.tf new file mode 100644 index 0000000000..104f2389fc --- /dev/null +++ b/examples/alicloud-ecs/outputs.tf @@ -0,0 +1,15 @@ +output "hostname_list" { + value = "${join(",", alicloud_instance.instance.*.instance_name)}" +} + +output "ecs_ids" { + value = "${join(",", alicloud_instance.instance.*.id)}" +} + +output "ecs_public_ip" { + value = "${join(",", alicloud_instance.instance.*.public_ip)}" +} + +output "tags" { + value = "${jsonencode(alicloud_instance.instance.tags)}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs/variables.tf b/examples/alicloud-ecs/variables.tf new file mode 100644 index 0000000000..c663f8dd3e --- /dev/null +++ b/examples/alicloud-ecs/variables.tf @@ -0,0 +1,51 @@ +variable "count" { + default = "1" +} +variable "count_format" { + default = "%02d" +} +variable "image_id" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} + +variable "role" { + default = "work" +} +variable "datacenter" { + default = "beijing" +} +variable "short_name" { + default = "hi" +} +variable "ecs_type" { + default = "ecs.n1.small" +} +variable "ecs_password" { + default = "Test12345" +} +variable "availability_zones" { + default = "cn-beijing-b" +} +variable "allocate_public_ip" { + default = true +} +variable "internet_charge_type" { + default = "PayByTraffic" +} +variable "internet_max_bandwidth_out" { + default = 5 +} + +variable "io_optimized" { + default = "optimized" +} + +variable "disk_category" { + default = "cloud_ssd" +} +variable "disk_size" { + default = "40" +} +variable "device_name" { + default = "/dev/xvdb" +} \ No newline at end of file diff --git a/examples/alicloud-security-group-rule/main.tf b/examples/alicloud-security-group-rule/main.tf new file mode 100644 index 0000000000..706ee08630 --- /dev/null +++ b/examples/alicloud-security-group-rule/main.tf @@ -0,0 +1,14 @@ +resource "alicloud_security_group" "default" { + name = "${var.security_group_name}" +} + +resource "alicloud_security_group_rule" "allow_all_tcp" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "${var.nic_type}" + policy = "accept" + port_range = "1/65535" + priority = 1 + security_group_id = "${alicloud_security_group.default.id}" + cidr_ip = "0.0.0.0/0" +} \ No newline at end of file diff --git a/examples/alicloud-security-group-rule/outputs.tf b/examples/alicloud-security-group-rule/outputs.tf new file mode 100644 index 0000000000..79ab88b6db --- /dev/null +++ b/examples/alicloud-security-group-rule/outputs.tf @@ -0,0 +1,15 @@ +output "rule_id" { + value = "${alicloud_security_group_rule.allow_all_tcp.id}" +} + +output "rule_type" { + value = "${alicloud_security_group_rule.allow_all_tcp.type}" +} + +output "port_range" { + value = "${alicloud_security_group_rule.allow_all_tcp.port_range}" +} + +output "ip_protocol" { + value = "${alicloud_security_group_rule.allow_all_tcp.ip_protocol}" +} \ No newline at end of file diff --git a/examples/alicloud-security-group-rule/variables.tf b/examples/alicloud-security-group-rule/variables.tf new file mode 100644 index 0000000000..91aa57d4b6 --- /dev/null +++ b/examples/alicloud-security-group-rule/variables.tf @@ -0,0 +1,7 @@ +variable "security_group_name" { + default = "default-sg" +} + +variable "nic_type" { + default = "internet" +} \ No newline at end of file diff --git a/examples/alicloud-security-group/README.md b/examples/alicloud-security-group/README.md new file mode 100644 index 0000000000..b54db7ad25 --- /dev/null +++ b/examples/alicloud-security-group/README.md @@ -0,0 +1,19 @@ +### SecurityGroup With Vpc Example + +The example create SecurityGroup for specify VPC. + +### Get up and running + +* Planning phase + + terraform plan + + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-security-group/main.tf b/examples/alicloud-security-group/main.tf new file mode 100644 index 0000000000..afdf0577f2 --- /dev/null +++ b/examples/alicloud-security-group/main.tf @@ -0,0 +1,5 @@ +resource "alicloud_security_group" "group" { + name = "${var.short_name}" + description = "New security group" + vpc_id = "${var.vpc_id}" +} diff --git a/examples/alicloud-security-group/outputs.tf b/examples/alicloud-security-group/outputs.tf new file mode 100644 index 0000000000..bd67a462f4 --- /dev/null +++ b/examples/alicloud-security-group/outputs.tf @@ -0,0 +1,3 @@ +output "security_group" { + value = "${alicloud_security_group.group.id}" +} \ No newline at end of file diff --git a/examples/alicloud-security-group/variables.tf b/examples/alicloud-security-group/variables.tf new file mode 100644 index 0000000000..e37d4e0d23 --- /dev/null +++ b/examples/alicloud-security-group/variables.tf @@ -0,0 +1,4 @@ +variable "short_name" { +} +variable "vpc_id" { +} diff --git a/examples/alicloud-slb-vpc/README.md b/examples/alicloud-slb-vpc/README.md new file mode 100644 index 0000000000..809ed9589c --- /dev/null +++ b/examples/alicloud-slb-vpc/README.md @@ -0,0 +1,19 @@ +### SLB With VPC Example + +The example create SLB in special VPC, The variables.tf can let you create specify parameter instances, such as vpc_id, vswitch_id. + +### Get up and running + +* Planning phase + + terraform plan + + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-slb-vpc/main.tf b/examples/alicloud-slb-vpc/main.tf new file mode 100644 index 0000000000..c54d16d005 --- /dev/null +++ b/examples/alicloud-slb-vpc/main.tf @@ -0,0 +1,27 @@ +resource "alicloud_vpc" "main" { + name = "${var.long_name}" + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vswitch" "main" { + vpc_id = "${alicloud_vpc.main.id}" + count = "${length(split(",", var.availability_zones))}" + cidr_block = "${lookup(var.cidr_blocks, "az${count.index}")}" + availability_zone = "${element(split(",", var.availability_zones), count.index)}" + depends_on = [ + "alicloud_vpc.main"] +} + +resource "alicloud_slb" "instance" { + name = "${var.name}" + vswitch_id = "${alicloud_vswitch.main.id}" + internet_charge_type = "${var.internet_charge_type}" + listener = [ + { + "instance_port" = "2111" + "lb_port" = "21" + "lb_protocol" = "tcp" + "bandwidth" = "5" + }] +} + diff --git a/examples/alicloud-slb-vpc/outputs.tf b/examples/alicloud-slb-vpc/outputs.tf new file mode 100644 index 0000000000..d8e1cc93e8 --- /dev/null +++ b/examples/alicloud-slb-vpc/outputs.tf @@ -0,0 +1,7 @@ +output "slb_id" { + value = "${alicloud_slb.instance.id}" +} + +output "slbname" { + value = "${alicloud_slb.instance.name}" +} \ No newline at end of file diff --git a/examples/alicloud-slb-vpc/variables.tf b/examples/alicloud-slb-vpc/variables.tf new file mode 100644 index 0000000000..7e9f9d390e --- /dev/null +++ b/examples/alicloud-slb-vpc/variables.tf @@ -0,0 +1,30 @@ +variable "availability_zones" { + default = "cn-beijing-c" +} + +variable "name" { + default = "slb_alicloud" +} + +variable "cidr_blocks" { + type = "map" + default = { + az0 = "10.1.1.0/24" + az1 = "10.1.2.0/24" + az2 = "10.1.3.0/24" + } +} + +variable "internet_charge_type" { + default = "paybytraffic" +} + +variable "long_name" { + default = "alicloud" +} +variable "vpc_cidr" { + default = "10.1.0.0/21" +} +variable "region" { + default = "cn-beijing" +} \ No newline at end of file diff --git a/examples/alicloud-slb/README.md b/examples/alicloud-slb/README.md new file mode 100644 index 0000000000000000000000000000000000000000..370476f13d377f3c79ea206832b6babe61ab681f GIT binary patch literal 470567 zcmc$_Wl$Ym7p9H7LvYvN?i$>JySuvucY?dSy9IYAxCHm$?(Q&$C(o-hUwtz*Kc>2B zcb~)Q=0NSW*V@;8FM@C=po26uH6YNF4iDRv=6SleH{NW)?fY+c^#LsQHr9q2uOJfX zW6%sUXy)HMyxTsrp@q5x%9BQu77wGno(0eTjJ47h538l~_WwfR(*IM$fj2x^q|)@; zxlB{Wn%fYs_`*^+{4$4|1>Q`p3@qR-d!|+?rfi3BM ze=CQ27ZVfP&c6iy`9Agb#(2>cnr(K|fE-Y7EUMSFa4G_~S+BVGHKxHb6)m6N`-xIH zr3G|ix@P}j%4O#AS{Lqp_R#%l_V&!h`rEuX9@&GB#QxiBz;=MqTXA+2)?0M6F7SK$ z11IKr*Xj!Y;tK!NT{MMQHF+?71AX#2T~((A0czi`v(SCbD(0zywrZDqL>6o5D1@)u z8@o3T-w#?ZpEd$9DZ~bjDPM&V5Qi^^ALe@AdTxqQE^WQLW)I@Jv(0onpZrWY>(28# zd>-q|I$UY)q#fRkvShm=UFxS+=QF3GJs)i+C*N*YR8MK4Ly(aa+nf)#e%A9m4d3E7 zlq8bI)=-iy{%pu;tGccJ#kl?|+{pQ))l>-~$F-rP7yx_oO#SR;6 z#|Hn`?|Bea$Nhe2$X>h5Qr4NGq^~MJPfzI*?;{kJFzV|~TKm6wL$h27VAydl!)Fr2 zRpSccpftBF?~d6o`JUSVCRerWdWO&3@0p);muKoLLZ^%LX!F3i;a`Ifm|LMb-K>A7 zh|%Bc;zsMH(MJs>v~w`p$mp~)AwY3*&VPN~Gn@4$>vDT~QJE=rGAglSTJZL|+OqA< zeK|Ocn&d-0A7P{5e@)=Vu%w?R6DmE|iWerX+OGVv}(meUixDHJ6#t)2pFhnb|O} zI1|AIWdd7w8ol(U|H!8u)*h{z&9Fk0YF%5rsBI(0^wUJ5#^?spY9Ai6+3p+!wOcru z;d1^;{Snvf^CC6~o`6{ORICnGjiSsLo&e;pSan%obYF12*rfYVS70|FVqYji;<#`6 z$6o}LvVuWSe1)rtzN9}~KnVaL0SS|ylg!Xu07DCa#DwgK$nTUW`oV61G>gy6z*5i~ z0P6u^_JI#v?UfU_o^7{qk|amz(@Qf={a!`cmK{y~di7kBedX$(EUyitE#SRrYN9lC z4KgDM*8c>zlvqFi1n(F`6H;jbGxINkC4nk@VA!{1A;L5G`*YP-W`Wu-9-n;rQm z8>btmjlCkpAd_$=@*>ai5iN5w%Dn@Pzyr+?Fr=WvuObd1TGT(z zhFKXgL90__nPg|QAKyDDQJMbIqpQI;9#-2)rLwgdja@{-T6$#J?;=Q1p z=%o!lHOf2}t@z0F`q1Oea~g}B_az(E>n<{!6|LMzk@`s{(#fi&N$`F~L-zPZFZH#{ zV!y&EB{@844kQ}MuTH*5Na>h#Er0(e{F|~^V$i90w6${f4(mY=jL}M8xuP=veyC$&XCM6bG!g!d{23hk@|1}a71OY1 zUgR_}?DUPq@YwM?`A%{%dIk0@tJX-=3FOs+ztYcc$q`*Hl^+>~qei4HT(3stQk^m5 zi($-+a%m9l}2`Q-z((HhZouBW;{1!KOBppUzi85B5EH zAI6la4>II6^FT}WP+VYJlAFN4ml^Qkd*k);VnVRm?zfNHFS6gy&cLlH=w&uN-_{}B zy)91;-tOxQu*X~f46)n4ghuzB8Nu|FQaMXn!OmqVJ2pkPxVN)J9=K1y?K+ti#szfr z?sr1mlE%?UNtWsLYN5>v^p;sR7Mgv(DP?qO@ec_Bmv5tWhzpsd z8*@7k&82}W)LV%Zt2!g9i9g)F>Rm%O;n)U@1g@0VT9U8YRGog^>$NG2n+7uHNDy!4 z{ZTjPjkgktNyCKEr}1o9Dc%fUcbdCIym{w4?y_-@Bz%I4;pqph2Lj#Xhma%WX&gws z3y8&<#w-vTAlPnVT13DP0CD(n{s=a}2nF;p{&fuqIYIZ+z`zwCz?>D57d-wwB-DFK z*BUp^1;u9t#0Lks*>=OD{4z3dMXk*ikIo47OEpZ1B$&2ZuHAQAW7d5ZR`%mjaw-~W z3}S6g6q~-4o)w9Tt5`dn?!}~4-xVN3El}_{znwQ?EWqzH@pU8A6YaIm`F3z1(tCP0 z$qbHSQ;l93Z`P`yGA+1tWdL0yGFyt0%S2HZAy=hn7K=MnnAw-^)mBdi5}FW{xQT0D ziXfkInzoX23?hgL*5IHNj&0`_r{?!%bo`!1u9IQiu|ub9qE&k^ng!w!pR}P0cm!gg zNl%(R%dV|tFYapTW?BS9sVEsgyB)iW>$eYYQp0-rV~{cACjskU>Mx=Xo+71Px!Lg{G?r_CcNOa2UdRH9Pukh*yVkuH9MVlX4s-al9NpF2PK2 zNIb>F;$x3`#<7byv=tEigW1SIhqmc@KR2#l3*Xr4=~e7DO*|7HrVQAW_*i<@&3{zw zR?76q#k}hD3XcBt=x9nkA&e zTXI1YQlk7F0Ttw1#nP5KpRySI8gQ??h7|XHO-CtAUHC+lidu3%3gcs;E#AmRe+uw< zuFsa>vM^(f!6vX-3Bc*JRnc_2n;kTIASpnyFu#DEd?~;WfVj{qiUE@o}5NUTj2p~CZNC1-pL0=RAVRVDLDgeO&1)yST@3R8S!nmS!=K~R_u-{U- zD)<5>=8p#mS(y1V&0yGHNmkz*jkY~mHYEI-0-6Ai_4P}b(1icj{NtenV(#WaVh8|y z13WJGp7`lEHxnXR$&41sm~kR`X~vFW@=}Fq%BeURxi8LnVKF3yii#PyrSb_xNyvLL zhq&w^Jf~qPw|1FE?6!=%7DK>!B!r6`kfMesOuO3j$?Limo47l4c*H-<$(fy34%H#W zhGSEl*HpY*k1)RkSzrb`EX<)rkWq4_?Ytl8nY=n+P~&f%V=4w4s3zU03@O#vPedzV zh9Z74;}>`a-Y*#9)Q?TLi`H_1WI~&hqCDgjVEiPe=}EpdXa|qdGfOrmH^7z~m>IZE z3yTy{e=4htd_TKCw|P2E#T3@)p#(e{MsmZRg&k{ONBRmWVNd}zUI-uDTS$i@a%Yi$vNlvY zD?#0nbAMf_FANBN<>@Xvj=i!S-H+HDS_6)N@J+k^aCWrc);wpk!UFg;6;=2cBuPPP z=mzQ`#?I56&fc%#YAUwjcdSWs*tDJ`e#rx%PR<>$y^iUTB6H41l7{MQQCm(vZ&r-c z^P)6Tvr#)+21k9OnGwOzgSFGS6h8Wx{r5g})JTYdyIeHcv%N*=97Uhp5Gse(bGsC| zop^QK>yR_^N{s{iJ#s6%FqUUj5n8Ty13Izimmw{AS;tEP^copf-cqd7P*knY*ix*I zO~P@hq};@1prnCNsXcL$CP(%KgnH(Y029W~4jrW#uFf~x_c`-*7%E@n^z z5(YvR02D91^8vnu&kTgX;yjg^_yS4bfO%WPvxKwc3r5H&CocRS1r4OMA<%#eFiB?^ z_Vr0tIy9HcaU0C^w#1n4d~#K{6X{}Qh@jP z{MD(7Nq@oK+(C8Fz@mDQ?<1SMD&_TWj6}$F=p2_KhD8r2Y0>H*E0EGI7#$&Z5gOVJ zJ}4V#uN0}eDF}UPleByx%5j%B@ZdGTjqRP;gEDlj`B^D%7ziVXH~eYDto=H$VME!V z&D@GDZa|Z0Kb%KZYEYDXy3@!`=W07(FsREaF4aiewPDmk+HM)6EFsmEkS?|M1k&GM zQWZD|T`AyY!}zO}T_XKA&PU>APd3%l7f&h>q)+GTdk0E7SD9y4aWX-&gH=~{?zD`o)pJ(}BKWDPAS&Sl@436vZf{{*Wt z0hB}OU`F4*CF^>fK|$!c8GF}lxu(D--7YAtYRraYB|25u%}NOnrQQZw-9!j!tx=?dkrfZfg)B>A1cN8XFrKw>g3%48wB=?hi2$R z$gG0O*3Y?lU9Hn??pRLG2G7&aBY_fMp*a4t^#ccc>X{x2gn-kE_p;KF>|eyv5IsOnpl ziN=ii&2k%l(q8$ma1Wj;IZt(ZZAS^kmUx}g3LdPlte>v9c zfiXcTbk-m+x3NFRz>(f#3==t2cDGV2^lM=P2QhblY$?3J>XZ~Db1)80k=@l{$o_jn zVM~Cik&tprvm1x$%sn0=z--MN+4GR5h=kO5Jbfan;)h z+~Aq2Zm50#SesS4{=`R9Hb8*gCYwpMdg%}=RXs`wz9EJ!L{;IYdj`4b*YTl=SPd{!LShA~qzAH$*onq4a zD_D)~qU~gMBtb-ez~$!Vaq{-%^~2JS`MBW0BDa2gnM~k^czWGR|Ls(42X3M0voYJI7jg}7+{IIfC@UN5L+@0GuAR3y|a~$YyXs;{_os6oo-dr`L1T6>Bt;&knfZZzHJ75bdtfwY0|IeVn`D{nI9kT+!JuHYVb($ z%Q9M$zD3(nA2kJCT57DG%U-2!zKLqWe4003Qim?O++c}n=MvZ3*oxUuRD^b|ahplv zUeyIv4?dw?6$l2#L688yg&)7Aw2kh^rFG3E*MjiXEr#_apCJnIz?}aHkP40!^O}^<}uuJJBq}rmq(clr9HM~%UJG_NA_A2;j!j-O>8K}qhUP3(1k`&IGJg1*c#f#t3JI$Df4^VUIW0`yH!s z9Yp!lTJP0B#kVp?*{={C*BH&UJ!%-u_ee4+X@#ya*f(uT2He~o`qJ-$;G}*#zaw&d;#c+jDF@>2A$u40{RRX80PZ(+;Z^i1= zBBI`?WX+VE{7myj^^O%!T|S$any>*IMH0zk9O909Tqd77_TZ+B&S$OIE|*&O%T76S z*(k@eGO$I4r>I5bOmo3xzeb}ww}^3Ya;e(fx3&8tq9@MZ8&nKLOEZ_O&}EfBCZOHw z42j)RGSo6}GUDAXJ1H$=xy3%);v`EWypwz`p7uvs?|Y)&wy&*|(#Co*&9oE@GbQyd zsP!pB^7i)fa>gry_wu-9j9N2CSX+5G=};eBeWhIG`!v4D$#{{N;w%B>K_;FFFN6`~ zttvPa6kU0|yDMzB08*ctW1$~-TYQAsz1tREt|ZJH6$Asx#(zA|L@6RFx?%C%gS|;Y z00tG91$np}N6VTPrG!bCYqo?~kOV67#>hwmA@YXOeX9I`CQ4$3|3ql9fWt~*d*Hy> zyv;DP_OT)_Lu}O8NO^?fO;{braz5tz$`1Zupf=eqD&UBRJKd+S*rlo5FdP1c89#mkRXs{Py?GuWpNRfGtwBk z$qGSTWx5vNtX~J7JEef1A_&;1`?Fux_*W7ELhJ?`?=TU$+5Eq1%!>ko5CA(15&s=j z2?-4RiR16gh5xZ}_YK(@*69K#1q$^OBn~4M%Y@Ma?2<%kT{JkZlp=v3e5b_p`tQZJ zF~BFrv09wqJoMv2(L2TUIGIy%9{$Q-5l0FKAXl*b%=uwi7%3{mgl?gR-Bkq*2>ysT zzKGi`d1uJHgrziBJygwje#WxIqnnM-_hPhHwzdY3MHJ@quYVP_Q zzyCsajQq9BD#e|&a?dSInD1_oikDFqAuCnbPD(3WkkCK9o+i_vxkf*2q5h>JO{a1d z+^`ykU}kYN#fhGA&brH+-Z8$6WqF$uhJ2urv(VLxO$rYp7(Te;6GFOae%u5YEC2=? zV{jpyrMnvYv7t zK3m(+nrl0xn>8sRgmr<-@w7fGZM3&ThnV$I%QtNMvs6p1(Jo}|53*~8o=D;${s-_>!Uwt~I=iBQz2K;*P0wg#(O6Qee%UEX}9J8;^{RJzMd zT@VvJ?;rVVH2%A)2)XdktJv|k0C1v%hTuvVj_Z%{eO6%Cn(#>Cc%~c z3TNJaHyDFopeG=;5Op&3dco&_9;j*l0~ZGaegK9Pn*=xmYBIpbfWUwN3im@&}6x{GUzbPnk))rClSFLp@ zBVo|?l6G#$%c%g&rlYT}(mp^XE2{W4XvhT87SfiYW<-pQ8^}--#n$RyTO%W*I#9rN z$j=vR>BRSUZa&7+$uj@s3(|p3NOppdWwwkHaUO>h2^cuB_U`tPTc*}ITsyer($R7|% z;;N};H}FYS2sTbC?v_8bSS%VTsRih3uf$r3W#Pk|MjhX@>T6Oj_|-95zLKuwA?{Rz zm$nL%f|#p|(BV<5J>4QS`zzqZ;U;=-^&FEUf0~$Wq01vSHifH`KVvei&mTW&J<} z@3+w&E$ugU1swn6F}uD0plFH!idF*49=cFkub42I82S|{)bX=cq$zIyvf{3e|N1ox zIS21KVB6PZvlB;9>Eb{gtM2v*0M?vPy7Z=U;^QtESoK;Io^ifyf?>?}b%pNhaPCCd zKOX|XT1nVF-TET`nDbzP^XbpMg4F2Qw?ElAO7YE?hR+p>TZnvr&Vk$KBRlWP4-~EY zC{>5f9_Hp?+mY8!2xn3>M<{nW71<=uLu%_Jinq1O?{P3OBkoVm!X$HFeG+yZDXH4A zBbQecT`XfWC}zn&!K!xRw|@$YsI}mVsOy++V_5>t5dPVir2LV&FKgOQmOneP(d)<7 zLY7#fpnX+VZ}D22Q;n+FiK+z@ae)p%T1s-ea%5Ez&guhx#``I)Ob_>IfRwvW<}Llh zr|bvX)PJ$IonIfUt?mCgYr7}_P&^*W3zwD6Qvk8kBP_gj>A_qO@G~N8=@JJJIlyv^ z2sQ@5c<#YbIu`}?K3VER@hGNMC1t|;yDh5a2RL}{DSjS#2 z@>7DA69iAPGF9$rM_;P42zbY&`rQ*gC(<3c|8AcZV+a9r{RS6z#YlG#EgH^p(1 z-fBGlAn9OJVjTx`=ZeqmnXRn-pA9q(=%7Atzi-UPo34@u+Q%_Ff43J&ATq}Cy|Q(l zc_qgPe##)0m>7cA8n-*&V}lkaI+R3}PY}(AAQme-5^|IT5oVf7tr{RgYCfrwyv?a? z`BfOEHtzz-lNPl@lrud_0x?ml=d1rrPW%i0yCsfkt?gS1nCTQ$0+HNz0W1(>PzM<= z^><>q`K~-1_=M7M4oXCrOOdbc*dSKK84CQ(Dhu0l;on)1Hx=C@iU|c51?&n|fCUTd z??PgoNs^QgLK6mlen%OV49bPiwNE=XPZbNchv z^Ke|$$*wpF8Ra~v@lD4}R+n>UA{p_?Ld}xkKR51@S`95L9~*aChZ34yB2FR(VCjkr zYc)RVY*2V`O(DSQf56H$O-)kJ2gc;(yWy zNCKDjF780;=s>^%{Z~e|MxZFZ{Q^YDBC+{0;B>yQ8^r3|w73kBz?duG5f4-rV|5-j z6GMfDi?H$1NjoV_^xvIS@MSMhjJ60-gAtFT|Ez-nv&7^L^$c*K}wItdxqa*NQI{d-0Fx`HC8sFcdc|l!C5WD8mJ< zu?h);#htdBSTbft^)-ZKj(WO`WeHV~c9Y9orQ|y?O43>Evl44fd%iJO`QCwTlC3zn zs&pG1f{4p zpkKZeUTba+%@Xo2R+~F&?T+SV(EE>KILu8<78C0|ZN0AGW{&W!A5M@TDm^GIgJr^) z?z4O?1_@Re%`Lfx_%utT#U#0R5&1B4HTNkw#4<0P00cR>ojm8Df?S!$y{OxjJZ)Qn zPysIwR`GI1*+e%HTDL-(70wb=1SdOE2{o}oHOF7KuzP=4-b);++#!$kP)T_82NVWG zwMoW!dCwiubgyJyuuk)VgF+Njia08bGQzh{Jt)OM#!_Ka?lJms^*1K1xJ&H@ZX2X@ zZec`#_GlMQdTL$+#-=8;$Jg{ScMA}%gU$=L6D?XiA0;OsX`EXjh( zv-p`%f~!lcmIcWuICW}%GLK6f#xXBzNV7c;Y-HJ5DC}??ocC!rWjyMQio|P?5}D-S zZsBsD51+E(cmEEgiZPLoydmfd3rxp>2G)!OxGWz9u7cQ>H6X$#%f%FyXTujU=M!Gu z3F7GYZ8a^z3)f-C^{^qfS{1yh_{F3AKjXGfVH0-$uW{S+*8d@H8zug~!fjMP{>E)G z9se_KQ|$W}ZcBmy`Ufb7=lTnjqgMYfL3xC4??)8>0p;6de}nQMl>Y|hO^9LmUp>+& z8Zn-N*cJj-Lhp9VTRoLMt7urzG{51_`!7ERkkN1KQp+18R&*bF+VIzc)OaPT8~wh% zt->bPxa+Dc{*r^!Bx_!?aB>qdMl+cET|*qFpPJHr<1layiXb=pT?2_frsWDnVhndh zOps!kKTkIax~uE`3%3c=@2THYbmq`e8IH!=q$DDm0owJXy?*5xZy_x=)CeStAX0TI7dsBQ_uv zesT9O0$;w!9Q2Xg50bDuFMzXk(z5C7r-^sI<;72 zssxPOEu*l#YdeRo9(BQvVXw%rdJ-q*`^4VxP>fmU;t{A$F2S z6LQ;CHJyw%mQ8x!B9#`I5y=nsTP%d{SMN7?G^nH~|KXQ3=SjEGNzGT|X-T#j+? zabrGkn_Q|-+5`Z%d42}qwh4?6+-5|1hB@mte=+h9bbDdD)uM{PnNRR_&7%UC^m1Ys z=P|hDETz7xV=;y0I##3D*z9eKd=-9xW9-15n9T zi4P@bAN~HQ7=vcHI%VM#|fv~)ci()Elm{4E_g+sX{ zWo8wyOH;AyEU0)FC7lxQlJ89@GYp6cgfq&1Z<5WP%s8|5^N`v zvzlud_m4~@jFO&FS1Ahf6W1lkIlhy$PP< zeX-rM5{uz}f48c^JQ>kRBB3)IoHp8%HZ&0iUt?8rGHQ=D>@O4lvLNL7>@p$q4_BT7 z@;C=N74G6oh304h8WxscIo8?QDmgHvG}tdEQW+7-Qgs`te&oBqDM0lM7FyoJaQR!W{5vlVg=Bvma26tt-@pCPw>M zb8tp=dN~owW*)8H60?@qs?Nq?tM~>J=HC*&KPbO%sltoJr9s`yA`DwGDUhP7A;Y3m za_g$r6DXsYSuh{u(y`6G;&mRG3FHSqj1Z$%ih^zl9p7-)buk=VO}?wU*EA$ueSh9k zx1X>kHP<~)gr_h+|Mqr%6j>{X4x&a~(ZKj0>h9W3EyD^zIxSRW(p5Ja3_#j#4~_n0 z1rMNX`3nc))e+raVF9v{$9x(E=D892$x|8t0J`z@!F57uPV;8(VAv#sCz-2SU;*hTZkq?w@66{e=j~XzK84 z_jv_R)`qcCvLHr`?n5e;@=bepJqqx!6fUR>S*0_Q=OU0N=Hw^m=mTI*k{eLq53$!n z?E&FGt5KQxo-w%IV%9;P*CzW=KJUEF?Cb~eN-AFhMy%8PY;LLj}vZ~NHD*y$GocfOfMYUK6u<~H( zodKm3PJ@7S2-rsd(hT0{ENL-F7%zNOGq@)KUfg@`cP8P2MYpxFoqU(sdnSX<`0jpAF;Hs-QD4@ zE8zcRhgg6o{V^p0zQ~6usj=eD3yHf<`Wfpe#EJe#MygsVzm}tZT{^my+J!9 z?jf3PjHK&0)U=y>dY$m&CTABqDro$6FnzFa`lo?)xVum5?egon1MSSIMiM8FyH~T1 zD+ixvL;3>0vT!3>L+;3fM+OewxJ% zTo}L5#$UbKFkTYi8Qvqp_ywQeAOfdGd}GO8J&eEXdl-yz!3N867J5ICJ-d0t$o6(H z(VaOCX?D2N`|Q}C#+#PF+si}{A4Ry9}-mtVkLY8CIDzz)8C#!Anf}-*s3@-s{})KUGimem%tEUeC9k(S-tmqhn0;;Z0Gf<|ZQ<+?g&1vskkLWd7SW zRpRzdtbxyr`)WvASo#r3O? zL7Gh$_|pmVo40bF&{A4fPF4r7p<_cF)_G_PY+f!YY zV_whbpfx9cE$>a*ot^6)lVcaN>16vPHjpYf3c3(k%s;-+an;SO`y9#E!qK+-OY=?V z{$Pjc*Z0BjRN=E3D)LnNu6MNwVpfVMK$+!eqWV-*@u*)Kn>pYLOvo`?r{5_~4F!3)hFfNmHaLN4G>$Bt2&$coxMl3S? z3IUbihPNSRmZi?GMwFj*S=s!@W09L698*npq2-OfS?0ObQ*r38O_|u_q#AX)i&9$t zB2e>ert(mhc$6B{_&#l!A5T4{+IK*+@FkCN^|799mchoWpN8??sSWt?C23+M$RfNd zVs-ANw>E&-iY0n-T^yXpjZo&_RoV`E;&mcF>@U{Q(ghvO=UQS-t6+)`7LE|^$5X5# zDM_ffU*}t>4?bG@ut+AkE zC6o3jo~)LX`xu^XXQ)I{lD5iJoJrJ2(K*I!_W^*HlfN0a6wX=_J5SS=KxBkgFw5^l z$1N<%e2MaQX zqXMZ=Ywc{dWBJL?^ihNK;}~K_k~$ff>=g7mSdf7kxW`4_jtNa}t9E2zF4VHT z87GCRRX0WKQ;#cFbfumvK94YYtoADR)5^~3eM^ytqH|Q*`4~`?CtqqzRAA%f>1Mtu z!wf?(q2O6cj7J4kOb(O(jz23Zt3tOy{Y1S|5XTFu`Un`_g;B&dgO|aELFXO%bnOkL zyRPyl3y&-FNrT5jVamXa^ik`o+NvV*piF^FmiU$F&R?FMj-JZzCgy(YctxB`n?+fo zWbf+Rjjd(()|G8JB_0Q#Em?|?HASeLAw#dCX>`EUyUgqt&++eXzR%h}=KAZ9~>4rU2#B|T8`OBq)k@EKm`kmV8MsH*eDnrY& zFkkkE&DW^&3%4PE)3wLSH5zKMp|($fyFbl%xwFg6q@-W2k!X8q4xT|8nRss&z6h$| z{_-WP2=8(xTC_NH8VW5r zBF@l$jw0}gr|qSt03^7qyD~X*%Mp45s>E)%l=;e;wwNbpIz6{UXJrAtgwTv(Hs{M^ z)H1tk6fMs=Y$hr-r=+S6jn=Vn(^(k~MemxzmnHo}n2<-r!$3qq9PXsb%DRmnY9BAA zEK%`RVtP_L7V3_S(GJY#^wZx5E~RIQg%lbEhkV;XQ@q=adc8|{(U_pGtMlQhW=Co^ zYcdgt;WLA6e=J+828%wsI%7?<67@DKm-i<*0hE%Sv8ac_d%wF%-rjx(6t?-S(*b&R zx|&$Bir25`wu~%I#}S9pvbl5L$vU1HV+R-p(Q8qy+n+7Iy$j3?>>T`JLC}!_P=_j< zYDAe3dk3TTtZCVuix}MO2wl7rf(lx^nXUB~@6yBHW3szMc$W=PX?ve#;CMAgukZj) z*JaJ+Itc3`m*d~a;~!l>#znh-38w15j2mSCv;dvu5XU2{i=Mnu@^YkG_n9Nbw7Hq3 z&H}A*+V|FkIIhal4vaNU$E_7!Wyw>e(}|WueJG~>Q2N}<zK7NGnmZ*`U!dQ8uoNm}693{d2U^kDyI2Y}QFaNX&T zC72m=#DxhQ2D{DQ-~bmJoqk@*ve>_wO+SY%8lC|-K-8=|3}9aZ6kgvht51miZ&m<# zH+&dzAe4^pVAfj!96?9`AP$FyR67g~*smDm#DwGx#R+!;1g)w0kT$uj5U4|Uu|GQm=MkE{R2Bd>mkX|ZZ zBzxE`Q2kw&dI!q0gZAISIok&~XVv23gg5d zEBn{zU4w$`-L{N!K+}OGT4f8!)=BKY6xOK!U4=E60+NDWR)+v@bSwY!;bl>Vh(xWY zw+|{>4aG&+Mo#|b!uM8`P4Y{wN|&&!7RefD@d(O>G&j*{SXOe5)8EZTlJXNFuzzc) z2Vp|1ptHW?lBeM6dB*}0?cW{;-t?=u$tx}C*(k<63lv-bFc$j}f|#F>i29~OJN@c5 z`yY%&y`OmI;aUEOHI|DPO$l^Dcs{k{8q9cBl{fPVN0${Km@fCze-b3)v7Uol_qG6$~r8kxMpA;mrCMF8f-$s%8Psc|O1_@*S5$cqH*6YUkLxPuXk z=sTfM#xG$LSH|$)a0EhyBG-yc3GAiC6>u-x^_-pPXN>U*p{n_36gIe8lCMS^$BjpQ zii1#8N_`v&nyp%CuNJbMvCt72G&<4Y!kAl#E zQK^wHyNs;KUedeDDY1|GkUjcGP%4v6+pfT*?bf-Hh-MHrM(*#+d`_g6% zcQb=D#}vjwO9KAtJ6pNf|d2k?gfrLfp@H19s|&{$s>y#Y0UbiBI-dc|FT9Dk?;xIvrnA`#Q2Xq zy0np@xMDeOLvw=kStv)^I7eDSsl?ArlSDqh^*1;&&gF$5WH0Gg5e^NgWru!Ec4TFw z$Yx@@;6=5}#S}XlYCQKVjPGAO-}h@l?7P&f`lh*Qt+;9pHDdRFg}vZCapUPOToIDb|SN=tf(hY??cA>LWr9f*&m~sT=^`S4ee43<4^E z`OeQP0@@{k)d1Zu(zt+ye)>v)909Px6QV*A0zt)u0+dd$q5eNTT3}4}9|jm?U6E6o z$0iFmLe%p-YV*o~jXsEtJ^}Km7QE)q8Qkn*4jmf$6vO@OI=Sw54l$vB^<%Y{Kz!V- z>5A9{XiWTjrDYoG|I?3^G{On_XLTK&&hwLQPdD27WAD#5gM7f?5>bhfsX+UtwW5V2 zYf+NtFOGx1h|&CAlScQOX$T6{*A(jMsb=V8OQN9RaYpa!t$59R{CISBQSxqXhJd2Z z15qx6pp3&SEd)HARB;ciu2r|A8*)wxt`xiuCx2wbxo;6IUoz(5^IAJZe zNm#2g^{*N&KYl*tX5Mxjoo3y<%!v~}nmkZo7pqrizIUPvxxy#G+f;YqL7ykaM2X*+ zZzm9HAJ59v#)V`S+4v?1yFZW1y6KQ7fnIOrQGM-@uW}&kN1`f`023#sf|_)j*Nwc$ zv}M3$ZhjCj1n!T55Nsm)JwJI-mUb~X4@t(3+IKug=ag^xOOzxzMBCY>{TZpvf~kL2 zQ{$Sv_nHG=tR4Q6t$zi{R;a!5)>%w<%NokbISbW`glCKvxl$^yFAXZKB&5>WYGy^r zD2hE~Mt_|KGk94}8q<#stQF}%Vf;G+)wQ)3bwe>dkN*hnU(HsDoPRZ2QC$O?t(ckt zYC9`2KyCLO!39jPepONN0qFmrodU)P0D0ZqEr$sb&|@W+3W@{-Rx$>tAV8G%{#dO* z5;=Wqth+b)m3vD73aDO7_hflq`0}pUN+z&b%j0pR$ zaDR1|7y+I;26QdZO_QjLZ~*S^5vY@7AOf1`R?hc%tR&fmWfoPW9p3-vo85TOSMp^H zwlZCCSPB)TgeHr;SCU=q?O-9sb^ht7bfYjp|Mv&{i0Ak&@-m!U?cE(G;`LsW-*I2T zz2=d@Bjv0ax51cj|FZPZ!@-3~a2AcI8<`!PcVL#TBq<*&Zes)=k6if=xD_oTfZzFi zk~-`vc0HWQhs7V^^AeNwY2H1`QUmIX>9GuMtv*{%;`^vQinVKrWSHAI!7qm|>DIcm3OrqeT*#?A5tQ+AnG*2Ajfl?cQMy`dv?2Q8_R_9nZ*xt zGj_O9za9`f7ZFwr{gJoWoX4b{xhJB`AJdN2olvGvGXsA`dW+%j1=5X`K2rs)#L3g4 z+zFEpuE8y&G;%zZ?#`Q00?NX=wI?^lWc2$`D5m#=GW0UfOyglrusziJ; z)UOJ-w%U2~akM!SiZ0DAEv8z*#Nb(r$@tM544g6($Mqf#Cyl@#1%+|_E>!FDmCnn6(#Nna}8x7_6KT!?!x7J6fPqhbejU6+T|que0_KV9(3}hyrLxLHn?>5s8+$9PJUdlUdIn4jq$e^A4WrLAVX>8bO$q70lEdTTs$tc=RvKtLg>;!e8=GVo+aA!cD%^A^0pUtNP^}p()&vk(M zNb|A?DLZrfljlc@GDiP9Mcn}ZXNqEi{ga|n!2u~MXAA_8qU20~0V(Qp9e~LL+K9nM zX#VnOCd8FNUJ0bmiTraH(IA!zt_QjZtoFr%`D#f=+)H+R_mk| zQB&qyO(jMtq4Q&QdEnm+!x}`XDrG%1eX1V<21*XApBS~*$58!bAL`oQX(#RauPx!dp-9BRnkFH*!SNU3_ zLaH0Eboa|D*3-J%Bs@Q)kf*&G7@#wWcR~P`+CL-AKT|u=Blcenu7EQ@L?V2|1{9Zo z@z1p#uomFj7g)~@B>#ALI>r;e8e`)Y!}lkFDp z67Ux#-wn8Q!gayF!3{5LOn)5i=N}Ky`8cfAbl?GR4{QuH_D2#uYCKP*h=fcdB)c1} z!P1zLs~jONXe}D$b@0AlTs|GWC%3TIku%pYSSL_hu}Cz%GQA~9JbE{!JVZy;JP#5R zBCa12fG}a5oAI^GP;3WZM&q5bK=B-Ze_H}b1lRPlf#&gDMHG1eMSw8=ajfQKpIhR^ z2*nk)_T@(2G~ZmNS70$3I~BebPmbmO6Jb2OXT>j+)%4b(a&fZdrB?fN9&B~{0n&+~ zF$Y{OrGMb{p!tXkBM6d6N@V*~frL!Z0^Ja>D7TdIc zQsqGi-$HqBDj{=Fei2s@Iui{Rcb~hhIIVQWP&DtA*)WgQV+*w0D?mVYtz~Sm34H-| za3#hs_^p98y^#fltu^lOWf5_a-L}9??Am<0y%9WioCDM5Y8Jq=8g-i+zMhI&5x}0P`s<& z%i(0j-LpCmmRydq;cM5e2b`?iWX`s%hY%?B0VwyMS@2ru=*H>mV{GC!cH;dvf;i{L_UItlBExG*WKK#3dH&_D_0g zSy@k_Jh5-=ywoPdA&FUZkNJ18rh_X7RTnV9FVuZ@FV~0^#9WZvr{dD{r#f`fvU&4Z z$tbcJno})8*RIGX#r5J9rv`kkr$hkr1-@@NN=^An%M~ax2=K2mmWubR)uoN2>)i^> z!t44z%lq4lyT$2z$P@~aqIqeKm|Rv%PLr-N{sY6OQuC(e8HWvR36A|RmQ5!{uFmNX zy4uUO_rtNJbu^{4Sd0;`t-fDo_OGD2=4C(my1xkIHx z#KcQ5;FCE3IFPg0>RM-gTvM0d`-;f;BE$#Ra`nvwWc>PHR-GtP*qHB;?b{VOI< zSO2%hZ)-W}BCpgGp~&3cZO7n3HD98j#IO8taFcsq4)Hiyj+f*{wil`VN3;JgsoAp)DNfM1_XCP&Y}6{Ng^_kDr}Qfaa$0~uw+GKrxSf@?(1P#SxLj1xpt$goN|M^yRjXpi1c^4AA~dzz(l zUX*&Tqos{RJG!=;Voe@mP3XL0l?19nI@Ay|2Bh_usVy84`|HItoWAjFvLNgYu@c#( zBGuFNb=6nE?_L@axg`36^lRiOM~}REx2Kk8sHdAVLS*XczA-S49( z8ZR#?j@1Dd|8qZJI;nAuWgkL28i$($kj*Q&+FDzio{Cy*YU>rT*Z1ADHQ+RptK5!` zB->&)c!e?dHWq3T8-%_&%i15}9rR63MkSY!JILRT)#jl6h;Ja2*J@cr6^FbmX)SQL zJz68Pv!ZLYCbjW6tHx4hzJye!{fCc4XR;G>Aj#pa;6omvQ={On9$#XzFPVi=Q7cS= z;gSqZ!dZPkf?S^mr`S~x9?WaYpC!cjx$tfRR`9)t30E|2WW()lxXbPh z_K`u@ELCZ*TT{mZa+@;n_e=gdi2>0i-MaC5vIClKJN1c>Zky7`5^lF&gN6_`l;yoPq@S= zWa6&D-eAQp5M1K!rov^6Sp2+Mku|I2`QXG&8WT6Rgef!jr!>r06-zj+Js@^3p&~gC zkK^{S#Wo9M7ox^xRh6!nP!YZ-a_^mXm5htyxmO8WRR`@OHR_M>K!$guUD7oF4R+RE z8X^AE;Bh>fI?Y|7YcGE5lyoTQ>G5qRTrj&OejmiG{!llEhABiyn5~6jDX;n&M z;|_k~uxu+o+1UmjKfv>y%#E5OwV#puk9>W5WZucpUck>Zai!nB%uBeL2&HK4YqP9P zNwl~R#<`K9jCL=u&}~uiV1fU_oHA40FEkRsd|T}|d(~}9nBb>X=SDlo(npW~UO-Oid<|v-3Jr0PFv| zq=Z;)W6CR?n~kyxCt8VfEh)$ge$6T}I(Oui?{E=1(>raqjax^S`_`iw@Z#@A>l{$5 zI#*IZhuY2FgEodY_Q#Lxe9+ZtMIB|$g4M<`!_9%wkM@}d?>yA2F~N`TJN8@90f_~?qQum< zHJJ@?=J3?K^Q3*-$el#*X_3}N)`*k=c29$EI-fp|6oJsFh{S_8e|B{}i;79Y4Iqxs zuI~R{D7TQ*hJFW8c0KuE|5a?Lc+>ugLoOx{1FR6iSl2W{x zQ`1IGr>GiplYwJ=;9@^qH^g7RMp(@WLCj|^KGzAu!Tq*+49UuL!A9QSbfu^i96}if zF8d|kPhN<+kWTQ9>>5@rsh94ja3jhrzOy?Uy)v=vc7nU5##eR?sWttEH*fcOgD_#+ ze#Xeqc5UL-Wk}W_$%Z?cn1inuXUcfnO%a^rm^ukH=Z`9!qIc%OV0AC05lfU<0*$d#OKG)B)Tbz;wky3b?jG(hy!e zZvau}_ngv91l$__+7V%;V=qqwp#A*zT&)PV&$;bej*^~5NJQ2Uq|VDKpa8!I0E7bA zKHooyY?pqeE%SQa`^nlP5-JS=@Q-1=PiT(@Fld-9E5b(49MuuTghOhC5d^gb%INwd zQa?UKNf^gn@Xd>16e-a|%qhfA__X!k2`Y>tiewneZm@-v4{}QzF{v3d2qcYxXrQoy zjhz+L3Sf+hA??=58o-2c;nQX`mEN69(0W5m(!gv6%;JfJZZ*tXgVx6?w=GBq3(m54 zO=Qk+615=G$(yE}XCox7eFmL_9dZB8N@^{kEhAqXFpwh>k>1tk88JPRvYb}c`kdVn>|`XlFq}| zjV_9ZBNyy63yx>f|F(t0--7sQ)>10tv(==4(li|GVw_`vH%-hJxk~+iR=c|awOfYm zzgD{g<5dKNnvv)3TrxS`HY3A_nECTuj|RIit*p@1@fPj zKkBt_OI%?X5dP-I{8uI14Z;WX?{3U0*q6M|_}nhY7iAz83fCXq5(y0m5O4hW5ctE` z5CJcyXYiN98q4nt@3c&jY1WDhXorvfT6QW&>Gn0Fj43;Dw2MXr`iNn0m2h)~YzH+7 zUU;U!-DQGESgoJ~86Z{7e*yzK+7Mh7BRX4!cOwQ-@8_CO8Id`BC5??G&MwU3)$s3L zy7dPjQ}YlO#GQ0u_#CfvSDN$w#yVr=D??Pe-bhM-^vTV5l>mH4H->A)^^t=Ap|k(s z`?ba7jwPXO`1{wi;>*{xa6e8V%7SuPu+n~e)I>d>gT`Fxg5Yv(AYy6Dx;3T^^HRQ=3d0bo`UV~Iz$v~TI3fjx@o9VM=+_1$G+o2hL%xwshBR|E-0%>N z=RaXPSGT8w)eW;2Q4;#TcqXNU9%3P2Evl=B$=Wo%O9#>x0z^C?c z@!>-N%LXF|zX4<6+9*9}7yT$k{OicQ59$d)>1A~HMFW>QrA69G=p=JDra9g-{Pupt znqDMcBFfd9iP{hoQQ3N{rn<50<-+p#bhvVn>?g(@vpG`>m3;mPDe86^w05)HF+ozF zu?Xn+@IqKzU4kKwGT4%Ltrs1Co+;%vP)PLjkK|jvhsy$imnrf@&ik%UPO5$tA44VF z(=TcB?S!v2t>~#l@C|P$H966r&?`WDc-XT6!@KDt|7^tn-V-w67hb0abl|41f8bh& zM;Zi|u1^S5x>Jv|wk0ZJ>77}*8e}fW4m9TLxOW-fYB=|2qQu(m+s@wB1(N8BR}=&t z3$01;uRCT5w9xSIg(ktP*|G8{5Lsw7m%08)*8wm)P5V-Rkak-6hS5UAC*LRbc1AHF zjmm80vr0512Y@kRu^d*gD_~I;c}xY4$citrS;#`*YmmLM+Iu6P8uA@naCmH#>Nh1T zZmE>dK-rj-{B+HDpStYoG*yVp2b7Ydq?UWi**HlWtld<*?vZw$!JvF@!T zcFQensBaxy!}}1!bo)g6@$_#1Pw0rWH_HH#VDauh(g^6T$JiC=OLrNZK{a3rPvwt( zwqPq#wRXmrvF6VTZ=09!mO(iz{Qr-zQ`qz`gq@=QMA(t}gRm1X{2vKBqkkjpT<34W zxv<#1Taj#a6^F=ab>C95Kf}3njfRuorcC_UeNm2~(g~|H*Yl?!mMr#L5KD1+9SzvX zeJ6R20za0xy0F^pWLp#@4hN){o^-?B#z-P=jndzAo`&=Jlk{7ofF_O+v1BSz3;ig% zvsRp_)|T1AwcUwvq$oA$vMV8|{SsJ`4Y)T=wwS85$Tmv}#aw3P7;p6>OU2zuBUE>{ zLcC0zROLUWV@?(TDf{IN6LiV4I(~JvO0a>6uGq&$4vKQyPh<`iw{yA}`(e6PugRed zVExZqB#3WcWzVLMhFrG=F64(=RQ3OK4yj#l1*4pZ8Nu#0H-?}T1mkb zE+H`m3Y%pUyqW%~oRh3&!yZ^pX{pdq(vBB-G)InRR3PqhPjHN&eyS!2CYO^ z{mR=%a^v*iGG9W8vPiSJZO0VRz?|Uk&I)35b?$em<%om4S9Z1;h zEOi+i53+3?Nv>WM8g>l@dX;yUi1N;m^X!?TG?83d&j10eJBJeM3fEKygQ?I;_oAw1u~K5zRY7_vdy}q;Z1bZ>R!JzM zNsp6L!#i8uSy7hm%x6?fV~1}|%+w8c7_ut&vS@3HkHMN|6j3=;I@O6M2p%i^%C4eR z7h|@ADv1Sn(AqG~=w%D{_yn6OQn+qDT5>w#7pJJ(txhqREnis@4OjaZ7o7g&-`PL(Xn%kXIh|ND2HdCHVhMq4s`KC zH&J!b69bu++-kphWEkmM+Zv*QQRjCv^G%0NgAsSKu+nKr?h;!&3WZ5_mBU=YPMvY( z{cO8^l*#Tu&`-O36Q_54A5d z+%g_uGpx3593BT*&HBBW(8`fHhuNpux2{zCxW?`Nq!smzJIKhZK!3j3r0JHt? zE9hStw}=SXU&~wI6*#27Dx=KbZ-lI0bqwU-UUjaa>YAwJA{2uR6FaxIl=W46N1q`(|!Li~F;{xcIp4tm>}8 zE!ApKW6lHLI^2ithTE)o5MF1H~QSTF_jxOmSF3QM0fm{`vre@AqA{W zJ}Mhz4SoELzpEFuF^e~FG*V>lOQY1+UwL3vS)Jo)^?om3rjCX*tPBFU;OHBFgbIg1~z6M%<*#9#I_5B zv#rtvyuukuY&&J!IN_qbN#5k%rlTghhT$T7UEOE7#*qN3osW!0f0NBH=3ZnohyTxI zGd3q7Vkeo2@i^0z@OLk8ofCUNW}}H1b1K#jG0FJDNJ{lhCJe6y{(kYC$j-jU+AUi0Yle(+5jchguOG@lCc9i&iH! zV-mu?)nlT@hbJBV{^90v!yspguq1st+4|Ie8_O7sK;>z(N&oyXIJttaRwNGe8M}t` z)$WbKh~W(mA%&kS$wi&XzZA`|YzvZxboxWoU}rpf+Q@9AR0xUO3sQZ1gBrA#8*_m% z0);K&)&wt1_rV?eNAybD{Yv>4c0<2>0aDx$GBTugd_QQZf*y;QzS#9Ra!;=t1E zd^&M{$q$PtS6%81O_>ZE`7AnFWKV7UmAh1(LU8@O6ecEN!7s0nl@y&-Y`ZBH1OgPr zm$`D?I?Cv*XG=<+P{hZNtWJUx@Y4NFUMPH@SY0C6UjsrL#y??mI6eT_oC^ua=a5^b)km^xl}eJ#9k}>;k8DepA?x)y z(w1be*$cr=sC&y2w7B?%f8&j2LY`KQM*UyFZ+^PtOcmKH>=)I0ja5spzx@Dh-E12r z&wD@<5&WJPFJqpd$Vs zE00xa(4`F$=MgBo%fG>yn6r&d^3h=XFTSv!zwV%B83d%&wjT2UL}+^c(P|=ZRrIwl zf;feZ4ZPz#g`tikr6ILUaNs4Fx5gAIz9_lgJId^lU04dZ`rT}6)-vTQ7B zV`3exYA=x$UG5|Z9C$rWOn`a|t-qr)#ci&N#Hqll%@$1~H}pthXFme;OdY=uo&Q!q zgR4ml4Adp1e|8GoBcxrtfdf=zxZZCSmpC{=;ZT5LENPMtR^bE!RL@XIUWqXOSBZWQ zL^RJz^%v`F7vJ~47K2}Umq^dK1PwNUuuQu>Or6b5#nTN<8u=d9=o@Q+9shSi!$5E! z{l?twv6&p1Ns)(BH)Z)Y`TeOy|GXLCLvWqpWaB>9A zDbeYaak(-zC2_f)RHTq^)-TfX#GleKpU-b;xwH7cl$P-`ECCwLZ)sUX>GX9={l6%+GAwr#Ha7KD#%0{dv_`lgyqPl&HOt8-3j}p zteFpuSouac&0i~n7kLbxChHN@mRL>^89Y_PL* zf7)OrWn0mxki0PvnGB%R`sa(1ZVn0rJ_#TGvwUtp#yvz&~&Emq6a%{)e3x+cI6r$h!EWW{I<{C{ob&V}*W@7#h-Yv$wS_^}c zCpC4Ps5=Cu-#kR`)XCPmipudrsw&0vTjxXRUBY@3D%02F-bR6m3Xj!EA@BT3g`Tz1 zB^>&SmFbBPw>5XYqzI|w-zZj^aye0A#0xjIRVBYN$=GOlEhQE4@$RJO@w7*L(atN> zA~QO&(&$3~NW_a< zx3l^vKo-kHL!&*&mo}d@pvyG z5K69{V6P!m@9I{?+^?1(Cd(aC2`5~MJv^0jLe1N<#ovQz8uOBv%k9&eK<^cR5^F&3 zJB$f7VR{ZA73WH3ot6Ta&!*WRx0`f&2}hHFsVTdzH^l?rRaSKOg75FK;?S0Vz@Ihg zSin*wybUmID!w-TsE6q*EIMDQr)owUv-ZvoBv*9ag(4v-uq7M7j!Pj8xu)Ca5jS~R zfUE%v5RHi9tAB^nXkYxBPW2BKwA}XD`NhUp=0ix-xaLiyo0o+#Av5HyTvSUd9n#Y8;4;w{Vn`fTZg$HXsXqlj3vBLg6 zV#Kpuwu$r(W!~ekAh>YK!=m_tUbJl)S zUra_;a|#@SSu@oE^iwQ5JW zc+~S|gw1W}fEudl;oKum!?ZNFd9~G1A;kt=cy8K7uS0n5KH*vQOLj?w->WGI6SDyo zNpbo;oCL~&I&OVc=0=Kvx(@wsh`s`@_Aq%;fJ1vl+_mIy{+PGsD*u;@nNOfEks{#X z=PwB0qhI0RIaN(t{FNA$&VLP*>{v#Yq#}iJ$m0VV&;=aIDd*S?zn6`T02OA3M#7gY zKWccTA+fD@?!O*^f96F83bM9A^J3o%X{@1C{1rv|M}5hAz!vakx#NdJgQFtsLHld? z29#G+YqeHYF#7r*xJrQxX?f0#BfMpMm4{()qrlEtQZ8oy;XNi%gfwU60E~qERbj4- zMidgBjEK&zg?Rp4=@GI~Z3&Koc)7xnccr3VxY2=`SW|q$UY%xJJo$SMris5Jq;>y> zkd|17_yZxm_&&I){3-$KQ)JK!e2-6E8;a<1vuzuyr?&EYhJt+ zJ#nCb3HQUUxT;8ia%V4z!BnLOg5p@|0YnXxOMXv?00QSXeHHi>UYr#@ zag(jEItFn@+@CQck3i$-KayYi1CI~;@zuMBi*={bRZ>Qp@r!_C$(o?_QK9?oXT$7g zvjzv-kbqlme3cskhk=)9l>$OImE{ zM|)*OXZ13@CiQ+gf5CFtYgpKB|8o2DyZj#)#f|tk3b6Od2L*B=)lAo6Hajm3ra7H!iIs=#dDJr z1&AIwY^a_+Gt)v4Lt(8lPj3vh(Z*Z~dJ7JpChU zYiNUilRLsOJqqEu$CAOFB?B65Q(NrQD&m0Nq{mnt+~cfgNZQ_(qO)dg+Ua_3&>614 zk^<9MfFqaDaF(8n^Pm?51MYU6=E)w5-}ac_wgxw%@9-^yBtiW+3CLwHOG@yDvOSYzH+94`Ih z45ltw(=YX*E3OrdL{5gzZ;TpFCn>X~zXY6Yg@N6-s^m+h2O&;g=b4W`&GM}A) ze&eY<4&W1i{*Ey7DofU)ZR3q{u!T&wVttf>EmCRGYp25Vf%1k-{Z>{N)iJ(1ML){^ z!eYO~O!Ke>S2MD(5PH8jurhQ>kEC*!O)rPmD|9n!EBpS;`iGXI(O$g_=x}6uV3Q~> zMyKiiT$#deEP)ZoC9*wGcA=|$-eD+N)Z#&L@Ey%LTX}&J9R1R=B>1zHu#_~-yLeR! z?wh)+f(tCM%_GsOYth%`iLb5R*HY{xT|ZpGm?ID$zU!pvq4Q~8m=gzA*G8G;{7ypY z#<#tBgPSfsSaoKNuM?zK91&lckLcA{r(PYG!lj@n{^N<8 z+wi?wO)99Itv+5yUQs-hP;yI_`u}{mZORZEo}wKZn|Htc^ud2fN&xD}Na{#O z>{<6%-DSNimS*+%oSZ=eAm@LtWDm2kT2BSFw$g_lAn=NgK5%*0&KoyVM^iO1F2jEH z_V!qGZ!E6Kj#Nf&e{5LmLuZ&AMbsriFIf?mW3Kxk0Ldw6jtV;lAUQh{MRt;JTw%b{ zH8s-O?wy~F24b*p#is+J;Inc9+bpU0jS}4O8|ciQlW#D;rB?gd3^wnfv0+EUa=>dZ zi(Qu+T^KNk#g;Ge4N!GauXUvQn8JDl62`bQ@j9jl-ZAMP#uIy#F+3Wl1R23`!X2Zn zdea7?(ee5>OfEd$%RU7=*wyM-i!88Z7QvveN1`R|IqEM>v!vyWiBX0V3;U0q_Pnm& zohj=z)=ugDnOMiL^~EiUmK;3o2tD52aK=;vaoU`ROIV*{7(F;Js1gBu^a~++mi*v) zb_1=H;&92@BmJou{w!TRTqr%Bvi91jZp*n7l$o@<%Av?OCR3o+LGW6K@+<=_Yhagp#%J{!an z_TA^z{_p~g^Zj*yP!Z1MnTc}ss`>5xFU|DpL2(*k(J>TM;Km3Pd3K*QS{b8KiewX# zswA4#P`O44j;ovko zKic3bTbD^UT%sg6T5z}&ze^%5sm{qtP3POZDc`UW4gHT?tV1rYOwaX&NF3^>Jt>|z zDZ8YC1GNLW$vXj#tc_UtT>`}@77>wTv!d5_J`|i-Y9+=Nef3Xhg4sW>@I4X<&PkC( zgYB63^^M5Ma7y9xdSgVArBdTubNk+fDSp$hefN1h7^bz>ZLKA8@(@lwCeyi#%naz< zLBQSI$x8lqlMO?ZzrB4t+=h1Sx;}U>*_(>cac+hbz=oXWb|Q-aNkI+Plt|L6IbYQ9 zv({cxbd6~0PQExaS&qB(*{1T&ggTb9Znj$uR`g!cEtn7LdG=fVG5xhuRPv`z6V61Z zXKRm=UPy4a&j?y}$~_R|N-?uAm&Q_)_f?9_BvmyHZmhV2$7Ce^B|VM3whGqNMVCi{ z(Y4jZrBauZ0r03Xjzv$3E_Hz=P#`j3ZFodtA9#%wtv!rg23wuY{RGkrsJMKCql6 z;bd2{qR}Ld{*j?_AmuaS-&p#Q4}^ zoo=wpJS-je6P~yg`#&cyVw8n!KUwf2Tb-2#ANiZ=3bQA1rRHSclg1L!5HnTD>RO*X ztei$_i_M&VoM&@pa&c}K8+W#{{ZSIFyZ=9!x z39VaKiiRFrHU-i`%~%8dZ*GpLUk{wI5((?MJcgPL@!(&3CE^<%PdQsx$hd3E_idSw zO^+IQU5lc0m@GFaeJ$2>3UMH~fL*J)@dnctb+bTyn77DjVw+Jh(>=>a+x(KCCAo+Z zOIe){6P6HdT0tuwEa0s5!t-P_aWk4fef8o|>!6*_3n2!Qs>!7cinor3&7g1MvDASf zXy+qQpHnmg#tYU{cVwsMC}ho+a|#BSgnABs%YZ$7_ZWJaK=tkGfPl>wyFr>SZ0ngX z3~EE66RM@Yb9z7t>=XjYRB40*R-ytBfh@r#JyB2q?gRhlPye4|v zqWMBTA5FA=F-Y5_GGk)Y(4-Z0#o&2}GebEe>FsZz45iAr*#xs+80ml?7Xc->3z?0z zW%_iYjenvYL%s*8|MdWIotx&D(X`v|*zmjzdnWruCVZ_%;IeLuO)-GuJa^*ip&@>s z@bP4b+0=k(e*4((D{70liDhJn<5 zUVJ*`G!!u5SXV2kTs3a|Olf2smv09eC;zCS?%=ofRU@`FpJA`o_j{}>?R+ftS z5{v9K;55ap$VozVR)FxMgk)DqE~`Z?bI>_ZL|@kI)j^ydc7jvwZ7?l@}?h z>k-3qs{;;ZnKzq@tGyfL-#qlGC5SRjQXQAZ8_m?gh>5#DJ}u`u-0afCSzm0-o_AAY zakDG@6jN?FHq);2R`-FquLv>sBlG?a%0K;;U1XO^THS@Yd<+Sp(e6_iZe7}y@ThVT zxWZa=6mpKyK7q$_<|MxgRw=ZYYw2Ya{E7I0hP$nijR!k3XL7w=LPE6m@~L`&E^7N3 z9-CDWSWndV&kQJJjO7a5x8p*>`tj0ywNH=P?&2|7{!zx?6ARcrx}{|VO`B)*+wPM$ zR~4HKAj+E`@s*J@y%J|B+z>?TJ8Je073?zK(ar2xTGBF@op=&a9fkkV|9<>Se6Bc& zMWHpWTk1Agl>Qt`8wnOKBLP|aZSKyOBmls+t!E78v!cHP*9MKd8RlCLaV)#9*ZkFBYXnKC7p`8cCq$Xp z{BvHM|6LR=#!xS~TgO@XHWj2Y>8=QD*64aD#x#wDL;@v4r0y^M{&?iiaE%J|F?B}8Jykc{UiO2z zi2iKd;yR+(q2jMqN^QoD^`d;)v*;)1c225(gVvvKoXz^$CxR99@x_^pw*2*Dz%^t> z=BnXK_ot75puK^wbppV2s;o@^U3gA@Y!gEP^rry;;kfH5#*T>0yO);Uh2vCno?k7! zfgF?jOD_)s8KF+)4M!;;8TPtC=vqP)_NArQ90b2HDCy)NZ7%*`pn4&9Fpu0Cm?I>d z$-|@mZ0Y^F-a(=`fjj^;OWx&S==Dd5$Pn}KofoGHP$Gg46tXdw;Z+hN&fx+rz03_l z0vtwC?yBVr*8GaSn|roQX+wEw?+Rfg5fD6Ias=QO#6Ri6K45v$T5F7}|6!Clvy8;R zod!=uw;wvlB@~f*HSl7Z;iDl(EP3nfwFHu>D5t$PjKG0ck}fKjpcad2%w+uLl1Dhf z^yeh}CDrFK>mhweq?j1|BScNUIy{xEOQi;cJ60~({-8aYvURo&1yT1NDbPZ z8Xj_dJ>Ge3(E|ERW6Bt|8qPRe<82i@RGBU4={~kmH{8A2#{DLE6K>-~lbkZ?{FL3< zfzCNau_0ZyR?_6;ZZqj>Q-~=g5!cTRWVPNo8GVS=w)$u^A73C)R~!(?Iq{vq;p!46;Y|QIlrVzu^&e85rVR;!K8#j<#l4l zJNp85ipFtaY%}m#rl>*@aGccM(zvro6>EKbF|3=SC`;sr1nfap=F7}!==#L$4zsr7-c8}CO3UEw2x|XQV*n7zr@TKh?ntTHC z1*8OP6xJLCfQSvEmgAD23zkOzO1IuKasgjl$zAeL)P76(XiR?`kuksT8lxpvP*?*X zC&_J{Xm_0c?QmA}-4F&)tG_#JCp$cT$D+jX3Wi(3n?=vR*V2ABI{tP0-X`$Ot zUccH6=srpqov(R^+#5g)-Dj~Mt4g&)R+uG^4jmP&7>Se7gg-=!kB-pYmUFYVhf?_08b;XU$J;ItprvVLq7 z4Mqu0R(VGlYdx#%E0LF$@A+%SoHWYo2N$GCTdpUEU$!P?-8aO59n&02%lktBB2EA^?0Kd=UnVDRI#j2r?fC*Ocmgr zp9r3(49@UQ&<@^)MpRNy|{Ryrk~)XEmEw1GNH0$!e+5(`>l4-P1aUwBh30mCeqGc{ZNox9uCj zt^uf&QVNfZQsurNz+dauHX`4~y`M@%MR%3d9>f_w|4KF71>=H*oriH4(^**1|^Ez=Jdu4cw} zgWmd_9%}iPgkfkvyiuj>$sf0o6_h$IHu*Ts=yGXM(v?PsV{6#-8pX ziya{qo>68%G^H^&`+=p3G1Hu0P8eS8j?(Ad>Z!F{K~rCo0+G;g7Lg`)?xdeMR_9%_ zmaXPPsm;A+-N~}c0pjy06S4zzh=#kR5&fTsbd+C*^o-w!bZEwa{QT)}w@L+48>M02 z^CP1Mjt<81fD^h-1#m)-C24|pb6=s5ejG@?Wp6+ouZSM2NWUK_?cE}a7?HuB8dwjK?#f8`43=!kyO%3D)!)sDQpv`x&gx2-J_HxuIK>9I&-vz%hdw!GI)lqiL z4l|aZ>viDn#m8joI7_pUtI~fbWKcJeb*UmFIakfk$z@U0`4IVRn54R6q4J4#I5SCA zh<9bSz^c`^7;ZqY)H%riDKKuC!Bj zO3xPJ{#9vL*<4?S`W33=r3lNa&7rB~4M5O(g%_9a{D}0Y0)YWb20X@IA@433?G}FI zehml^AG}^3Wn2g>wbZkT292W0r0&^M4o)!}dJAy^$WjkyZ`jRuv;&QDUyp^e2pov8 zq2t_Cz}?XFAzp&gf4&Mj#YXwHh+Y5TP~$WWdocR4hy|gFYO!j9ZKZBnxqVX8i+}t> zba>6%5z;xL(kgr*rZN;ZG=)xJleA;Xkt)s!ErXd>@cJFlnp*#HDXgl0SfKzEfTA`k zlb5=xp6LuNB<&Vy>3(~qtM=xlmUk6T8Eo`-J3Z&CY8i1(pq3Z&gLAUwU_9`bgknUm z^}5R-5My$Pj}If>Q(7FSNGMBw(0~dhQT+KDwaHIzYt`d*-Fp}d%<<=KV<*Gv? zYY&$gnCEQUn|zZ}^n)qzS%Juk_1z6+72SPVGI4}mc(BAmT2@E*lbaqIqJx`2Gfd88 z&K9jaqZSr6?Wx|%OPwwc9m%wceB$VB!SHaVAR_&rZBLH_E8_^UX3)ISSV#9*r4BCl zT}um$EiKqT&lDA@c=b=Y&=k4S<>!@8(jR`N(yHD}zdJYgHBkF!m2B#<_-w$m9+tWz zLVXy&dShyPI1yRg7g4#M)<&&PTNjLc6Imhmp#H(E<4Squ5gy_m;)mP14r-bEChbz> z2sh0T6{n`y&DY-giR&*GC)({d)y&eP9GU8Tneed=_L$*`@dGp89JMuPx`(m2o!Ol9 zx!LsTuXZ|{Sez~v*mMpy3XPJHyU{@p>z0M|PH>Y%5USBH^x6Po+9z6e;8_u|jJn$N4lvT0{x!*erez1@vz|WS6*y`E zw*d|U;QISLFf~anv1cY=dRCfzA5o+*M>&%k0JMR(Kg(7+RMZ!w2Vj6;b$74@LUqTI z+qUIsMvj`qtIQr1D<87S^NSj_(a9nm`%=bVvk-7IAZAHu$A|0Kzv?QRSeyYxSw|xQ z`etB}dr9=cXxG6YT5ByHAXvPaNl+D{?K)AKeIa zAMo-{apvk%&h8JoY~%Hn$nZ`x{#f|bXE&)msN<{A5W&SFvUQe9RO37Md7PF} zStrP@F_nQAWPPT?a`D4im}jP9RWW5sM{`k*B#iCs>tdNhL}wSpVfPB8lZr=7k+R|SS)%h*X*&y zx@z1qw(TubP0)xVTpw2(V0>#-I)tO%n=~a8OQqCAnw4j3Bn>)y4qFaAUWc6JNdBy> z?k3ZZcq|DW&? z9lZ|zd_2bfV2dK^WW~Xngu&_Jo@2axylsKWTvB<6bb5)N-Chiz^|0M|oRnFGR2~;* zlJDH9`pM3$K{omb-p*}jOMb41V7pp&S;5)6=e9mH-P$l4RTaPqF^yQH_+x;833 zJclSh*tlY}eLV5KL4#jPo5kwNg5dnZvV^7swkU7eD0zFYRH$c(u)e*ZtaNi$w zy#Ir}w~nf6@4I-B6c8y1K}u4(JER+=ySqf`M!LJDySt=8q`Nz$8|f78+Mwrn&hxzE zz3=_|jy=X^xi>J>v43m**8I-ToDGk4ylGh{?|;?tI)3?8#~b!o$J+&}P+6u$+?4mcTI5?ibCcc#_TH`G(q;GKczA%SHG<{v&+2LFUn=yDRz};kd9wo zq|n(K+ihE*seE@fZ$LmntigL)FFazl)*N0otNIl#A0`Nzwn-xx2`|r8B zaxkSViDjn5f;G)%OY`r?5}D0u26Bn9r_KV0o!|uR$!n__i(>mt`b@g0HY<96`VM}> zv{LD)D>C1CqpXS!@0nVtDYDUa#3ch z$j5O{ZIhPzDsp$-s~KyO9rB+y*~BG_vlgJ6Y;-1h#p8ELNbz7Klb6g%ZyUA15D=hh@(GP9Ae{N0mAKP|+ZA~Wp0AJXO+o2771toB%7g@_+?1m-+3y9!*J@~rI z3l7+rv^8B zHtg18OMJbTNhOViRvQaMvv>k_qx=k&>hn5%GFDXPXmY54P&YXmMv`Om$2eFW~_za5HKIlbaD;Eo|5i^J0z&i^C@^;~cy zed`2TY@C2gt(|GFWH7 zDXyOOz4T zdo3Do-<9eH2aPLB&aI+GDI#Guqe;Omoyo5*CZ>dBX);$&ji8k0udU^r+L zlLAyO3k$@6buv-4aUF%iS;1(IWP^GfGqI|ZYjZJ7_*l55nHAV5qjOetys3krS_@WnT4Ha3cJKrU|~ zn-=z}tC-|28bxklv%fQStCu9lGuxmX|5D{j8`)3o^jK!4aD1>u3}*!8ae`w&QQZa{Yj~aLkTVDV7_J3*MBacPe z|I)yvF5136%g99-y4XaiPFcM2-S0QNxosMz&8#&+ppHbA#)@8=q^MONQLysiNe;=a z%+KX5Xh+rB0matud)G#-unIp#Y{g_N+@b!Je+FF9Lc@2x2N5`o&PUPX;$1au48-^m7?W2fcJ#IHYQ71ThUi?uAVP46rNK1blH9`*uK$U>2-nYPW)7Gpkpn6DfwmP0cD)z z)OEwP-yzfmWDdg^LcFop0xgaD&HhYw7oO|Dz-=Vd6}0>9JDMwbJzNMc>BxJsveviq z!sz45#Av3u5gjjEHujpnmkFUI`0;$+H19;upf(+}EK&+v<4@*9|G`vQmP!n7V$0Am z7Go(X`k~n~r~aC#<`XzVjnT8LCJPO5Mh?^A@tT&#w&5C06$?M#FB@R-W+Cz{b?l_2 zwtS{xr5&?+xQT;$j%VZ&n_#&oUOgCBNR@0!GZN2R^5<{lES;Kxh>mQF_V%|0VUr9% zLy+Yq7?K8r9zjA&uQ1 zEtSu)sj*J=7Y;I&9%yxZg>2XEXCO0-lX@4wp9XihJ9|UVnZ!4uiz0QrnTXbySZ&^i zy&y8cZ;MHP*oO=qH=J0oB|{`Ilw>Y)5Xf=fZ*9!DmW>+S;Xc-*8I?VBm)(-A_?6Ft zJCU-;YNy-29A?3k%y+Fw)^276pD*@3GluR6StV`%sOPO|0y*P_la@whW(jPayB?#T z;`WQHA?qb3egi5^Dx9go&BbvTqH)5!-GqQ@()HGYWbgSMQQ-wc@3`h*G%+O=#6HNL zeNS?cZwgKlYQAf`nN{_b5isT4G?)t*?z{7Maa6Y#xXvB!RRbLw5Sej!>Yr5>1|^3! zEO1C-O{eW$vTY~{l{E!&)1i+}Pn}rs;L!pbCy*~0Wg};jHDi>g&*3cimAc%HI$@G2bdL8i$)|#~7B=ka4t< zabsd{vo{SBO0r9AMtGwc#d2lt=~AwOF0W1j?B@K=Q3MlSi3v}!aQmx>&NxpZQC?fU z&-Df)wMh(vrPQYMy%=AmobXdTB0Pf}K?d?xTd4^NKMkRVM_~UYhgw}AS%H@Zg zjSIP2IlR@f8QX5)i*waT_EcYL@t-Bx;o^96<7P79Z3`m98<7RsFDFxXAl8fy7n_Il z8KczmY;;e8wu{JR*Yv%cb*`qY<9v#$;J{?9)I@sr4Y#!9a#YMvgp90gZ1O{LScsgW zuwLr+B5x;ohh`oneS61E>>7r|ee0L$Izo}gA*JNp-$MECTgfi`$&HS+jYSf??iF9>O>bu}(!@L!%9+OxI3|VVlju+J&yMKpi=H z_b^o!J>orTnczRXWzkLdbAWh;m)*{zVYZ-ME7d(27R__sqP<)AS{1ND+?hK^RVDp!p0>LH|K-f=Y~mXCP1W_c5q2!ST@B38ZyB;a6+=;?d2|)xcekGTz{cXy*P= zxQU`@zrhQ|D#`kCh0Luv2z=3ars zS5$}x@%80d38>eYNL#3hBF%npOR%Z%U^M}c-x?XMF2Kpig<+v3XC&1q1v9BY$udLy zw39{`3$ge5RGc!THcb${fo>uDrC7jR{33fY%Ej%bDP}WPE?;sU8In@#Q;gs@oxDCm zA6ar{@;TO99F#CJ`rp`KPm!7xNbif}2RkT6+x%wROqjCbe5XT^>vYOdNh8D-!LljUM`y-iI_T;#lat%KbvH5ht(bA%T^%Mn_Cjz zo)UQDM3xV9m=-XMY{CwrObj(9Eam>>O|>WPEqPCa5!zqe-a%2FHEgVxCT#jFBdM93 zOdTT)#lPpGGy&boi>_|bBvRg4J!Wp5T={KSn4K27RCi=vgOdb34jGzU0m`JJKaB;S zV{Kd?^A=|)(?rq5xd1i>oRa}@j8ild3%*kv|Lv^Ru)M!=-nf1=2O0Z3aFwJDffeLF zQ8Z`RsQ#ksIpir0%Mzf_npw8`=E%cPtt}JG3@4`AQ}~G?$J2z(1J)DN;&zis9Dw#-k}u5lkm*b;eZ0Z1_nW_We@+W533zG zHNSXVGSFe*!yI;}p2F!j4u)^S_+)2OG)YS4)QmHJW$>+)@omcCZUvhn(l2X$5 zFiylLsBWSvLp_Zz^Lp z2$0KE!G^(G*+6m`k->vp7Mtcwk=+PgrL^i+I1igVj4sdd#kP zhyC@|JG%0E*>f(B;^fnh_QJDViBz z3dW|3D%bYSOSyY3;N&g|r3P&~jyk+G;UMA+|AFcFRqr&8t15@P4J*W-dQ?(`g)*T!~96HW0q)<0T5N z3X{gJqr^gU82djdWl<8ZFd-8zyPM&0wn+W(QT?hzdsP7esuCw{##6%On_&BYk}OKoA6YZ2l>svn6U6SP71a?a8Vx1Y z?xnsZvNx)E&wBeM`SKc5tvozp*jwhouMeFCx7|FRXKSJ)ow+pxvgNLF# zG0lR8`&vasO0RHi($I*7{$yp?Y*$;u@s^j8abg@oQYtlT`+e(OII%^--hPg{kX*MGf|8_aTux#1exB7XPDzCxgr@t5Y^f#cQ zsTAvp$FzHegH*oM(Gq0n=!pCJvB^{wj&XX!SW>gq7a_ zZC42EgbV!0UBHgt8Y~Jcjn06z`GvIl0Y7HO@j<5Ej$Afj2|i869D^}W#GoV{aIHu7 z1#CagkOAivXv+7Up9v_4!)*9k{{^15lZg)hr3fx{B=9$WnDSN1y0ZP!Yy;3BZw8ET zv6+oh1~~h4+D?Z`dCADl0bh=#i!$rf#>)F^d2x+0T*vp_WARItV@p@%vvgQ(W5?SjJ64&lC(I#rbXpK4SbH4_2h+NCzJMuW zZ>K@koKMXn7|D@O5M4%dnDCt zs>|#9$9WXPS{tkFi-Ad7V_T^FXT5LS7)E@l#ps|+1e+T<-7n7W-jVi?+eGceOKD%i z`_b^_G^C=`Kj<&3*wzQlt2A=b3zhN{! zZ;Uhs9`PDPaR{rnw^;J~+~}#uNt0GPe~&`gr5HOQnydCTbpOhlRe8&@+WhA=cnBV(dL;9*waw%IX}-y)vLa2)sOxIX@lxh3Pnu#Yob+9MY?gP{UqN^*s z1SfzU6Y*kfevwg~a%Lx2IwxMz8H>2XjWN$W9Pd*_btz(k+8->qByi0~dX+d^(zSOod!@-jT2sWDjp7ynwyop{vQq-dl zB(KpW?r%RghhN$1p66F49_+&wrZNmYUv$2W!DQ``YvEupTu_-uEIPA4LtN6L!Gu|* z=*%Uk4#s58K(K3X{IFkZ6o>16%2eAaj=qL{oBq6-{lvI$Fp9{+BVRyDY|ShvdBL@B zejk>Q&TSyUxIN;MOm*q?FoQjH(2)hvK9R}Z=}S3pww*F9lExC@9xZ`gVv6TU7)eTf zgW@y%-IqRD5!TFl@SRHozt4fROn0{huUTu8wNRC(joTH!J+I?sU^>tzp7MVVZIIVz zTfbLTZTFVy_Zg7i1}}uRAv;REWvRJ@-;YDZM>YaQb>fv{2L;Wh^#!$BxjDrI81eNx z=MOXo9f}Q_nx_^@Ld#w~BQHZ;z#?5G$uHdaqo|XY+>2i6wc}V8NK-i9HwPPOWO0Qc zA$+-6AE{*wC3|T{0T@pgYmV})zGR2I4O*d;|E6wDLo_m0;9xd#(!kO4(Z5P7yj{Og z!tU%UQ{oK{XJGNb zf_qQx%9(oSN=IoGG&E;u5qFAXA~6Dlo@&vVYVrQn))qz0chr1S#@UJf(AGBl;bwjP zs-g4(gH=#=+-B7!FOPbeCP`d|U3cjzh>sz0`|WA1zi#hZ!_s}d_h=E4I80P*azRC_ zVsS&pkU;G9-H21x+*S}4RgsYIq&2gYx=Yw@S6Y?tMrnFsi473p9jY(*8kGI!i@P6) z@O7L?`FdME9ITj7wXu(+!L||p2#^m7zF-37fqWO?i5o@D6r%IkaJo%e{r8DqANo} zxLaUFt)=>Px=y8|POk@1Q$nkR!w?1tpGK21puatf0s5sM%*Nx20!D@U!2p_js> z%e901?lWKA56dT1z964}4z4P)od@an`*ESzrk5Qo6k5yx{k@D#K~)($BBn*M z4J8-?z}b{I;E~>WSBP%!UL-n=$`d<|`WZ&s3@Fz-qnLJvAL3Q+9w z&LXlvmz(VaXkxxq#j-R|c-r2si2f~wCX54|i&Ckc$q??RvyeHJ zqcOh4rM6~EnBxbMCJhbMh_I(3Ax*|oT4*duQ<6cD7Pv0^948+2kz3E=)CXl!rqDa( zUB0(q^NUHy_h?7#1dx$X(iFbmVLVkk9@ns1C9;t2D3ymT^lG1Fed<_8y}95b_Y&p)su>u=IXR4$`ab91ep-_xsDsua2@dxn@p8>Z`MyiR*jpxXz zNtO1NOGeaEBudoV7o>;#D?u-@DQj;V<9|DN(J;!%%c-(C;vrR3lhUXOW|?0^7ZRd* z@z*PV$&S}r$0?WW60(1SkU4~a6^STH_yRxX*%+48B+Vf_R^Oh01JiuQaafS}OyEfj%S+Wz z(y1jL%q~e^rziIbbA}o1_vb%b*jtcBc8Voc(aV1z2HT0%qo7(HAyxaal0xJM^s`qTD)eiBz!mYG+m zfVuJ^Liv*V{a2gB840=dsMhz)NZ6EAK~QZ&F@dhiWuK_tzqWXvj2{XotgxohrME%M za89i`jd6Zd*%g)|l`<0+Iup#rUQR8A%T*hS$>C@^yV3EHNp}|5PS-w&^E$;B9wuY9 zQA7yCL)B68!GJU&Hv-hHl?mS94a)41DjUKe#_9LwLd|7I(j`x;sJ)U-P{? zMT%5-`-PiWHmnG2sOSI5**Zw6a#ZTh!*J7|zgF9E?pt1RBb0cGiP1mCKR%*EHl6%{q65HQ%NmWuAA3ODH#Hbv zwG(JF9Q*?1BYJ{V{&T?F)bJ??F(_Lpx*UuaEcggq&-R3=KE>TstAb(3Dvmi3tF$zp zxY-pSy%yV-a}*;`Mh#2j6P9o*J4>s-hNuw)Bpx&PA9g1QAT%4W>8W3aGXH}M1UwO) zuU~?-pZu)rrM^kFoE|i<8G`cmeHoBm{kQW;xubPnb#>c(#{@Jt$|w9|ZUjd6Yi<;? z6^U_jR)YO6<24&!2`W3wUeb&>#oA*+q@%yK;>!J_+*F&hvWry;XYuC{35{QTyx|#B zUxLy*Ifjb@@F|$9pU0{pY>3yJVq+^}A#27uf>fOuD4XkI=IH7~yJ=$22uTITx+h0p zz4NOr0;fFtNv;}aYF)UwPta02qAr?#8#@q_#ji?8C&DE%h7~*N^O{9)JUZB11R|TL zaFCHq0N(~3)4-aL)N1X8B#u@<#vIdT?6t^>Ho3P{%^#$9%ze>w!vE%c^;`Xa?R-6V zIleur%oPq(2+1mfyQ-N?gF{ETrbmyJ5s?u@icj8}{lF<|9}qbIUNid&zRoq|6|x!E z$EXYpSyc5l>5K~KSlH1jmdF`TtJE<5NzU0IwUVj6W-@W2_~+vj686s z*8yPKrt06)53c(K4C$iuJb1QeQ(qW0U#TAj5CR<2- zB|LE@6op*3isePF>IH<&nb7oZHT%&|6KZu6=`07(P+geR#gtn=L#9M8NChA=&H<)X z<=wm@fWM}ld-9peK^Aa_f8nl=^gFEh1l(0}z5!O(ql0z0>^EeK2)qiqV*)&^Aca&K z4hS>@T&RF@1UOiMwFHW;27C6J_Il6^K)8R90h&H{k;%3K;AvfIy)ZsPNO`fhVE-IL zZSxR}uFuY21^6*QGX;JfAj>D)WBnCeKo1nrJxsCyx9fu}Pnr#q<)y`B>r9Quar0-j zFNXULWVlM!!awKW(n?%Q!BG1xvMql!pKl< zh8OZ7DR{$v;>SQ_qIDwmXm6j3@SoD7=BGA`aJ>&=7fGq~gNtV`NhVnJ;hd?U6cW%8 zz|GXGNZd-Zh%gXQAx`t87&~vtUr%q=f9hv##kW*;yIa@Crzi$wnzuSTKRKSv7)f3$ zRTY=r>Br0`;#3>p+!lQmOlv=_XjkN?l3z2%ZDFuCH%Mq}3l9eJvO;4l$auX^BtRkR zb$S^jl<_Qpo~SpRY&YBbD+z2s#bB-MPC&(^oT?Mwsj9U|@wfHFHT79h&6?z%ml~=W zy8>^l68Ysl!mGy^9FgHP9KJ%IcVJsv1!F!WD? zii?|MDqsR&fQ(OhSZwA?z<;I#d^*1@aV&UJfDD5%(@E|!B zH@*`rcmZ6<3XSRe4A*bcMp8*2fgI2GR1ufh5tjaActE=!LYJRRNghr7%L=97`(f~0 z)(5sG*>dp=!nx`bB()GwMrYahx=M=(A{=S9@35lB{Ct;c3D9<^jD(GL5h8a(e`%or zPjk6}_n6?3{GyMQ&jGBn+fmZ~zh!pBlJPzPv{5inW{12S$`+IpjJKwYPXLeH5C1YZ zY8f4?KP&-gN}#j3Xdhc5mGV1Tx-Ukz zsJp-IV_f*E9E(Z3$p{<4Su8s-N_y$y6KA%9Te6)?I(@2G%yyqN?v^}IHe;E+Reb6Q zLA(>5Qv4muPHD5hipLk>TrpaC;I&)MH-{YGO>2BBoE3Pvo-tcPEu|c-5~YQ`N!wKa z7$rgtqSy8LbgFA!5gT*HJ}^13$YR;4vFn7SaM%n}HxBh>RP{ON@iCu~2=j$wMV<*= zD{EvuAG3KKOJ2_IpsfE!e$BSfng(k|xoWsyM6N#TO(}lg{_D7_Yu!$W?hqOpl2e4f zmXroceHa)Re@H@<`OIwih~?%%iOmS@wZyYceuI+%i%p@3p##oTDmLja(wV#5lGto6 z`a@30VUuP2yq@r|)I?vF--R#p0f!}2L|QO!Wd8zCC9yX}_+~;!A^<#pBV4nufUsV`k&s3V%P&|6VW5W18&a2TF5Ob^V-?mS9 zxS@(tZy~x_DDNT_on>2eL4f*mpLdy8CN(()^|hIEu&BLoRO#VA697;iWkSf0;74vx z82{WNy(>ipRt)oBqg2<>*FS-?_Y;9{$BXrbnDo)YAeZ+|(-_rsBM3>0$aT^J4FMiP zasEWoJm3&or>V4wE<{jYFU#FTCOgJ9RL$6QEao4Vi&(D9M8;`zz|66^On-2}To1hV zOdxRXxJ0D--Q9Mzp}|uo3@@^*3;9LlymOmdIiB~l>N`N7Wx$PcER`kCP~PP%1~29? z{WCEEs^Ra%gy<)~6B7iz9upH*CjM(;0-6;ouwot)6BHVMB_>3_`*&hO`#JAXri!%n z(D_$)o&Et}7s^D!MZ^ z6E&D5s$aQjY5As`Xe`8aES8Na?TsFBzR%09;*}RYg1A7oqj2)RzrBcrO7D${1B5Yi zykEi?`Q^`_h}^VeJ7?%ZtWVVBU%Ol{FXiJOVIS4{A(GTi z9*wN^$eqmSAJaNAn^v!U@2YX7#2x$w=DD4!HHJao;#*0e| zUb0>x66v1@{qeR&b>{VxOA|aUG+#(*lM~*z3SjT~I)0zE9HQpB+cJB<5l7k0a=Gf~ z)RT*Kf{E!8c`IDNul|*-*R$UNqp`a%PTpa{wT=RJCU4~g6WRvwhCVYIw2cnV%8SL} z!HF(S0m$kJs^4Vwy(~ahckn)v)#rba)p$z)Sxr;&KvqM8$m%=wHa}h(>;>aNz4cgk7V`H7GnkVOMehq4c_!mvf50%Ep*3_xl>V?m5hVF zehuyTwOMR2KQ3`+Y-N({Zr>e$u1wwYuLgk!r8^|8&}8{aU}Bm?e0aEythjZ>4b){v z8s$eMQv!i!ar;MP?{Ze1$Uyrcn>SmJlL?t$P=7ejMG6y3q+R-h-_Id(yq#qp(kZG zw~~%j$Rklzz!@EvuP$pMw<0RtPB$`Ti8eDO&7vuotB%ScXC$`dI(dba=(2rk$fYY6 zr4dDJ?+@Y6> z^Sk9ino;}@h0?zFeB%83v{?P`9qKS+riC=fW7n87RBEw}X zQBy^Fl265~n7sKhHQrYwjirjc{nHc)BM9G-S9W(K=IAetN$FO2PJNz<*6FG(d*F6| z{?vSNQb$+9*R>1deHXpwx}XhK!FU~eO3LDo4ZZZQk+tmLAoQzY#gIUfctSi81b?*w z@Rw4?me}c)$0Ph@KkU*Gxuix)S)EE4aQQz1*WemgL;nP>v8EZ%ChX#5R_@9j2t`cs zQ+Fl_tBCw#3aB?Prw*;R8O9tPB;vG$6ZPSP5 zKJK_Yc5-sDDKj(7a>zqm$I-rOl=#+#Wp@UKN3bim0<8fbra88g9Y0qy=@p>Svvw`6Mx&Fz0{4*2E;thoq-~Uo`MfzvvgFw*fNYQJLfG zDskblvFite>M^iIfinc_q8xLJW~wotbN6fLgRnxIu%0hfF+zWQyEBIAHSKJZ)M-pH zmPSxsNFMe1%$ywWWn44N&A$w#T!mjYKtOi=SQK@1b@h9TY-&Uc$f~Jz0?Dn(f;gm2 zUNbg4^4Kc3(J_bVGgn7mENe=W1saoXEd&}MkBB6{Ng%9~5>9upf^L&K(x9NND%MJ_ z8_a_1O&ZnM2%nut3WA8|LO!i6z zh{@iULc$u^dnJ($jJbKKT#Ek42zm_3{(BYSi`qr_f65&>QH_jqeqt$jhN#EJg1Mv_RH5gi z*FpmcS3oK=(^goBTPJ4wxY1cXQZqIooU%}-01kf(j8nLxV9v$gn7rICSd0~)QOwT_cWRb>A2Ae$qf|UYf z)i36s=j@WSVDJ-?zb}95yGj6r9j1Pw@@!Y@I=!z`#hu;T_>jH#a|t)Q{hkcx4iLSd z8*KR$i-Sr4&31``SuIvR!9MG?v>v-cM4l-gS|6PAhxmwHf%x% zhl%=?i>ifDoBJ__eW&@O@Ky5BRiF@x-DCZvZTa=sm2$#GX|G)60(avxemkzPy~xB= z9wSXgR7L&8&i;=xz7aS@(OSwe6-&pp2y>v+L$YU-PcQyZzFJuuPF?6$1(}zGmZApQ z{Ge8c76lY;yzP)bip!VI+Hx9d((4_r__*re@i^aZZLcN#+=?HV&P3f^SsSt5^)HXg z&9TsYg@Lv{XGIt@;%7`Q6K<)G9Y;_S!007t!0_Nng#t?808i?RHNca4AP^pAxY?Zo zf!YcQcl-pXcE25bdcg#kQ~$m{!Uf)V@Hhcq0plF-j06UHems6Oc|cu(1#3X~PI}ry zg!_5A01hXzX(+tOot02e7tlvl(KsBBq%q0w*7w*oWG3F_oOY`?Z zX%y#iO~3bIGPrz!CCtPDNLL&_8r%glPcR!~9rQ5%j&RHTj013b0GgU-2y+^W)0Ag8 z<;r=YXH1>(T2baO*fQs4it+lSAJt3lY)A4mBKRmMzSAp&?v@z6U62L!D`b%0%h+B~ zm+e%Fjdv>|ZRm6TosW3`D7xIJld>gCsgLL=DO99JPKQ!p{xq$FNlGvl3_TM;g^~aF@`; zn&4exyW%LlS!0Wnl=Mpa(mihRdv3dOZzU;!c4Lszgeo9h{=6+-^<~;M2{OC346eyi zdJlj32lgR#=3o11XZAypxP8EmLcL?mM&H_bK)y8@7w_7uZ=G`WoME4r9pq#HnmbIM zL}YAI;I5g!Fww!WikbGPdQ$5~oW|uZ_=EV%&;AY#)XF$KjoJC7U+~9Ag4Ri2&y?ozF;83JV-&h_~aTEl-hLXl*-NYY1ZEP@Y*r$M@)k#ts>4UARR5sKG8 zJfT~SB~fI0Rzr|p{K=Tzt+ydkU0!_?y-}4x*ZpLbbv$Y#&Ih%E=;MgC{~Ip?u9Eqa zU_0e0PG2MiS8z49cU)ugEH&AI>r+KJ^xfaTOxp+W?6_!3V4Nm|C0cYgGAT*?nE3EU zht2}ULDr6yn0wfq!a-KMII}|WM^>vjjB(Af=*#k%mFq1r+~z)TplA_LvH;l`V6g%B zcl%!^Lg3Yh1qP^AfQgXv4ah|JTOtEYgn&fW3P$VSy2Vin3&b0Oe5#jaIoV2WQQ$!O zRAvW@gKU}o5*z6IINvObdeqJ#{H2KjR#j*?B8VQ4RrP<=#Kba0Olk6DWvjcs#%3fR zWFqV!i;jlYC@at(?lQcz(r%$c78DBGP=6t|MroB<;aU(ni!7UIm~LN9x`2)IQG`Pw ztD&Tsjr}@!{B7d-&g#!r5%Td?)+$0PKQg9SZRQ;vgP%J-Z-0j$()}HN82yj%LyEt` z4;g^)L)*!4<^ZwOL#_n3)F1$0DeF2?orO?Sb17`So_bg1Lt2>zj@Zl zz*wP9C!oU*biNHvHCpAySh_9 z;?p)SxS`)@X*ZJv;S^)miyzM`z9N!k1lgcI8db-|H@z)LMkSLEhsVbxon#61q;ZU; zKHKg~sfwczWM{;(Y^m+&%&~1V9}=HaG8HovjvWTu^f~gaRZLo`%#6#GRZ*ft@+dWh z_Xn&xXEctDuFG+KN1)E<20dr}S`69b{^S9XiN&?HT0k)0j1{f88s8wUbo~ZR-zj z(!KRVXYF|CygG3CXP7NH-%VAF*b{w}o3nq#qaCXO@o2m69^%nt-husp5WdR(Pr_H| z7X<2v>I(z%X5xCgfw+&QzSQ;4sEBCDiKx~_a44sI-Gox$REjZA&Xr&1`ASaaiR+T& zmy=}MsN!iwqEj6^AqLuJ-0+Q?F%R1&&%2G&lge#W_1uqE0svj^lymVhs>Mi8UJT44 z-~M;LiG~ctJav&ZxihXz*O5*5m>$&vP|RW51b7KL>kC29u$k;A^3)t*s;q(CDCZ}g zd0B17H&gT@2I5WzCeBodh+QPDc(Ifch8uQMs5(vCO0_9hZ8^;l#<6tcOu`ws#E^sb z22MOb_7*9*!>o{0Ki1c_r<`2pHS&XT79WdFETy<*tObhF-Qj7BSkuiHiRxwz7;E}pOr1pN*XooQ=(QeniAz_rU83(-?@ z5T&6fkrB$|Jo%HY%7|khX9%l0LUP}!#xG?+@ch{fgZ@xB7e8=N*~b3j@wjMWFQJ)o zBK8=DTr@XX;%!yb8=SYQ{8TJ?9H>R{RTIhSyuFoYNtc(M{BN8mB{vE=QK$U&RG8<` z9tW z@}~a1NFv<+TqM1OzZZ$Q=C4IE`d=5xTc5{8;+zGG{NwLMqI)cVm6G|eNXnAE_Wxs% z{3?d~_agZ_@wr6VasOcii0%$<31Y`+B1fdL;+9qbD0+C`=0oiyetro--9Mq|4Bkg5 z`sja$q620BS18)Z83aWeIxvAuOQo7<12KZWE1y$Horb@BD_wzV)Fmb_l5PB9#chB1 z4L-nCK}CP$WS|5qNA-D2>7(P7LdkM^`@!$ZGA=ud|KNActN&#P^L)47D^5v=Q4r=M z72@9Et}?N9cL#$PzpcOI(B)@dANMBkKH$dNq=b|7e!cFBs$hl7^~a%n_+`lD+;4xb zzLk}^6fVh-P_ZJ{?bpx#ehhSuo6Z&*tCEMuA}+oe6CVFjOY4u;uSIcF~Z$ty5@0yCnq#?D$#w! z4w8~eXazpQA{5Q>4l$FKt=2)HFf(~Cr|*;Gav!qx1Zx~(!Z&cLUD7b?g+|rOF1CG* ztzU((YfvUyl#WzuuT5C5^Vv_N!$ZH`=S$c)HAf42eBgEHTdRKP1DH0asU*g#)I@*@#oW z=SLezTjX5H_TczFw|)ahPWF%@ap(+qEsD^$mYfBR>*xE~ecGzDRBU4f&+Ve$WD}}K z#el@Fl4$JWQRJb!c4~o)g2WkG7mcV?DM(38m`5lY6$C|7fc=pusPQXN(CqI-L9YLv zD7g7&qF_H*_dgN^J%L0)A}_iLP@rI+88q{NOpa3sN*pygq0x!M-5YM>I|cbcY&da&F(9yA!{6?GJ5ug+d^ zCj8x}ffjX=^z?Zz`w#Ut4|88MQBQFmu3>_tjdR71;(`4RKGZMN>&C_53e-=6a_d_$ z*a6jSq+JDC2v{bUjpIfT ztuWM*Bo@Bq6Zc61JXa%KXa(Sltd$AJfv@3g)_KNc(9VE>qaqpM*RZlpCfZ};N^=@Q z0cu?>w-C0s{4$kis|HQSpeI>d6^=ZT)F>2RCEdoICD}Vhg77{# zMd6%dWURfyBkBr;NCE zu3DPmov*}IFT}BqmD>BXs*^-bX-39agFN5W*TkPFKFD6*jXpW0GNxm@X5zcSy!1h@ zcp^H?j&1Zj7Jcasiig6ohFNSeoF*k^YF;Sfd_ZQ#D&m}A@;;W0%uJu)Q}tI>waUr3 z8XQEVesn{QSCtm3>ga}ZdX)>Xe1xBqz23Y4a)0uC!kGRjMQD|T|JN?@pBKQqR$v+m zO8jw#0dNmcGwRtj)o)u28?HQ%@didc+B7yQmum_MxMB#v0s7;!3)zRiT`?nmkfHYZ zRuCA!Olw44(e~R)!!BZ9}tMoOh=~xe2{GXC`A;^Z{f%=RG4L=Wwu{NKh1` z8m&MomxWVvH)3We=}(g=RDcLDHEgo7gmP9mjVEaCTr&y);D-U>I$u0PaYD^BZBz3( zxxk%D!2}vsbJo4&57W_JVl~6GzYH~X3;zJINhluU=h&yS{{pdl!5=TEbKc9pLF|wz zm=4`{RA%~`293}`75rtB-<2r}4B9|9jd`(5x|JQUF2soWRG^@}YD&PE|M>iemtvyg zkrbY&0NpeK&j2a>`xt~a0>{t5ynw)brCPpfO8GHfCLG|dQE6O;3LZ$<6ArSJ@B0c$ z*z+(|$&2+dRrxUeVH)D&bY3i!_Hz5(f+yYk3hJnJ;s)w3u?VdryCU$J5uc zVf}6IzPd}RnU?D&QO{3_sg#t42mLT>ReehLSn7*%1=s46!`Ke+cD*G<-Z{UQIy0ga zC`nHf6YVkVDYE-I>YrcS-EP_b;3=r?KQ}uY1mp~9TpmYy$K%^HW0##p(anwf!JmTc zB0U_!(kVQ}?iSzun%5h~Cd`y{SZSO?5-oC))yLzbr@r^ z^BDQd492gEi@*TSHL;Tc>E};d@JW;COn*1q!IGbe^OjPU?LKTF;fxGdX)gCit=98? zH!ksj3HB8_pYidfaO^ANQ7spgPeIPjah@`K6HgaDE1=QuGFB7YsE^U0FU2|u^KoI> zj5m~_ndP1Oq15vn@LlOE>K}XcTq$U8 zQj%}Kx_JJ4D*Us>2-xKA>_V;pUPkeD%}pyu#QdDt{hOvv1Go`UFCMJuj9$xEq6By! z=bb?!=}J~Dd$9d+M@%ERi-BFrS^ZdL?M@o{%DFJvU01!%`U$YH>ltv`_8Eh@mx2md@^9gC zp{=V94I_~zA?@0DjJug=rzrxmqN6t8e`Q6pw+Uj+`y*ZawW)Jy6-l{XNTAytT-m&P zpq!lBEH++n;-UXzkm>U<$mGXSwcSXcYmRajD~!^zu?adL7;wPGy|sqXf@?1bb*|3yp_Rx2<$Tc=|kxACB}xz$<3aGc_iu*Tq|BDMpzx| zNH6&KWAQy&`leVf>h9*=x3aOEz#X=`F4;4~hMUmT-{ z&#&e37*E<*L*L3p)PizXK{?wQ6X4V*ss}pt(d=OeGv@{NicOa=85m__Cv@G_op&g% z7x7r*JxyKAhCC$tYT6&88fs7*!7+mx2r)t0C~iJdgn8w-8?4*2 znkulB8cproNzqPmVcfGCBd`@?<01)3!f!8A;*7p(&2oKzc6Prsb0?{t+vAx~iaOyPBGeW5#u-zcC;q^)R@>EHHatIXbFf{3)blJ zV}FeS#CcHWo-ZI2z9S}IimzEozi$}7DY7Id7CIMhHui?@$xWipm7&NGU$ptj_DJ^B zEf}FzRP-Qw`k1g&j2LkqK~}{cBV8JvV^}`;MGC9R-ge8$3TlZh1I|^Xw|FnUj()`T zgG$4x5LV{j0Juom=Kwez4v|QhyPg~zbf%;-zdgnQN|>oSTtMu#qt2JjxSC>wb^t}X zX@=OFTl(CDjG@F2@CQC|T&dB^O|MUlTOQrJBK)W4Dttv})a<=zN9?1vmp9D1Ynlti zsQE?Q;_w7Ir>GcH*OP$?4;1LOWIWKRl!g=h-vZtK2T8W#H;?Ca7Nqdhc`Pdq3^LcL ze2NDsbB4LKxoChYM*}HGs4g4bg1B&mFO5WqTbmnj>yKVpZOlQY(odk^M370MKZu9K zW|o@$qAKzYsrqofdw?zvmd3hm`pDH^Am3+5_e-2g(8b4)S_uikH9qk6O+HyFspW*0 zVtcV}wHH~`xfYS(^W?8rw&0%LjWoF{2wsO1`SdWFhLik=(|Yf}&)CN0R$nl=krk5C z@`0c=;z_UPjB81g?^)Sv@H2VK92nQMEw8C&vxo zYEx9$CVKbrR^fOTLXlgoF6{FWX3QS+1$*MWe?@2b@{u03m3_YpXpa&(%m(w06a@wIr8)R95!y(TzwM(*4^b`Jh5 zX$`lspr~4EASKTP!}eAv3L(Q_28?fUHrtG>k^K!(PC;;Fv|2&~pjZ!`|JNA+HZ6zN z;g`~(9aR9mo7Zkjo}Jkm{$@cQIj&^7R909u{!1G|jpf>2-i150g8NZUWiQiR{DG$7 zQ(;w|*7ftjy1_0=brx^AwSVX??oL}=U1%30Q=$;C7%CZfHecsov(I60h%1zqHY;4f zrUFrI?-GEhw(VnzEVjw^vNylR_`>NXbRi{L>Upt#%n#Yc;8!wt_-Yc_Dq9Ws_uZ6k zUrD2zSTm9aPr*H|jcZD>qMJoFJ-zi!+k-rKF&%8np_)&ajS9;=hVqWU5*%;F} z)5^)HC5{8_glVm*7E@p6^)~GhBB@D_6;Bdr(3H(zxSR!J8TPlW5pLqq_)3co)#Mto zOWcj-B^IwVN*`bL|77KN`1zz@gFv#Fb3E0dP^)3n(rlEc0J&6}}}tjv-Pk!Q4ywI!O)K*i57r(SXE zFqPaoMX5d2K1tt0SR93*tFijCvII?b=*K9UL!;k)OtiTFX5jz)@IM;(3x081zhYnd zBKZfb}^^kT4H?bCeL>WGFW?q8k;IU_JbAGiNOs?c1T+UOftNGA?zHjXy^a6{u5g zy#uVA2>t~p&08VrsebdWf$X1XP0!!i zKZ0{x`p{p&KfLf=ERLBbqj99wHygH*FR%DN^|eWN{gL;KHh(h@pC-_Mz5TD=emsyH z6bbP5t6Kb0wk+tWLa=?KLgrFAMwwn`q}A?UlLY)-x&>w5kh<(XUV?6^O|&K zDp?^@Bea&~(`*=m&6@n4pGh7i;!$SdR9u{klIZ7Gokvwhq>*NL3YbcLW)X(2-Rf)XJn86cL>$h9e(5S zc3HN(F-&OK)#KF+8Q2AOnKqXZ+xN(7O`{6&=7d>t3j}Rr-Jy8){zsAJ{NS`u$dv4z zAu_CY(Wg@H1kU-q{*N90o~kHT%6+;u$%FgCp?uCFC3!a_gl^&2XkBD2@BwM3x!n-} zn7Zg`P3t$J8oHraosHzsJz{X*C9XJia9(R#$i7m{csyU&H_MW^?C8C4)_MF0L(sOI z7Dhr7cGK;=s(0-sffb+Hj=#uob&`v0nM_>gu&%DGWc&E7`(cZr`Jw0TlZhCj^G(*}d_E7bW*>E!jt=6rZOmhH~rx3ew?8P1X9dKD?Q z+-Q4{sxnBmtQ~=S-LUVYBZj%6eUXhqeeoT^)Zpisucz8A{l#Z@Y$0=|buXTl;P7ke z>WgDu=i7cf`{z_RmAlmvl3FZ`H)havM_~(kWR@4xW?l=mKWp|@y#cRRMMvG* ztHXzv1_ra}0MbXWY%D?zEd!GnFliQ-{1XQHo(LLHSwLZ+DNTAG5an<{p6A}03C=!{ z-hv-N;!J=6c%C6aL71(p%0&!pkDq5u_jz*g%?t z9F@{ta(jeN0RJ`%;NLEP>utReto~fR5NJTsMHfoI@j`pGId|c2vc@C+^MIHAX5cJz zP)z!ZctD$K2>TjaNcI-U^5o>DDeB+@vOK*VxnoFa>*)xx^=eXk(l_Hx`v0WD@e^Ak zM$5tcd{HG>3-=|{c!3Wk%!t&&^ME*lzOdrWmu)rE37F2{9|^EpgxIj0F)(3aBq-hOx36_uqAFQ7 z^5YSRUybts@P4$&83yy-stG4jZBg(B(n zXyjd>8+A`7B8MM>G?f3#fn>U@oXS(B6uU8;dC`UuE0yeh!&ere02_Dd6X+uZYDXA(WXuRH)AB(=*}Pw)~!lwcCyiKRiA*XIk;C zmTEG@3pf|9y5Z@!4G?n@{R~&Xr!5##keQKNq%z`a%OF@t&^!0YRi|xSLp2)37_nmN zT%9yRV^5tnqcl*`&xaS<8v#VJ{mEv-=peTsxM3n}Ocb^;lte4gab!xMn21|5W{V>X z|0O+Qn=e3~qZ%*`qXQm*y42A3lSpflKH*Db*sxvz8wZB*V45uxZFh>CjO6;>XzW{( z#>#+#ubRq)Z`@*+mAZPVeuuOjXyja;dkOwDdcYm^t-EYjku+gvdbfyTwt!PNjCi(R zg8916bjeh@U6&00cSf5JkkK{^%4jQR1ZA|TXAv~f{my7pQu$}ll8o&yW!nB{qNdRA zj5h2UiL1XEZHK=z+DJheZ6<%z8swent>ew&`C+t2)FsmG?Uc50m}k z6x823C8PmhK;+h4<&Rw-Qz`bXwR|_De`uh8Wq1vmscgFFZgTqyj}rd9`-MeBama-2 zM{BQLLtvsZ;YHREXpW#?C$1G0UGJPp!|PYqy0F*!pwnI~MdN^4hNS++o&hja$-G)Y zbAfR5?x+@j{;g&528c4vQnb(XhceyJ_M0+|)qsE50Y&zkGX0YvKIfS-Z65YxP=s_0 zpiFBnOA?8|ln%WqP%c@aC3R!KFvJc}-o*({=-=IGb(vBu3#5tW4+Z+Ey-w+>A&O@& z)hiH@)%(?15{4IFU-GVN$YU#?H_yv=$_+kVT@b>EA8c3johPQHMiy*1PD%riX$PWi zc#hO^l`(?zD|ZyaH@cg`C!-L{J!C49Gz1}`q>i9;HY;8nga&SehJqmKlT*`E%NS5PThOJ$yML#%rAd}9ySg%r z#Z&(UOsBES_R=q@5%}*t?XL7a1P`UCtjq5t#$A zf{(sD!#q^*1H~^v)Mp+KAuDLxCT@#N5S%)meNPIlhtasYThL{VRnxZilgGCDaS=sg z-SFl;I|B1!;_2QoJ%E)qq>?}lHQuo{KZ67Y2aKF`w2m6AzD`9*z~tQH={|VJafhOa z#3iH-yxL31>k^G;K;P`L%%zdyS8Nyw>%_|F_R1FHkt@*{+Zy$LSzN_fdMWjkAftB3rGgM}Bji!W? z+7d?MPA0*c;3TF9%%EEADdsfjs-N}n@Y{vJ7au7-{-e zONk7ug{Q`04kRz~?op3NXz0;~2~o+DXYWne1_R%-qJbh%j)sWv$Vy)RkXshuY$)We z`k|C5O3diR&>L&A5V5_x-*=MDDpy8Rok)xz@mLp)C%~3xPZH4AAHAdV-Yi&M?zt~I zL{9Bcttv1*`W($xrlQ+EjsXqp$?ebsi;l;8x$;SGYupk;XecL;#qS%eyJ&CT+iVbqikZV0Ob?td7N z`@dqtH#N5YxBLHlMm8fjo-Zt}@2RSE8WKHWp7$d!5afe#jg8Wp^}oNPH4Oi^9c_>t z-twOvZ8$s|2uNYLVCcg*QHr7FW9Mrs@wekBx;NpMe*yme9Y)PKZxs>E_M*7B;#-^r z$UmW4SE&8};H6*0=RD)3fnBYrgHc1zGyy@T>Wiedl$98hHL7f{wrw%CA&S_H_~VFJ z=YV{~CvP*r%zv`-ZOF^Z88Gv=C}YlU{`^{SxFP9WaC0T|D@^eVb6*QmWDUM%Sx=TU zkj|z}9)i3F)EpJG^X@fIhd(|FZ01;O}9qFCV>1+x>IvbqEv7KE=3gNHXUs?45TO~o^O4Ug{A(F4B?Y7UE-5fxo+A7VCE+Q%=~?-lyB@q4*2WuqXZ2EdEHm)rD^lJW*gj0H5LSE(vDg_v{YY(_IV#-sm$~`mlc2J4b_ORb#$#(m@tzK z!OToux};WfVy3WSr-brqSGn6L$=82fca*UJ{N&q`-F2_aqFd-aD`09%PsIX?82_?h z3vG}zHa-0phMSR!^$cqSz51E*PrQ@ z@^n)4*BqAonQ!mY;F@HSA$3en@+}Ka*3ygaqV#tjo0DP)V`?anO)Y&4H3l~(vHqEH zvgzf=+O3IuQy`W)0IdI$m3u_6lOm{%{O)7cGV%|0oAbtM(q(O-0B4l_}^Q`}1Mptek?3W7{C zIlLl7CPND8?dh=o83JzP=@oE1FLt`u*?ziv?B`BW7ABZsjODx>lwi69+1o2faJw?e ze(ms16s^UH)5-4I$cFHsEw0MC7k2R5BuBhE38T_>!0Yp?1R#H{8YK_9w?>w8ohZGT z>HU6(+Bz)xQS$)mKo_w;155NzTq`iGqk?_Xb{xmeSG`5)ynA#Mm2^Cbv1jX7Sm-G& z-D8ET>l7tzr>N=2^9oh=W}LLMH5&Q0d0dDbO-A3iB|0CuFH z1A|^N_fN5y9DF_D1p!ryBWe61h|Z4D6??~7=f?Q9PNeyeo&hEw$57u=BTmsY2dv~Y z)DF@&+vG3wV+n2(udb6Nt}Izc>Yh}Re>%bRGJn8d8BDpaC{|dA*{9a-CD%;Uk~Bmb zAM{wXto9!4OPwnf{-Kl6yx3|6#SQGjgshM$7?DQy2u!pgs48Zf*!D;G__E+VV&?+)EUm213}v zUN+lGEkj{L)SJRDp>RNf+kryZ2L6PwVM74six&Xh83s{-Gsx27&=<;27U}Y!2#uOw}5a2{My&_ z%>z3id#&r64lYnb04Ql-zy%)2UXy;|Di36Z%hjR@>^&6^GYy@E&sUgc z`UPXtslsci&AYTb&HwC=y?<@bhqQ>0lr5icHM2Cc_y1lM^wIoV*Q}YSQF5!&FYQqcEB@Z^) zcE~qK=V*qaAK-01#`S%$Rbheas_nYxLyWQF-$P>Y@!;VJ=Fv>}24w~!uf_S@CgTwf z_k&Eu=K9ocg~`ak5d~a@4on?i8g(`hgd}wKj2qH56ochTK_=|)yTj!v?v1x%7Wi+c zB#uzv$K`OwO$p&c22@hxpc2IS{8qqh=~!x9!>2=hP_2|(Ms*E`pcqq!Wb~cP!q1!O z`zg2N)F$T2iLI4Il;d(MVN{Zad6p}tFwy*?42Og8;^dPD2Xa$AeRTh5P`H2wwUq*B zP|^R=piqBnP!j~<0q}=SKT_SS-q2Y(9HciR>#SQWU^i0rb-h0Pm*xbJp@9$6JB|NE zX97=P`Hj&Pd&cMzgD|=Pa+>hPTYxVOOjw=IzXcG`J+%NvH*jA0%zWt#fIoLjjVF4% z>x6vQ`H48R0jr)gg}rI`sO1!rhd9*0&w#nv;15Z9;V()05g+Y0N&4MK(Wzt*+6Ox$ zPX3uBElzhzL6;Dfo}Fnhkq!POa9AI<28`FAnDm9t{+4krqR{=P0uIhPj-OSB;!?T> zRgse(4I=O=TVSM2LFD4RB_0itF9*8dZLtccd3zW#7YRaWVQu11RB$-vOKE09#@o>w zI<_1U*qG=lE}eMOFo$_LPYB6SzHFQK^$fSlFw03uuG@pN^(>~-w2J(ql4}=UY)w~r zvGnp~pw6c;RM_l%2)0`4ALUw2__+^wa!O^V$x}m%-IJ$uS#@y|yUqpdAtCr+ZD;rP z;MYA@Na1)C1 zVp5-RZE~2&x__nD+;@Qq@sjNi>{ZAn+IX|NBBvpGw~RWC=bo=`YL5o$PDvV_Q#w0O z{|#?0DTBU!hBqGsl$vd3oa-+@#z&4PS0C80@p1Z0Tk!HzG0uEd$j!TH@s$y~9rq>2 z58vIF&kRa~#gf}zxRp1OlfM!-+&O|Bg%_V6U9SVT)&m>&nrz<}7@4C`6;u(tpm7aT%kBG(4hn(QR4 z%wGv;6k-Nq*r8o6PeQQFBQ3R6#?P`sR~05=aGMJ(ufqENU6|U&!(s#D4*UiQT2&4N zHi4sLTJX=3tj@7taXRN(=M0Gaf(yC7$;`_imorLMsC(MI^pBnz-t<3QedDCZK0*g; zVC>Dh8wbl7BAt0k;tM%GUT$nYG} z9OmU*VMfdl|4Q9zljG(TKgHGV)KE=G+3#{vI#a49^1>9@5oeA#_+B4Ht z_m0nSTH6wk_NDT?;XWt+z>pMU#w~`m5^T2EDnpeG{VkbufzAdjEi#r*p+lMF23fe6MjNH^qlA17ht-(VQxVsAG!klh*@7OT6s0qziMU$mhdscY$X-E#TRX`JZh(#+@fHQr94xt zWy%Tb-!-Hy%W?R~5vUS6MunyNc&Asy#=zz!hdm$+fSJ9iW%Qvs0$sQooUSe`WqTL@ zf|;%TBmgjT5@O?6Tf`k&2DCLg2+XVlfSEaE05J0g&53d?^hCFMEdo@viS0}we(6RW`_6v3m^Eja1lR$m3~8co9X#J zL5Y1Z9(i~n_oFO8YCaGn=J0zZPn+Tz9nNhazy3{=yr!`@I#m??>*_w4am2m4!v{P$h9Zscb`^8jVnN31CJZ$O~g zEa%Hw>g^Ki%hR@scp_xN2d#x;I*(#Z6z$@5l0F$?d~J;kbY?xN+^I?rK1R69Qfq}Z z3a6p3I00kbGIC*3a|4SII_NbH9-akNwhOS>;5egN%;YmO%8v|UuJN}YFKQZcT_@gw zVTQx%T!GD%M4DD_gD<=rtzLQL6Z#C?4#fUh&C&25!&aPBB;$;0NOYH(O*v z+<-&7kD=Y^*Yn>qCoHf~XJbB+GH=($D*6*|}GprYsRW1#2(9s^am#mlUN?r|v>*fiADUoZdt7BK~u0jTBY zyId|YR~4oU;9KB7Qv9OL&h5M6>)^X?{KgP;Yk@ZeAkO)q8&h#U=xOaY+A4@WQZ6MA zUx(=U=Dvv%RI42w^R=>K+r9Lx~2|!<{ zHX9ezrWtAIdS2uFjNIKd!~wsiw$bpPH4bX<+MJR$t}MtWG~}s^n=)me-A8mY;+WL8 zmt8b$nUQP!7nlaA9U4cX__E>BY6YIj6o>11)g#er zza9il;-n$fNqQ|g==(W71UuMS?2RdEtrAK;tcm$wd>vycA79zxc5$aCc^?c~{Pxjk zO_VS;Lmk!zG-z_MUC&`JddS`agE--SUx1ZwYeyKR_Pcy0ZZ~3Ycl*x@UG&TNs1I#h zs&Zq9I7$Z?ubj3=bZJ{dvQr4tQn|D%wL{qsQ zk3OnZV9NhzA@u)wZxP=zxqpAPplEAgNkkz{F7v4ba@WQ2E}o!GQn}=YZFM0D$)XXJpwyLRp3pF%CJ0uT+M%xp z%Uqmr)vY0N+uwycV~tF4ZQl&Tc~Lv^FQ=>6Q8-V2!C6Z<3$oc0KS2{jDN=VZ$=Gqv*;RmbTRd`p_(3S#Xug;^ zs&iKTlSo3r{@FmH^U3lTGRKICw+IxFSQHLru6TU7z|BP0KfL*Pxy-);tRB4XdkNE` z+w;b7`jSL6Fwra-URM|HzJXqM3*RnewWs>Q{Lp;UO<;S3>a_5{$VI3Wj~L|bT-n%t z+H;*Kj62TsyzHSl^xS()+;b0rh*`GWMCMg`fUdeP)rm;1v@Dz;dmFAnhjML{Ov^JQ zAcjPcj6|mnuldDh1qQbFpsRA>@^D zXy1X_Po9PM;JV$UM_SUo*I{|&f}*+%VlYfcVV=c+_tg{DFDo& z3CP5{^@*ep^ZZ^XEGJVonRKxtLB%j-IU5S^)QuAhCzVH-8lcS{C$GW1Qs4~2J9dLkl37*mbI8jh$#!#{R4trikB);LcE3ZtfCp}a$q7_x0xCPq-H}T=kQdPrfWrw3e zu$x95qm?Ctq+Rs1<#KmJyQfUe`=%PjH5FDft(c{9VwoDhS5u!pMZl9%mAyw!G~b!> zI1<^6t457d^Lq4nEMm3CF>tXJnfAUH_(A*;E2fsg#HDnD!+e{%KD`EP4B75+z7_jH zl=?$wjZDN+$B5hsu3iqc>{O| z=XD0O_}GSX*ZFD^+|=LBE2vV~;`#1>Zq6DG%b&zbP+%n)^0QFepA3cbLO;{8S0Kw zRkf1({zfasm@-w-Tmly#6*`*YEA~9Yf>g#U)7@3oX7;ur)nu_!)w7i}m-C=ey~F+Y z-yLcH5$&n9x|?vvDhtQZ47y8=8Jr{(9LEqahKQ}1>HMN)t_oRTW!%dOQ3)n)TgclN zzLzWxuBSatjuV6s{cJcMw1R=ZT5?du-`%yuqDELnY+t28n1sGQ9%4emdW<`MTDn#9 zhEt8FY(C3lbj_y$?<OIxqHw~f2+V8j)OvP8ev&W_Grj17aQu17^v%}b3 zkkEtpJSWyUTE{ztxWAf&lAKBWC4I8U&q_BCtVM!T4rVTET+h$umV?Gbosa#+`j#tf zU7N24*a$bTeGHRk1gB2Z=L8}xlNy2wHfYUVIpUerm~YtGk+i09J^H|G$$zc3$vv&E zF4I1^o1?sDYG7A#>H(zby@C;SHY7ls4x?5M_xvdCF64Ux=+ZoamhXc@#vY3v^!(m48+ z*)zO48d9BDAp1)6^$aPhB}cEu04D_FDfR=?lE#;e(PaCdE9hNK+6B?_UJB4QF9-8# z)A1+Pl=RBoqObvU${Ha|KafH#y6^fJ>@fC;Fc}lcZ%nJ?T*tlOez7oGe1=v|FzwYc zjV4T)+J+MX_0uN8e8i8Hums`{HGRIf%PiN-OG36poiXTZi!-`|!dJIE@T>;0J=lm> zmTZ(yJEw$3y<|Bud3cH*3y_Y!A2h@yDfk7f{&HJil2oOi~bd1 z_%9Dlw^(sJJHWRfsQORKBw7a&A2h8L``q!jN%aF7Bz$FfJWURx-9(@vvV|9K=Jr7c zS6V5dE@d4z5nJH+It!`VoJP}}84LlO&|pwM3?`M0UEKfeo9LJ{H{hY%U3xUttKn=~ zy1Jv)zS?01{;>TeV*Q5~)R8rojc|Hq zzf(Djv~7pqIDm3F6As6(csqlaw~Z}Ql{gHgT$E{+toRj+3@tH?IIzSyVb35r6#Cu* zYPT6hCe|o9w@NZ5!06}_D5(~)=?`MHmz|LB=&k*W<$U0`@>V6W`PqkDfvO<128x+~ zVU5#H+5PnT*?wa#&bffBj?85|7#yBB<%I4Psa;9*hp+6Ts6@-~t5B}Rb>ynZh*1xJ zH!df)I|jW$5;`XzjqX@+(u$J$#pqmZFxIp&*K`uk_f*xqMVZUhW0*L&vutjB-hBZf z)CO;FN^#tET-G&=n=_dnmt(~$1YMo%EetP)`hwV*_obQ*)S1P=S^TU%iW-9#uZ}C` z6;ox5U(A-`AZ6jD)5T+m=KN(c@|T>p!8>HdLFD5wSZpw1!&$zKii(qD=Z$ZPt6$ff zm}FgUEi5>0@77lO4>qKCzx1YFY3ZrdwV$4j`9CsWz;R)B$%G2}m8G^h*j~#_pAH;k zm=pWUon@T#WRxez2T8s{r9~}cdY{#yhNzllaxLHbfslzd0C)#Z^o03SdtT|II;zr9 z%qr@j-~$5=CA?k7>F2w^Xv%9RaUve}xUqPt24nJ*E$IGbPV^tXT3fk{YI96Y*L z&L*sg6GTgTe3Wh@mdW$;G{TMVgG6$TNKeEqjgxiXD1zLRA`$WIX0G@}CAf+4ZgYB_ z>sDxLl1l85gFil}u<@kzL|Uk@{#-7VT_hG;BH=Ts+dh@q+r&(3EW@bieBSXwHxp$F zvS!F}h<+>$#>Z>#vpXSl8zn&>(m@BEXvtSjY`sa~1^X$h%ax%9ku9iI5U!aiwZrjs z+rA%L(E(XnM|@rNWv?GOfQ?ZSY0jGiVPsTZZuqL70OR1~JZ2d*jryZoo%X(k3<8urrR{ku*% z(K1gG2*9iSI>`Ja`|DAwE$DRsY$nRnpbl#c4q(ziim;gj>JIGt-|7zV*PqoLnAG>_ z_Pe_fk|IDiCx?Sfc#B-vDI&C-J3D0;@#>QRNy|An`dgk#BLfGG+~6!2l7Ph@_B!?5 z#YqC+V6745RcZu-$+b#5Vk`}oqt%xF+J2(g!vUwKowC{-!JS$pQf{1@+2(htN7X z6*5gYk~_X^eTn$WAPr;@Y6~e7r+Z=mNag;m3@&}4;NM8)g#=Mv8IeX$RRu+BWi0W} z^7~^km~v%^f-U_O49Rm*!cI&PynqiM-VtxpCma<32jzza7-RlNp@yYq(bV))RX7;r zzEM?}Fw7H()Lk0T-5cO!Ov3wIo4+x4$cPE`nL4tu)ZUG^Cz?x^zpbmKY z?MI-p<|s}1$rB3!tmOe5a~N}fm-5Zod>{%5Of}m$47?qD~1NY-#d zX|EY|8i^^p-5EYI0JOk8#V~c&{WTu~G&vL}ce-Hu$uim)zLUWLn4|CZ2SN4^-?1ri zJ%Yei@s%+YBWA6UUvCgsXqf(&HM$|_#lFekHTrUx8>CMv@Ui2?T4Jt&eE{EKT>4i( z8^J2&M+1Rkm%38%D+r+L0zTyWs!C2=7>JTPLU2+>CcYJGCm6ab;0tJvLBnG#x?q4L z>eUe~jm@`5i;VrPm}^A$ z@;zC*@~4C7{{B=MTn!w_;YJJLOK@o(d7ie#q|JVKx?ABj@8gGo6Ipyu61QJ9Bg1+4 zFBL8Cm;~}7rJK%d^u27zlgUlLN%+jLz)lq4rsqSvcO6SlWb>93xM|d;KdrXn?;}|c zjKB1gBE&6wOUhc*--^vGyUwBA`HSpb0o@BH_okWm422gLz= zMCN0tvTu!2)1@L@J!+MI-AvDk^ufRp4jCH{ACue)YZE4Dor7NYq&Uk)^Oz`_ z+HioP&#K8nr=hcZ`6OXlMUGYDb&-0i+>2wNV(Dh-OYcQg+(eq)pomY-QHoGovNohIC zmAH=iwez4fZN#z?!DC zAzS2->`3u--W}&8>R0#vG9h2mQst!Pvd|qN`RLyxnxI z_Q7(2Qon3@-O6HNddUtWu-W34wUFc`()TVXJb$F}E9mqLP2)F!0M;TPXs7G#l=6S* zT1n@i`*Y2S0k`LaR@X{fQpAVW*keS_sMxCMIR~#r{%wHV4Du5=f@9yw;QT&h zrM!b>3q5%e?15Bf2L8`BwE3n%Vs|luLW`z2yaVkkWIWh5m#1?t zb|7+I11UAL&;jo9jsU>MND)?+>TYEw>LWu*Qt!Z&v73`fZs?A*AXN_Rddz$76NhYb z{;(e_Ps-uA(G6#aqC;D6wKB9fxKH5+6Bk#WvVGCQ>_Y4(bv9DOr0>HRh=Z;9#rJT^ zhRmgrkN;QE%)pA}kKxiO9{WZ)Lp6`J|bZWDQxmWvV9X;+FkmGMBs6!w>`DHuDpg&Vg@# z*&r`e196*iYuG{D=Fq|oCH3!6opYUQF6QIHTwMm2`V3JI=HyqF8%bsrtxrluMfGR^ zzov26)4KYVt(`;?HoX>Q6K(9)&e&dm<0_BMnsGPD592Tqng&Cr-S~y3JUK!R=vWwP3Zc^;70<5bV3sqvY$3nB0#U7DYP@npP zl79&D{gmW0+b@N7aZzzm-AWXyc+BL)el=jD3J(Ev&%RMZEbT-U*=yj4GvpTWOo%%? zCVxNK8JRp4v!8jR1^MZg*@~OMdMDOls$qKzxmf7~0a|bY}&AGMVn*Azf9;5g~0Za2a;tz>JDeX{&9#Z&dBIn&6 zL3|g`=IiEbFJ(ISB|Fj2kuHdn3cvfrLa{1#RcD1!=s+~v5+m)Q`5^BOxaSg3?EhMP z8z2MlX?R=azPlC98Ae+tC^moJH2S(#>upy+=}{_ZzyZE_5tHzFny@dKa*ItV4`Sso zYDQEYdE{p`%6I+}9_OjyPzwRd(6SK*|9mbguTVlRWY&V;oF}ADmgh z?)DVMS%iLmqF5zOQd@~bRb}dpo_>t>y_bJFQ-sxzOcd&8h~kXwD`!X{LmVj1*G;O< z5v{L;saOQir zuIxB$`rc5ZZooIF`l{*C{y_jlUv5TTo{z6fowYN;t*2V7N~2P<`{w zf2&`;4FVX>@!-WD!S5A-09-h$_-gD_9FBzoFTlU=5`2tw&hGbn`Oo0M>@$10UT)Vz^mOS}|D7je(M)IopQH{z_y4Ic@X_AO7jD zRjD^v_oyRt>Xp;T?x|~OHF`A0H0(wH>93hekS)g#i}>MvCHQL2|9yKdUuUrXB{2Ls zn;#R~PvuCvV`kJSay<)-B0Pha0UOaj;N{mq2Ob3kUIsew$ewp}{}CCosuP;x*{~_P zFZzzYHkzH;>!B-H2ED?^gfAHKdVtzUD;(C7J83ACTCqqXIvEOOcz7Rj9+gT8&{>e z>^kl~`di0qJ6R*5$a1jHRe+XOh?%z{s-ehSvU# zN!N@^Yep+ckk^$pZ(2(b)D!Iy{(5Y_dd-1DS2CjZ&TWmWDNc(^?UmN~_%b^_Ql-6# z8qRS`CEQz9H_iF&i?J`#t;5VRj<@e%iJ;D4P1eSnSR)oRrRQLl=W)#WdAOc!8W1;- zhqayDn;VzbI-{TnUWgkI57tH3`wsoToZ8@l9{{Qu1@INb_yO*LkNV%oz~>t=5@fdUB}j2@sew|o|) zDV(cuPrboo*pWsk%D3-fo_KJ^e0b{6?kRA(puQFAx%PqGa8pim3&s^FA|s~vVa5~0YT^qm zy2ou17;{8}MEcZQ+_mFtIrXPh01w8#b&cxjErW&q1hR4-0ai}tjK1!faV$RSo>I;nW2XotOd1=g zoWbJwfmeoK*a9@l4Q#9s1&cg`%BmlGG#u9c*{O9G-GH6C`=}cvhdnogR9*+L1I-{c zaMoA^%F-;Xyn8TZtCM$VpL4gTtk~UA$b=8KqHAX!vdKkgOsxbDs7iig?SEeT1+4&%khP)GE%R8_`?Mb04Ta`0g~Q+?LvjQ7 zs>iGR9g!4cvmMRXWj<4Eh-JkTEKswsv516DdQ1jw_(npJvH00_^^Ms~b_zshW(hxr z!6{6a!4?HWp+nZYTc+AtP*gdwzKyX)<8_AIrv?pgMYm< zO@AXn&x>rIw*s$8{KZ}@p!1?haF4amcMR9#4}@2WeS%=lJ#vp4{OmgN4sK`DyH)e% z`REgEqe~G49u0Uf!vUmdLYBBfX;dRT&YS{;H+17Tj`+ zFvsYHyoihKXAQD%d)LvSy`y9-S4pvJQ*qFwP2qfCwnlBP1_OvF0~mzA zaBNM0X*3GR9eSY3`0D- z2(@)GhI9|%9K!Gc%a-Uj8#|v0kfofv`U`lMywgXh3FwL@Pwo2p$yrYpg-mvwG~mtq z1hx~6LQx`45%6BGsm#uNjLSdNk+(JOKSVTPI%t>o{N?@G|6M|m+-uR#{&X~AZ>@~U z)IwwbDsCq_t=LJACOxisT#=|qx4v5|BdUmz4-c+x7Y$YVF2}}6?~TqBRIjBqWG#*H z;y5>)HHyS3&J-}J$>LC;5~#uO=8OL~_TDlou0&neO^^^gcyM=uI|O%km*Bx&gCr0j z1b2tv?(V^Z2X}XOw>yh;_e!tTd!K#pxaa)2chnd~b%oF)jH;S*e&72(54F>)nJsZm zqwE6758qM_^2QZEYlKQw8DiEkuSjOwgeZG33bWRZuVJ#S1iFD|h2Z6-XWSQtFF2Iu zyC-)O;PyF~)|PpY|2y6;#*dQ+p*@kX8^);MISQ$HV`VCm`xAn(*p*>a?QW zvidFk_u3Ebm);-1Rs*(Qe6Hd@_<~Vm(o0n3=CeUzT7qmZX`xn4X~E-$}9_}W2Wqj1NF)*(tQ07ED_4& z9>9&ZMYAfF=`+U-o58gxyLc@+ZVTc@FYhheG_!4!;OjHrG9~mputne29!u8%+-RCA zj^tsENOoLo85p_mLY^RN3(PPfhswT}qukE&KswyH$4V&{MqBeWK5G3kcJ zxIeCXmIKc4QPO=B7LiH6MN7?cWo~^u%jOt^nmWoZFo~P$BW>_qW$ZapbwqgwAJqs_ zrmcPEA+qx#vQu={URs@9S)~Fg(=hgr34T|m@uX@s-QK)UXVAWiw`x1xgz)2a&JHoD zZxDIK^lSu_8jk0x=_nXvp(}aU+4j2UofOLtk=WD>`dNNV&f(}w;nyzl>78&fbGkMv zuf$G37@c+NVUYKNfP_AaCCeE!)=hYun+E-8RS5cD-6D$?r|-CvSIp`KksS@3``g zQSfv#T87*w;j*gOwb_f4hnjk@NydnC_kktPtxxu*{+@h-~HLik^(weCb)nct<$j*TY+6x*4jl; z^GE60o0{(?;Sn8H`y<(&(wT$rAjkPA!r@3cV~fgmFcvx%+dAX?qNDuI=g4=YS(QJ* zzCp7q{$eVMulfVV1xPuMFu_p&jDD07RT(TIj`(olY$K%`p}*)@$;a>fL}|Lj*nz1l*DrNn}ZdfWk9{C{QWmNP=TkgRLWm>^E8#9nqi?GPhOX~zh18-zqSYlL;^uJopPxNP? z6V}pIRD*aQmbDBN z=ssrZ;DJ=_j%rc3-ZdB9ngEg6YF4mDxT^4Z!yBh1NmnsT(4}g=5#Zy3dbHEPk;p%AV0$?Hl>G{se?pZYVH8VdE!9R zKTv1Pb|%|QeIGS@$8eY#_}1?t-$kluo`K_EShM`=-=owA1}xP+eZ&NK(+g=C7~|zL zq5OOOjMygc2^criR;4=^ZboY`XdAX|9Gy;%3-^wqIO8mb#9svkCpw1m(fxOCIJFtgq*`41hh`dr09U3mwT@!DHXW(TY zf=2j~&{V&Bt>??_B`KT&t=!$8Q?hJ2w~rYb-4-P=humHdC!NcE!^3?Tx55+R3$F4P zGMtQ$&KpCDD5QrtJYJp0GjoU@ea_u|lU8juDi>IeGg4f26Dho2=jA(9-A;BT%2ntP z2ai=R&yCDHm`IqtgGMK(sNs03tgfbz1?SkjWS+MW`vo!cVR8A!O<|fKxrg6Hi(h*- zyoN$dsd@ywO!v+=h=icE-p9Fw)#3ZC?qXYHKl{yu2YdQv786~tn?$zz1Z`oG;0h?> zcrLB>I;P@?Rv6;Fy*;eMA2qhzV8cV!$hjdIaKdLf)ONdz>koELvd8oxHp~op^39Jf zD;x)-+Gg2a=m@@$C<)vTpGaoo&k*r3=T=e3(zw2!0bR%E_`5BHY9tI8i4jhklA@FIB0-A8a~Zu4fT)=H6am>&8IVCLVI zhws+~bAS0k(XA{3a;XNGohP0Y1jM@iuhWx&n=mklhS6SYa}!Y!nm zTV7bsFO0D#Ky6A=@DCtO)~#ob=IZx7fFW+hS?)U)#u)}mW0YM1@$uQ$#t zg)z2U%=XmzHQ)5Z<)7OIR^@Tf4mMSCTg(aN@G zK?}FuX##uDfEwU@?D8F_vu?yhiC8^Q02s+phMr8{tZ}wk#W7_g@Z*ocTw9;864-)i z?>&6(R6gRo)tJPMTAHV>qNA4}Sz&>DV)sx;tl9W0+vfFzU9r+m$h23O zf)Tu!yH6+6JO)ecHJ_5>kOgo8htUr@fopzeu~yHbY{4{Kn*Vc*kC+i-bFmFpb{fNH zL+dVZH;wD|4fKFVVEp?-&a&`hlAC$82Oir4jEbCHZ@U;}+ri#M&rZHq{)2KNaLcB0 z%i=rfZ0X493N<2d@}_d~;@vfdFBR_i^N+>A-7lb0f01wQXpE(5?y}8=#GzFNI{@~L z3cB&+OHi4(>p|qe#PDDaxuo86(0^?;L}=>QT$-0ralOD9sUYwe_V&FAuX&uVK6F-U zkWHhknk9C{ML{H;q;w0G$)4o|%0)5KAT$yMafibf&4Kx5-V84nRaJ7boQ@`x-kq?i zjI~nJ$+(*|y+|Oknj9CbNEd~h(SM88{iUEww%wp3OE7Q9*ymIJW62M*XRvdfDXgDi ztvovD%eg;n;`>)Y3mMY~ejW=>S9tY<6g;ZYP}))b^6I-4;n=unfy-l?eGCR`tugLylbIf zCl!{huF@!Cn8Bcg({;1*t}^3aeSxMT@7X}jSnIt*lWddGY6N6w#yzNdD;pYOH7!wW zV&67dK(d(Cm~%bjIzc*y1*la=&b~$&ICcOxO#E zpdU~{>(CIQj$;(3i;XZo(BE;vB-zgjTyIjknW8{G;)Qs;ef0FSyJ*=yYp0sLyVx`R zq)?(9?Pht`d#anwIj~S6@s_G<3s@M7PgB^7f9WE-rOYh+*7mb1O-2Y=;)v`WBrgSL z+6MB`B4(h5JUJ-Ju1!Oxe$kj3`LX{ny>qY$Md(#POMnAzXq=+{o>ohB$$m!c(1lKr z&ddifUX(TLv7Ik^5Hj5ZmhLW>No zg{IL2Tj?~unKKSTJ+A|(=ZRmaXETMZv0p$S44H@ms>Vwhyzcu7%#2UoOmrk?@G7`t z027F|>^oPoX*>#c671|(Zx)VeeM~KX!5Q8CsXKX(lvdNbd~jM?BzF#V8K1FD!w(FM zRUGQqzJy*!OU?0BJc;>>pYwhO?26!b-_F%!7~Xb&KoU`Irj6IiJ3?;%YEl9LX_`>o zAJsHyZs*aiZBQ16j~c* z>ZT;>M$FrWUEM^htb;1+&|tMulOXi2BuzF_BvY@-_N`5mVs#mz3@{{RVlw~E>DBpP z#5^m8{T4fh4b2Ub8Hf3`!##p8loDX!DBDUKtzLN#t*uQ4H;)T62o`qEr4A|Gsb8&D z)-+A#5yg2po3KJtf>Uv`Chp@T4B`x6r~mJeZpf8a7Vm8J;!aJ&D23|VC%JYKxW<^8 zlJPtLUMHw#%4xaoFZqSIiLq5@`D1Ge3v5k8j3jp0w!^0_@MYS<<_3vsG8Z<7zF@!$ zDJ*-g(92Xur|i*1r)ox*Z?8!^o{9hBJpZ;dy|1<6q8@8WYi5IfP{GQh)vZ!+kG<9O zw#ycg`%ny^m&Q`=^@CAur{Y9~h@Id3X9_@~=Q#+z1cPFVI5~uEW23hV1lojR>E&hi zGJZ?5q2}x|I0NELQR6>1HVZivu<*Z8n6|hPLCb#~L2svHog}`O0FUDZ%**K!?>{qb zhT>SdzN}y-wprvjct(U#!_7w;1kG?%|D39XN%hQKcl{nd}!Z%C;W-7a;a1tRsF`sGV4;iIsFc!?>`^Qz4 zckcUosVGlsvD8~=KFDdU$?v?ZNY7l=2)K~&o!BLn*e32JBs}p;a$S6L!+K<@x-SJrtu&uu(wo&*fd9;l*i!`Q`-W$d1Hh{pD;6-KAQzA zEDL)DzpTMah{}r2O1ru2 zPukoZh&Fe&^+cO(EngJ|jI_vR$1~7V&K?FuG^x^PiH$?oDCE8v&+g&Cu+j2QS z(6XwX!mRl|21)Fxk&G*fyr!#&t`tcC6Nco7IUaRG54xVRWR#wL817P`1DPbi);m`S5Sk{s|j$FK_n?}awDKny_*5l z&S_zR+BvZH1oEW6N(S(|e>y{fXP+)Wf#(4}2~Yz7Y|wv-Ye7-(6jUH?vjibB=_(C; zD<0$#1%Y2)j1HtM4A8$pL|VDS4fGKj}fQc;*^<2oKL%t7A-UX@|Q)h1M@wlR~Ct4JLg6TR=COa za~lYH+eX60==se~ZSLYuRnn5E1z*{5{lGyw3M@cTtzeBz%i+zu{@+^%Fa7`CI!OAS zZ3kAgUe1~HjgPDPeDfQ@@nD6vGtjSNSpNHR)XCmGG}>9C*1=G;GObP&8^&pS$ie;Im7r<*i)`H) zqrghT&xsb;{cUppx$_f#U3lw#pWpLfiCG=vnho$)v-Pv-e!U8KUs0Zt&5M5jnQXR; z`&&llP%(-Ih=u)CE|c?k{RfYapbw1T*%zUdy15mue;oi^ z#6u`g^~8SRWXTX1ZT}r7yM+3WIN1WRy}JBO+sLUBR(4q$0N&qR7z={;&#jOD7G~b~ zE6m)-{uE|DP58$!GmVNj*k57hY?431%rGeb7H0mVD5eU6^^5nR#rKQLDKOL;+))J- zTaFfR=MR5yLbKYv2>qB&T}e|V)92VfUps1NIxLKim-noUqyF{6JHjla5`iVdZ4~mr z*OTJ@qGS`z`ZhX82EHa>Zo{bNUxJn_qBN7cLfr%qqy z&%keL12km^wh7SQhS*`VF?mN2;x9)x%!p9w+yig16mU6kP$NIWfxj5pe5ECr<=-p9 zC!MlHN?S1ad`UI(;l?{a zxB&+UH)hzip_tfy2{)jX@IL%E9nZSH=HKEBSv-ICha&8s`a?O&|J)xU^aV|QfAxp% z#s26IL6iP_e~9ZvXQ(mp05JpXy$=HuX5D;j$<2=Pv2Xbgd9Be?HN>cbV+oz_)GFz1 z)YGu!74f&zs}%GCFdHa+*=LBbH=dedc2eCX;7>LOW7H`dl&M zMHm14)4!%{FV#7^*7A6hpv&-RVuMs~8t(iLAul1iP)6Zjn)}Ql(P>@pc8+C)Hm6zf zkn%UT;`6Ra@{HGt4lPBl@xpM^0~laT78UU~Gq!8CUDh04s?6p};dPG_jUXfu%UgTf zjE<18m7Y=l>oN5=?7CD;bfu;8ohs@22`2+KWss=3r5q+p8nY8s9h;&s(v~-U*m2dz zy=Iz*D+%!Lidkrha(9v%C!XicH=~<}+A868pX?~V9>hb~{17Q~LMT$fn-Oo}saO}< zu;Acze&4O2**U1Uw?c5{EC|V9I=GC6H%KOQ98xw7(C6}a>U>ag>)8hY zR^O|ZH1vy=J*MQdqe4~_3W0H&%V4xba1wTIp>)kMNj|U&?Ve=0$_uz&`=fkN{2VAB zus!OY5RsoDh2NrtC)RI);V+`&tJV=K6t=t%h5E#Z>nsSWcz%=Qzr0@CnV%Fz3EplS zazKgTC8r1Y7>?Yd^DN$R51|jib3;Go>PU4Qi(OqGsnEM#zc5JQXRW`3@Kta4VdGHT zNGCAdgZgG;bL~N@+8Zx2l#ydtFZoA=rmupzVUyH&Idyr*l%M2OV3iz!Ira%lkL7T_ zj*AR@UtKMtz1L$Srq_mw$pHSS9L4TX{3Rr2Mc@!;xkFLiDB5_qB$}mdWv$XHrlFI& zwC9nV2z`~KbGsgnMx`^xb`Qy8LO3b8!d|J@#h4Q2w)HkzT&W)-*~D^1sp&1^))M``&s3G(r9N$m`)4uKCFQkJJx$OLR3bYK}e^8(c zH-4R$3&Lz}Z=YKM6zH#w>*!Zc6lm=kP_hxT@INTfv|Y@FnaqWo|3-nX75Iw+?O1%r z#`$*&^qw?8f#!&H?JSTgesN;z^;SSqCP87m3P;oEGmC?JboKWV!@-!Ora~~?gHwaD z)}X+$eL~rc)=f-^!ck;&@cO{UrJ+W!2y<8j{_Ihn=|Z zq>=4J4|G;msPW^k$&+LCGu5 z+YSF0CzKU?{tyP%JzW>Ce#52-UOT^?rv92>%E#MQMKO^}}jT__Tx(L9C1d z;s#&Splrq{jmW_P6Ej<>8U057nM`iWbQ^=S^zy|3nQMI&+t!SA#`59?#pOO}q&PI@ zYG^L_>R!ay4;F>!Fp~sZbO@E#h@U}gQ7!~vE$VII#FeD0o`Bi1(KVo^ zTMTwP+*O&jX}a``MJ`z+H78;ISktD7eL8_M8v+ke<2!rNZK*6d-e$?QSG^>n(F?v! z0RVK5V&o@&6BvLEya8GXze_=q&%lxRAYLK$aXAmrle|L%K#d?N$UQi~mU{N8jC*>w z=Fhpq@BLSD*}Ip(Zq5Tj#4eQiT5hR7B?5NH4uduPPNZn}#3druRufB*@3>EPuQn}* zv^W|@jCbX=&0pBPe^p{%JssiveGlLN-4o(~d#C2m3K$K>sAl7(g0Pt=JRL8~o|AvG z{OpHWr9^p>G%O<=in-SyT5pjWXZ3wLts(PP+`n9;#3~9+lmVT|=jS~bHIMuVJ{A4G zgr%rU)CyVSbS1#|`qe*xiT}NSKnaphf%*rz|3&{m86G$uKC}P5f1m>S6_`l;P<}oQ zmRe;bA~s<5HkiLheg<^j7aG0dX84sJ#N}XuFQ^p;L+bhh8lr(SA|f2%jy_%*+rEoz z@X0C<<=xNPSd<*#)iIqXxReo7*-gU53r@w)F>2$<9#KnXs1C=(&bU{-9YTON5DH0| zomlSc2{hMFnAmMM(8tABFCjl;JGtXJoQki@t~t$6dpMa86Qh|CUG2)rFypenN#ymSC$oQ?AXvZ+3@Uh0guVq4BWuYo zoZVh_cCeT!YFy#L4lQWM6*w2+mdZ=4!~9BRZN96+wkIKU|JOimXnWRgKl1x6so?|K z=Z3CxBCUrW;!5JQ7Hci3x2f<~`F(8Tmxh){o-$h%&hqoT+1edoC;-;=e>joWDC(IM zAddD&9F|MRTl_xp#v>8=8W-Jn;EA6$w)%^o7Ha+7itCDr$CFp|1P3^slM6)zif$dM zBDKZOfeU~0zb^TKKX~Fr0$+h^E8rd&ul@BH$VX>R%H(!Ky|cikuN-gK4d5PI-PNby zZ=&re6(M&XTLlL~(#tE@=U1gpi=0~TbGu_>&j#5nOc8z+ zoz!|VZM^E-14XDwRIVwEfuAcm9}EIXe*%2OAds}a(;pz|INT?Ybe#5|AnCHNZYJ(b z^DV!Dq(cpZbf@SKOsZ~3WB9VZHl@x8UEE9Eem)M5OZy7rNEAWvU}RcY(uMhfiwa)k zsgJ-76$9p7JLsxzqJQ44e#j2m|2Xd@F#W)y-FcCnOMYn^-VyBhSr@9&Tmv5_=$z-@NHwcS9y zP-}1ymyHJ8=9GWUSph}RW#!dB^$@5*AP#d7h@+H}l_Bf6&;cqu(9~RZ8z#oid-Yrp z2rl`G#S=*HzCVUV4=%>)_$Qz=W>xAx1En!S{{txPTJ<|n8Ze~&kATt|t0Vl)pI*yy zj2k^u&(F5fOL(O-^Z6_@k?Sm)wK4l(z2m}h_RRI*GYN<9WS!wA){di`XpY9fN0|#H zcWG~@%e=SGaGM`(-ia%{IxIUu*wr4~Jh2bT5Blme!%4V)irjJw^#1BR`q9qW$<-=3gpyjWwfq?GsT?5#Lzq$qwK-XYEj-0uzJbkeH zCq@dod^6I1Af?M&{28b_sD+w{YQQX+7p>FS(w8Eh%(X8>^Cdr)Evl zk18iNv$5WmEN^qX*v2>ebuLw_=h;)XJQhp^UBA+jDR4zl?<#V_lGkfSTA49N2mTtS zS<0@p>m$__PQ;pU+Ns}^%Loo&no3_X$6>vXnAIj-{>H^d8DFIsyzL=5W*KGWxqHGg zFHVwD;ahiaj=3`Yq%y#??4%!2F-Wvwwh1D>DCoLwL4~Y%urv0^m)c?2-ou z(j8}M(r!7K%>}dkP^JlO_&+B+Lv4|$Hd9Y`Jlvk<>bjCQRXJdKXM`0X%7Us!QfV{6 z+|EQSjgVX+aX4i^ZwW(_kG)ZNiC=m z@G^ztQ;DM$4_f#u0;|QYEhR=X)szE^Fd1LPEcl;!M_4&}oM;fJRUgyou#^=2k9sI{ z`z**(REim|g#c{d!>g|lYb?)6O<3Y5O+Y*K_~BVe48nilW|v(`Rb$H^yE6zy0cq>EGV&9L?p3(nlJi6)f9;c^VYBY7gMJRW7z4OmXkfE1Xs;B7X}PMGdwnoA5z0%qt6Zkzp>>0iKkOGH!6otk61mbow<86Jgu0Dn9AN-!R(sy49b_G~mTmw*=)V9YsV3=q}v86(5HWnkPV$I~1{?1k>SW=FOX zt0i3a`;3ijRW!8hAzn*3oW(ReE`%qc7$M3`IqCvqT``SW?DDgwKUHF-I4?6XMo3+7 zJ1B&tgfo#&;GwW4pm-ueiL>0gEuh#!pGr*)9ojoJiw*tQHc&@E!aZi@#w73U-AeeV z9{&x3^?vBzX5&U{fZpxL;UAT)*yuc#%6?yZ(~YXf%S`J9>A0v;$HWK3@TC=QYf z{yXX0udf@P10z*6dUiYEl zCxM|&yiXs_zY7d01_0F>_SL@&3>|R$=Lp>G-h*W8sX@bX$wgRrCbz_rR#*Mw zO#Wk|CWk&W?e*fM?7!4BQ?WN+oW3||BJF<`8?^ll^$spc#S!~mkoh-0#;`eE644)y z0-GUPNFVfkQ3szwmzPNW3g^VXLYGFQQCm!qq4bA2$fPe+jX#eSP8_~Q=jMeX{$L@fZO#E>qj-}nIh1L1KARVa;{&VIgL zzR}~Y8?s-v9sPhhgIl$&sVmvL z_Hy@&OWr)at_juBiV&91Ojx1uqK(+)e>r`-i)=Nu^h}u(#XWk;6@n4(x88 zXE#|aZVBAjRT>XR%WH8#|C15|p27d)5&|UN>(ZScL0AQuw0rC)?5j0NEz}7t`9J2o z%yq&6{6|7cU9G&nsM+z__BZ#PLiD5(iFgdPsa$<(d48E|TFa)b|K45G*{yu=1$CEhW|jY& zRQJ3bFna^vQuyidM5i4VGN{L5p8vcy1P$kP0Aw2-oP!CnA2Ym&FPRvftpZe&&2<)9 zzWOrwwZ@r*1=I5K>s9+O3Z+pW2GUnD3I{@S*6}WCDQ3O@QdRo1cZN(^EEZWGwcFQi z3p0ad-OyF)y0!>Kc!E;2&LfMTY65mBPfuj?@M+FUvdIh%ug^A z4|$O**z*Ck6iwVRc?#}+Q$EisDa?cqO(n%}TC-AvEs+UZttg+i{wwjCuOJsx9v8-r z1TRPKM*Re&DMzdA8Eq78#Y-xbOcR7xC7~l*i~=|vH|g&jbnoWs9s~RDB}0=&bS>>4 zzAc^o2mhn^%`e2PmDn=(+olC+OG3f$nmI6#8>XR3GVz8boYMzQ2|P`RF={Ii93$X= zSVSlojD8gP$07n5=Ti~ER4a;#xzr9)Fm%l2#Scs&V;FTb0(+d;L)~O__efb8I$xA7 z2O18`46N?DXw$8JMoWIqw)N&?E7Pv8MA+Rq<=$LMw6jWF)hY6Qf&07l0z(wGOImUW z)*(jz=Wj5APAdYFf=+sbl*wC1jW1g2zd1WOI=Z5b!+8J%*PeMyy!ly(uFv~1mtJ41 zWsR7e_z~PmO|-+?bi@@lM}HdSEk>i-Z5!;m4Zt$innTZMDO54H-za+cZLG{+8^k>gLBlT<9Zhuo4zto6SE#a?MQC_a8y<7Q#|Z<_3>%H2 zSbjT{@!7-vZ3w|5OnwhsDm@qKI2qJtf`eA;{9}*w_zW1jJrkmly|{S;RJP$zk!{gA zSg8Q$+%t^r8Pnw+6kdRoA%g!2uvtj+pt4uwthH6l`AUq&x#dN)ryE{SQ zvf45YFwX)T31xqQBcZ|cN`LB^{_7#Ma}EYUJBckih^Ky?-ZGcQf3;c?NA^gns3+>u zH-1$=X6%R!)2$Z^Z9<}uf-h2ZOj%n9gG5bH25&=#vf2gLTdBbsmTNpg?L6pGmfj6_ zVM{TuDka4hR^!51ysuu@2G5#E-X+KTDbEpodTAg8g5f@ohl08c*?#;`!Cu-k->NX(oqC~gdU(%!u~5Zk zrL~O?u-61uF{UCdu11{pI;8;M;WqFWJ+eM^%;uagbLF2jR)to|mHL0dosZeZ zyAA{;7bxEL@_2gIIy1Ui9pY9%&vI214_@e~i3TMn1JXCgB-I#e;$Uu3@#g*c`ZHVQ zOiUWH#8!=4gCdF^sEYlx>2#9Cj{{EBZSKrmq7P5Ay9m(iZp~Sww(Mzkcl#@@Gjbvq z0h--CeFe?#BA#Y<5l?fx2t;6Z7j-EE{LH7>T}Dr%4NW1bQTtgs7ZcXEyvP_?|f^vF-WobI6I#V!t1t6dvYw)h|sZqR=gFi2R7G!zN z>;*be)Bp9V1dMOLs0X>ukf8BHuJgDj_o>0V!FD1xy~7~ujk6Nlt4&|(7dd<54M)gL z$ANKO_mwIwLddmBb(g1ILdq%<7-Un=`}txyl~?pPAB;G@=my&pQqoX%EV6iW7)0@9 ze@ z0EEp>%cxLN$b}H*x$39^dWh{nEK%Y>SC@=iKTNL+WlukL3)xz1EM7tRLlvQl?|0FQ zOK;`%?-C0wHp%bmx?+07%&xC{BvfkV(jBDR9g*s?;3t)3Fyb5Uj%MgBKhW-QourPB zlUHe-JWPFi`Gs1c3oA6(cXtjOY7POBY+(%jt1d52Qt*S@m5H4jD@^u8{B8=s4w>NP z;=((AES~p~g$j=vt-$a%F){HcR@QXo1uP^!FkGfTRlE$C72mKpKb-a_CVVPKFF_w1 zlR5iR{c&f;28wV#3+wg^e4~Ws_S&89N*b)}D@?kkizwR55;ArTMf;f*iX zaed{4ldG?)lOxykl)f?Kpl-(YMG9JxcN`0J!ncU%*-6|RplevlzEof>sOmfct2vB} zBMv=k2gA-C-CH$Lu*!^tKwO$!(`vzEoAQW!5tcIU z;fA1ABk8hYI5#+ZX|Xb>Oze`YjNUC`fpA_|>T1kmjXC;Olh!PV>kg_V`Kgs(DLDC$ zRS3Nrtr^ciZ!K?8z3;}qr{FT4fl_et00IsH{;#(cplt(EaHg)n^Pm(Q1#kp{a6tu8 zdbnXgDL6bxAO%MNSVbD4o`!ZVs7vh*GzUxa-$8Gv_sIj_B0J!hEoZ$D%pluhmL1EM_6~jt9}}A9Y;inWt-^W0^!WaG zclW1orCdMT_x^<%*Ee(fJH`@|IUaD*#(h;6fllbynReUm>-#5Jd_1cQWfBhw+HQ;* zdZf2FgmhX3oZFPbkNulza7dOnR#ayY+)hpHm_CRoFN2u{ zws`I_gV|KY&zYDUpG?#i_APqBVq|;ezO^XOO6tV$yp$vf5qmjN`dm>#{7tOz2VJ8{ zei?ju+*(>fEY8uObH|Pq2Zub8fXW`)@Ufp5^(xWg!L*anbT!pf8)vNLq=i4WO?eHR zEkx&n>EYkxkfR;v%~DIql-xdl%@JohxJ{T|Fs(S=rLJx5H_1Bv(GZw-8Z&wOvHTjKodeYnC5IMVsX#dE{Pt>iw5q zv>yoZ*t+e7U2{64G<(0WF*~~o0^V(|8Vu_L>Quw@EhNB5ccJ|rpO*tTq^QN~72TJq zgax!)e0X0?fP!B-Z6yqkmXMsmP6(_)KT6^JI;2qeLxax5{(9~6ks&<2_Wv@}0k8cZ z9l+TbnBfZW+D~hA28(C^SZ4YCOG)|;;qJjF?yvyo;pj?Mz9T*GBRv9??imCv)Z%2v zx+Z+m;i+PKjb`eNiv<^-H6lGuY*}MPSswxv|33~VPu{yDJWx#TPq7%_#Ct+d7b^Yy zLo6nr)%h&mrQo<ER3?Imjj3F~^M-k1EH&wCUa&U~cPnS9Mu_tN$yqULiQ z3&Sh;lg-l%m{3ehR=W7+3AAyW9N@!b7oCIZ_){AcHZ&+hCd1=-$$nFdHRvn0^%m-oI_nVJxuD>t zc8&9r^%v>MZ)*>jDo?;zNs7N1Pa*!`sd9!uO(xJJK-fmNeZxEiJHqWFN*+(NCg6`v z@b%|A&fZOH&P~^NHFXWBgu>=B&autm+#Y24Ru`G+c}wtRSJ9Qd#4iDeeg}Q?$_nopm*Ca>iFHb&?YqyN*#tr5c0$8DcG@NtVwE9sgW{1U`8XM_SiXs?rz z@@>Iz`MPI3VM?FTJPWMkwuuLp?m`-Pwf&%1n$`x$woGdeTAIXi0O$G?hsocK5J2ZK zD<3kG*YPIbAZl%BJCGK^zjB{?=Ki|2zpNS@!`Ga=w!hT~!5Ye%&Bhvv*8UlIW#I20 zyTH$VlGIX<$Nusosz#|Xd^cLiM}(o?$-;%d6hgB0Su^)?xzO+J>cU_s&?VKCDa_5S zpP9N+?Pcjg^w)3pS^;MpYy?T`^q4kg`m1m@LR;L=!R5uCq;>6oNbB!ynSR-h#H7 zAK@{+uN)LdgB4lZ$i&WNbzgh}(qS>5&_tr+t5x@AY_)kReQ|J<#D~E{`5mdHGD$EF76v#Y$tWatV!8id|x;6rEm zc9&!I#rfA`Q!!+>)c}t3`1d~Z3~XxG`<+#N_#Pex^^=r_zVH+DyHuy|$9ffO2v34% zzc6jSSPgy6e-cEApr-7nOTyj4Yb%&8jmF9jSnG;HC)vGXnDCb+$Z}(0R81Kkx=P}w z=`7bGq^Go^6#8FJN)YlF^W8-a)nL>~W9+fz7Jbfok!VKdRajmWeHi;S>4$=l(udNQ zj?+}(kcoHE8PdxJRBjq=W*m5Mv8yrk1j?)Ywbs@7f*I6ergAdJi2-B|@?R;%s86of z*BeSg{Wq2*dDl?cn#bQ2Io~NzsCgGg_nv_)kS%-NRZd? ze~%t%M7LM*|B^jJq7jLuaiSbCb0@5>Z1yU^F}$x79Mx-Nv{>R@uD__1`us(a!>Lvj zqlRgcfa_otnOOc!yK!2;%M7#Xaj~BK97B2>-RK9Qhjc2JX2T*S&!EQx+Jv(_qhX6XyUB0=X)RD<>4uLln2?C%FirCjQJW=FnLoQf;7CiU`22%VFqXL{S%cO(A5 z|Bp3Hn-gpHn&FQ1iV3jkNrG;>O_GAgEH0p1m7XgL@HDm$_AD|R@YDCX48jQ|xU>$! z2V9qGC+)27m!|_Mv9YZOc|GghAH9?h1`2zmX!@sm(5jhaF7JsRio#Croft0gwagzw zg})!PoO->Sa;Y@G+MB=$JrLz}3XbKccP_r-ymf` zuO?>+A7Z7}V!t-oOMW~Vxx1LrtZlvOX+hy%VOz0&GK#Kd3|AO9nT4cu4fz$t00O%J ze?=q)FMtSiBCs=AHcdWg)mTAX=-H(;>veddO0?7=dw(DmcmB?n_YZ;HTN9k1lGmwz z9gIX%Us$Jb?R}ybb+j7ph9~E+O5#&3kNi2u^7}I=xk4i=IkJ&CDZ@(!oSj17*w>H; zbzHF+q_c>wWW4wA;gg-O>+h~XszJ#qi!_l!+Q@D53_r@p>-0h@!YE|7(Xr8i=b|%0 zBwWQ<5uu&_Jb>xNW}pSRQb%+=`Tzo*3)>iTwGh$Q3?~$Q!LhtBd~ zCGe;%!S&X+Eg0DQZnncUmYAOq$wW=)kDh|Hw6=~q6XH%$v^Pe8$V**@_qzN|7mygwfm>NLt-x{fhZgRx7fT3p zU^O!~x4#Y#80^9dV@V>{a`M{W6S*eSwK|HI7vrigrSC%Kho~AL7@0jYWH7-uH)qXBq32V{M(_tm(5&;*42jU3mc<9kL2lAf`<=qQq?G)JoxU?0H8E7%J@4c@_%uV%-DTgN3$)ZenG;Eq-RYzy9VUgd0TF zTc+>}s-L>uNl9!O;MQ;F1(3C_=7SKHmDRIvq=h6+g}JB-?5o5qVV#5(_lulPfSxlG zg6z0;^6g`J-Q|K&->Ku9y>U!a_1)Uk_~L7EY>I>tBM2)}Cz4K;&s#v0v&Pqe)>c9v&>W{r?8P+_Vq$;Qy~ki8I%?XT>N z4b49kotX@`;rCKp#ENz=aa)#YL}$C#KWh(d8H2oQve_t%feV0Eb7cyOk7>5}N)Gd# zSVhD5O4c z#jo{pgmH2>;|Iic@lRsABF%f$bh}aVGPJ(0Dz@?F$d%W<(V^Xm5c@Y`fuUMv2o63e z1l;kRlP*|1{9>;a^{AAf$JRY>7z<2McX8Bi#rBar&CjX@piC`EOW|oIGpeyS6~cy_22dZQoA9S(+5TO zCb~lE6>JpS!THDSng|VwJU*pCZOwrHi?g$gs(R7aH6bV=C?NvU-AGDHhje#$cZYy< zmvnbGh;(;%cQ;7a{R6JGU1#re?zwk32F#)27n#i8eB*il7`3CQc^b8oEg~3ytGN7q z3}*LkKwmLOuiXWmoeAY|p+QU~A+T6^-nYzZIP1>$ zO2K@)7r8psaK2zdW*BrI)(RWQr~{Q=j)UFPmiNp1JRH|GDmK;)ZN2`YXh?MtoH7xZ z*^rU`z7PQnvM|*%F4b}R1~nYiQ1G~L{e@vCPi>6!mgsHc-TPw@?+UDD<@VMeTwT?+ zU)Gv{iIZRF(#qc>txdYZd|&U@SDnp{)zW021(p|`Sg12%_@1GBZ+Cv!lx%13iyhps zOJ%qS#^UvAB|VYq)Y+h?wJj3eC%d`4T3+bhs;Rf@BQ!qj`^h}(q=E6#)>d<7W8A`h z+ax2*enEG%y>b=W$OTq7e$?RB=`F~c^k(N+5F6anB@Sh`2VeuQ-pv%)z^i4ESL|3_ zJP&`f*Rm`Q=P+yz-qMxCr?idpLTa6(~9i= z+6q+A>zvsB_RyBZrbVQej>7N5&!qRA|G3z#k^0hmSZw^uLh4yCOAh65? zxu^q-a}I;F+_ZwWY^aG=hYdW{#R7_7o==ZiPYz>tPwG=BxTAAAmAecDo(Jw->zm~q zz6Q5A4T_AWP{DbDttL;UJGJ)`WiN$9!KrgZkU{=>bEHI8-e`8>dZa2H)ML8E_(rP= znog5?wjrjF&f>~^zYY{U8dC+xZEPVB`ggyi|L{AsA#fOoJEKzoke-IZX8t;lFZArw zUp&$71r}y=QC|q_u_6KLz2j@Bg?1+d3w$6XXbg@my*mlqhHam5-U1Xw&S&ENGz-OC z-5-N_%hjJY&*^K;3%6TzwN}qwm$&(dww;9Eh4Np2U^4!a4kM0tC{M8ezJ$!ck%Z`Pum%L60D|d)dX$9r3B!zTkf{ zK!z_=F)SOK>X<$ug@xqP{oPtv<4}@S7{yc+biOY?uYlK*%l}J-uJd+u=74_!H)J+9xSWNL8lHRd{&) zxFJ+Op4`X~ruYzw2!7|=#m|IBs}c=|%Pioq=AzZP3`b-__n=Y0s&U5)OE(Brqogg4 z+gBeaSn0Jg3I9?ry%F<1 zHX$jy?J#9#l_J{*8DC@PkmkEZ%|y(w$r7eWutbrm*o znnk)l_Rx>v%H{V_2MFWn6{n>AFT3{<*Y#`i6=smwLgJPXXmFz->IFix#+C0IxYOh9 z9V#ZMkE14FaTkD3HdNy}{r^_->lFV~^7;R%N+SS^N0^YtXE5M$V2On4+?~6Lpp()G zl(vM=YvzE$<>!4VLDa+W37!+~0xDBm{!E|CA45zf^F#n5ps*;xj^2?<6XrU?$T0pb zkhVp1rdJjc?@|ZSwk9IK)3)-vDFp!U`{`X3khV4cZ_~EPVB{b$O}G@l)3)FL=d>*@ z_0It#uq31-;;;Om>;_6-nVKEP0X4mA0);>koS_`p@<(kD(cM)9dZUq zT9yM7nrO)nZ5ebx$?#P;4Z(AR9Nh)hII0OAY6_u}C^sN?MFqKQ+8e$Rh4xB1R?4BG ztSvG`xN-k@|48f(1<>6_<^#F=pNtq80=jf?qAP3cZ6azY3nh-wu2|8xRm_?-b>JGpxK74^2VEc4kK6?wqb{h$t2 z8{)Ek5%FO~jO7>9WA6=0v#!N;x6SjFewX!DJ|y1J-dBEyTuKj=71_xj!k-rNCT3n5 zxfsk2gESj!RNZ}?r6VX>m@%QAo#=l@Fa7;QJmJk_2lkTTgIZlO`0LmFzdNuaEO?Qv zPaW7hBy~`Ekaq?SZrGMy!=@?*kUqx^9)Z~bvHFCCwX^Rc9US#p0#-2(&_ zXu;3@kFn7HE~)qbiQQ>1g*GEgXTsix0SyPzfxy-FsdHLgK&#ByH0iKQ-BSfF8?Y11vvFfol*2UfbLbw@tKtv34tPo*MwcC;}QAj8sb@v&#!MP+q(b9Lp4;apT z*uJwQ-VDIL;*#98W=Dn^jmCO}b`R(eCyI>ZKKfRP(2u^Alsk9sy7(^#Pw1UD;ep}w z&Up6~&UU;A7Tv-(TG*?hlP{3k4JC{D2a(0^VTp~ z8t;(9qd+3jk&N2bguYF9k^F6EKq}c^#+Z@^REE8d-N?I-OPm)Wl`tQBtmLhou%jCI zO^64zNjV|+I@hd>ocOx2HV%0<`?fc9kB`9!>~<=7n0N%3f`furQ3eBVU}Rw0mNuv* z8HvgO!0Q(5=@ql86{k@P0T%MoLD#aPkrC~^*XIYa$klIdZmiF`r)g(yW}Ibs7yMuJ zUOSPNy*rCwIVnR!-Sw9if>{-v$l-&O;u5gRw{Qxp8G+S}9uRJ2@y!3m8%6V>4K#Pa z{FkP#;-I-eR*b=+(b*+I_xx)|hwyjTl6`qJxg;AuPr{aV#8}awDDi)UE_rZsdgSNF z8$CWYByX1CK&h-D7g>#s;tO}h5J0Pl9~+Gg*==ZzPKfBBt%;)|ImfLTDILrDULcZv zn$Fe@yCc>ts5dw=P5JC-$+AQ99&^@5MqQd$p25rAT#DfLt8>D@8|{ZwHdO5Ee^sxd zp1qGTi7S^gvtWub+10H@=2g|V4c_qsw6#*MS1Xq*cOUfiKcN9`0)O#v*aB**D$!$~ zZ$BG&NdPMm=)Iv0{N3m4c4DFT{F?f_jdzfE@Vt#z$~u0t^4#a+QCQO#e+G4*i^8D% z26Z>^qalJO&ra^ORA#}%2Sx8Cl41n_4Rrzss4AGz_~Z2WjE^T3EXePZiUiI2+rQ7_{l_CJcjfmyUfMSVH^R-g8h6TR zEo*sw;`I`{)$sn9B2IDqRVeo`TDp*oTC2TsE@yfAMdWmmx!XU}URLhDBvWnbl}l?Q zZHJ-KE8q6C$Ehl&3FvcbSdeMu>Ak-=(|Hp!(XXYjN6+HYUW75Z?yV?*&JzVFa_?oT zpB1?ntQ=(rKYU%Tv#_DkJ>Z8NFKUQ^VCa7Nv(@3pL}lg0iCznT?o zqpoWeP*ETI!Y&g7RMZhSIS???5C$~fX}8oad8^i^ROKA>stk8|BPaV%ryw$*$Y|0Q zrK{ufR_<*XG}@n$qa;uW$0Enf>i5gGFlX%ahZ4hC0vXwlp;gr~6vKNtd{XiEmyq1E z_7;^CXe<7->RI%X4$CJMbrVpztj!xnAr1vBA?*+a)QM~f1Mq}uYv6slZo0JW4}_Lj z@D1H>cW**6tLWju{n!;l6sUv0r%gv zsQDN2-~?yTpPK`Cwp#HnI`J57CLtIaC>T^X;{+P+%l`FL_?~aaQ({z@`86DsrC(9YEkxtE zAp37v$%Hp9k*8*7NYHdL7f4Bl%MfRSkGGJJ_7hxxqW9a+6E)d!kwHEYmzx}s)fS{F z+(?ce4NQ)WaK!kc8sA%W=|iQQ?O~Aw*WX60_)WJ4`b5?HmhE1bH8`AE+T>TmPnuUI zr^XK*qAg91W<#6GtxEHMD_O;tAyrInIqn2k&6)iGs{1)Q1E9K_3KvUZ z-7JtX+hnVs6!7 zD#c)m@V>jSdxJD|ND_B5Jl6K|&NJ^TRr4dq2MLZyssRA8JUmnDgb^onLa^tQqRbY%e^ zU3y+we77^){HP9*wb-{}NdmFr^$6)#KaQw#5iJyk&(!%~QF|5D6H`{H3w*xv2{J`# z#l<9TDRnHB4KxCo8*^4xWdp-v>{t)4vzLOOJ- z$~o4*H}bT#UnFTbRF{*$$ZOzy70D`ITbPU}-jV)kY2Hnnp%oguJ=q6Khaq*b13tp! z8Qi>cLDAKXw5e?f1Y?Q!?Fyu-l4PV$^=pC(NUwPQ=5!lyG0N6|`vm{9_saM_B59`e zk<&dAqa|qMCdh4B;MQI0Jt{4N@5q4IC);;P7m+#~E{v1-p_-&((O$pdCi@CU55-U$ za!m^9m+|Kh6{4N7cV6w)Zuf&d@r9M)jB83J?ex_W87?l;49Xexi)W4b@I7b>uha`m zCOQhvGEMlwUzB@IpLZJXzKiynww*HO)USwqnI#Tr1}X2@C(P*{Z%Wm4yn0$N#-~R5 zfBOpN4*dZ*CNVEWFW?_t^3Oac(#4Bl|3NBHw)txu4=~M-eZc-D72w^5Hdz$Qm@e5p zzK)?*W)4HFHQu%bqd6+7EBbw1!wUwj4WEE~#cmA39%#wyGSK(6Ui0=P3{dyhA$wt;Jfh;fT6Vh1y+V=}ODpqKl9@Ni9b+Ydc}tR6a%T6KlKm9` zq-5XZ>&lW{$d<@?g`lo@io1S~iIGxsi4=_d5)^FdC#fy_t`J(%rHU94(&x2LJu+Op zrMTea2S}hdS))KkPO5aI*)g}Xou{rf3!*ir4H53Xc8{y@vX<_N*Mb33E~)VOnb)%W zW)3B&st0x)y)x-KJY^AvT;z)5nk{wq=;ei`K2JNvMz1+5J`Mc+YI%*D`C-fg z2V($u{5%GEl*{d`D$T7^_fd$PBI8s&Vo8FB0e`k&YzZKiN$3H~)*T)C5AFBMVH$x` z$F#G|j_gb|npp;kn28^E_okQ=kCLa#_4d|`f30jC5oaH)rvD?;y7yP6^&{|_3ykC4 zK91vo=^g{)c$mq+INoV;-Qzf(Q#GX3;Wr~?jA2XnU^542>K|R&?B6%;@=wN$tKg|T zzs?VE)vj%9_Q#+Pd_#__{%Db9+~0K`-7{DyIc;_Yw9-E=-VwZk+;~~rF^(&L-=kB9 z=U#25tX!KEbNnIREaUKaW2rR(SP*IHapS5D{XtM5$7Z9ft4gc2xii*K7Lts0kQkUW z>(pu7az0RyUtW(g@m5QZD9c0@V-sn8)*3H!*vFhyBGgPBJsPvfkH?c&hgROmtpFL~ zO1+AWP6=rZ9f%=uhlc2Hb|rxW7|KTx6Dt|L8XayF{*fn zAh;wMwW#M&+X@TK+lKk8S8^2ad)^P1P@=d^zS;;BR+sPpK{xV8 zh(B1~RtW>9Ur_ zR@}6O22!rcRY1yBFBeF;CITr}V{QKz1~Waqi+1{Nj!5+jHCD(98_(aF9!=CdP{lhP zs+O?{#O&J-@EtGxnx9^*lRk}uYg;Vge1MILlj~i*b;!Xl zk4_B~aeK&7L(JSt%|J6J%p2fICi>wk(`Zhl8a**XS$xz}WJ-jt)K}1SWkff>P@X39 zrks!yQM%0X<|p?9s~y<8m(E60!ENgJe0Y&PF+Kjs26Ji?^erv26>XK0Ep)NE8i+mU z`NHaIncq-Rde|~I;^X6_ndP5m<88gM2=b*nya~*fR#xdo=iIA`t=-qKrq6pg^*+)OVIWu z4br?4$vVAyP94}foc2u)gis-Wl8Soo58*0rZUGwh3XP(7q&d~zYXS%M^$PcB5QpUo z>{;*rXRU1895*-M%H5mmBtD4Fj<#^v@t%|Mf@S5vxAtPCWUBs$6TInby6El67|p{b z5#&U>kIa@Y)0*o{MjeO>yKTPxYxd52C&B#aGiCnSD%2y}5hohhvCFnp5a2eUuJKWp z#j;e`9*bx!A>X*+h^~JFp8!y7>qa$u_I2CP<7(@006c!8QV8Qk6g;{Fk1Q1)egllj zUF}ektEE6uH2k%pyb;zvj+N2KAC5-V&BjWMN@L;*J=_*tkKNSeS##%OS2#MlKWq+h zxTkLFxztw(Ko-kbze6R20l1A|zP@YqiE*4wzsZfj)$&Ykluz3Ei`zvGLga1=Z@Gg(M@-HX^yU2V&)*Q+_Z7HOnO<>n+NsOe+4Xg#Y z1yi!RM5NwNT=pOxO%5W}pdCLTq4B}H?|3bJm5Iy`rU=SWz7unTK2dM5nhT_*5S}@_ z5?+2B5vRT-u9OlQmNL3I3prs^ZSSnHV2*;)zEQC0lVA&jO@>x!W)V;l!@wpXwt++sOD1#GqBS3T31=?Q{zpxu3P z#8`QDZaFdHV+EWXEhcFStV4oxOqjpN37gdfRkXW`@BCOFJ;l({9o{sGx{~fmA>e38 zkpfMG{@|a+J-qokGKZVFihroYsa8Ad?qg1=U3#10#xN~J2Q7ng2zc1;8wa|MHuXT){XK z$MxIe!9x5J3r{rlvyOQB$Y}FY<5zj9eyUekmbIXys%zXVFXidkbReM*y4t_YULebZ z=UJKy%a>iI2QHdj(Wqvo#Xqc@Rx1Vdjgs(NHK9Ah7#Vz*&>1<|xVdX~ZOt`~_U4;C zzH``V+UME0g0p(qGQF-O8z!d-`(F*sYjG2iU!}uE>_j>FlL~X|c)+j9uVZJc*j(%J z%@CPps`xXe6EZr+qR%UobuO6WB8{-6#@mq+tk204_fMWISiSX8A*9}sFA9F}a!L>8 zmD!6t;V{>pYUuwa80!@;tXMt?#=NS4V9fReO?<`q?}G8z?tc}G!=9muzXjt+{=Wp{ zsk+~Sapr#(jGrTN>f^oAgT$R;-6^yajep5~VDHun%lu&yf z8ML0&#cF02*d=MOLi! zwbjTAqW+K=IIJLAOKthU<*f| zyB&DsIiho@Ye;FO&Bm_xJ=6F{lQzQky&hU(Vw?LPlT2#$q;X_0uXPN67Hx6vBQ*M> z9GyfFmkJ%X^iCUJW2^e^=l9{BCKcO_Qx5~P ztbF5zM{n;>7hyJDlFF&#`o)U7-~;1vWY@iAgclbamra5EwH++%+N+7u^Vec?M1i!t zB3D#Y{bm}-hsP0aL>_HsZHs@0c!~I_MES*5^;%`WU}B2I5MYY7gWMiR7k9(ts&QB( z8k}HXIIm(94WKh<740|LU6HwB_lR@=c`@@E_=(7%XKEtTBQ?=#hjtjUR?D>HWH!Jk zn&Uby{)+yz{Pq21s_IE@3#%h!@~4Netv50XaOK#D3lNOIb|9iGZPX6@`54H1SigZL zmek}Z##vxM;E_Np@{8TH=j+uh8EfGIRZ z&HUkZ&^lMlJ!AwlFM@Dmr_;QdD3aib2h(Pix+RoKoFudvDp=mHG-7OP%3{ zHvagEG3>_vNF?}aJg(5;y}++w$372>iWfF-vqf40V|q>dbSzkz{R+N7rdg@=hI}q& z%d1;I-)oO-u25IB%&JUP=Xanr(#psC5Qz!Ywk@x8pK99(T4Z!T0*ABsOUHARVU}rl zFXU_=m*d9BKp?0PQsG19utMg>R8XcatETJ|jc~87d(Z9P5RY20Ju-Gce!j4Nlpt=d zKk;Zapwnf4986wCUzn@0Rkr$Rbomw>`vIdixwOWrckV>_^aNTKUpN$&Puf${cKd_Z zwuOX7ii(4px0f_=oEWFjy+`>soQRIN54$Czx`MK)2_bSYxy87#W?|ivi5tiYd3gjS zk}^bR4Vd{#=oeK#j2<UY$JZd4QML4l=WmcON9vl(<53IY?_}2-%}pr}#;VCEiI2EA(?nN|herNQ1WJ zfUl@WSyci_iYZ&LFc7n1@`PL-hEB|P+?J#w$+7MqO#Ys5=II!FR*+R0wm|jL0!SXA z;8$X*qPp$QJw*4{Ur;)=yH)gGalcNF#!{Z?TO1Ug+%Umt>(HKpch0mXZ&g$F%1)I` z>|NdNer?A7j+ES?7w_ot^8I4^ZbVwLz9JX6ecu-Vw=aQz5l=GK@7ouqKbrh1(WXW6 zhAG^aqlSK383I93Xib@B6po`v9QmjtRya})Ng{>pCN?-P2S`<^S280Ah3<&Fj?ZeC z*`pDf+gLxO5qw=R_k9Sj5*zwEFqw8GS9TmfQer6B9T7b&t>dDEoT8`FE3lt02S2QK z34!834xOz$&8yphIBDNNE*FR&!F~)OD~~##DKuo#1(EtwL;l470Wponlxp`)7A$eQi<_a{6+H0T;lRcc@R}Yyv_bya?7gJuzzC2BSbO! z38FYnXD#srQM`PFC?-5X6eFZ5`T&UHTkK76r{jbD8)-h;0WSo^_z=YFr)@bJ8nD5V zy4mSS2=c}^AU0ld$GRiitY_mn^|Vq3EqRsuHW&3Z-sx%H;A9lP)YKP+40KnPWV7GC z5*nAwFd<9Lz%k66c8cp5ia#qLQ_Jov+oNj9P=P+jF_BDQ$h81CixrEdqSKDD68-7x z3}xooL@LBaTJtjkDws2`^ox7BF)Z%#buHsnWU)J8%Z zchv6lmW3q|6(R}C`MWZ$3BiI2KBH`8#0Cx?mBIXHEh|9pPc19ttWgKW_J z@s0^i8kltep$xAdZIQ_~d_fHo$PR`FI5KX;=B8@pFq3hV#taNC@K7Hv6xShL_0}cA zqa~EkR6gGWn8lpbY>2F=R|4!S|G)W{aM&%0CMAixR{1{Ij_S#@J^6#FLuhx89YJYZ z^ZYzHil{gu9UR}O==X6on+RlKIwIltJ6d0VVB>!am$+%F*_KnvIYRG$y2S86@ zRCpdr2bSo5>nRj4PkIVPz6%yUSgrv_D@GU-Jn+AHP{_gR^bwg6gK|*Km6^(a%M!eN zpXv0jRN{DXAozIq;iK)nTW{^URomV}d8=9jjmk~D)=Ng@;!Y|of4gvtBEyfaL@`uT z!N=e1dNmuC%glNOPDUe3er2pf1$*`P`Q-b26&q+P_4g9RIJmiLHhOp33 zgu(uFIm%$Ks*^F%siJ~W1IL|OfQ8aVx*24kQ>evBoIP(-_6RJN5`yo|MnBV%-CAha zCjl)Y6hxL)IuELqMQD;mGZd|d=F0PjC6FHv0}nXrz^?6bxsny@PprEsdR55gxtf)S zy%4khX_VxLHB~o}L)?{)cF)wl#I|pC9XcU?GcQ>Hk%;BoCUVu<`jO# zz*n==8LDC8)RRD7yrwhHMsM$8>G&k)|iwvuyU$q>)(E46Tfz0fl$dy;q5O zS8Aan3UM19O~|mdyrviAMA#-1EwsWLj4KlfQ0(koamyk1rx!Hs;YUOwU2Y}ezgU-aQoc_2ob z0!fonYghxH_BE5}oU97h6AsJG19Qkd?KAt4YTDp>mX#53t44oLo?ZA(*8As~#c5^k zD-=CdMMxz^)oVJniyn2mhI90NOADRe)QekBrpN8Xs7mtDzKaC4l1XzEuJY#>eHks6lmRF*W~m zwQy@7yPqxAi%|5K-CyHl8ZrG08&N>h-J3rD?HB`C;08xZ*uDhA;MhNFQn5;Z;AVn=~fS3)G2ACBJYWHC}idHBYMqIFcR; zkB;P*Yrc`f+}n>4Hu>~%XgU0Z7WWi$OHr4aLkYcZ7Hrm9jXLf%W_~y`wf>!5|BCug zkJ@m?h81chWHR2JxMRa7ruMy`8%>#u7~wUuC$P^n?SEf4l2R8QY}UP$u_c;kA@)2x z_jF2LD)ddo&{`RO6NHi&XnV}~%l7|U#=j;u`iS(2HGyseZpRYZ zlX?Dcqw3t2nR|KgU9|o8M0Fy+Xd9sIrM;8x+*6UsO$y|?5KZAuZuywcvhmG=5+UsJ zjSRBnp;3aNq*s=Ac60_eD<*`+mCEW23~YSAEZ!`d&IVs~rA8slrJ;IB4-xh}m0|Ux zH5&Jv_FtYySJrUVs({F{4qM?Etjk3uHaa$37T(`;d3y#5_3M?P6Og+Ryr3a{LsXrZ z+G@%sBA}*jnVDZ84PXT*6izA$(C6WL@I;dHq2Uw%c1C+w^Z^cj^KZ54ZWupml02jo{}cwg1XA0poLCKM(j(0x*Qb#}20m4RdAj-s+~06H)r+`NGHel6uVQ5^``#DA=Gx%m(3%1H$6Dn{`50vRwSlWJ9sLA^-U)P>m zuU6L{)})y*nJ#Rju}&{iE^7Q0;Ux);Z_7LBf_&i^|J1@($(DhMW8wpLMsR-08cZ}i z(6}b~Pp|WSbjiYwtrFRXyxb*3YE8>95b9Ihk~@I--oPcY(8HYdvQnm8bcin{2JYiA zKQi4Q71g4eEB%5)$K%0#1LeIt-G@RCuk7@YO}!-z05RE`66;%}EYtPY2ZGxQ6UL1g zjj9b@74J5k&*W4|9gXWlVlm&H7qc#sct5Pn&tBx#VjLH~SwRjNt8-((YhC@+4-Li2 zjkJ;gg#Be{iOVpeoM4d@6u--=aK$NF%~%es%gI7(i-U;?EjT9EG`>zIYRLbeM(M*QBJHiovnI}6N%i2j zq$;fMD<8a`T+CXCVxkzYKiMyRYv4%yUcRr!Mrvu{oS((R)s=xhfJtpfJ>vkoqj|;J zeo{^T-RWJjQk_(Zt88@B?c>(goOw2!WVJhb^F(y9!w&?FS{cIP$x_jDS#%$3O z`ZHPoDNUzYvrRmL-UR-z)cd%eA@9$ju1640k%kx;NUU>K>iKmpQwD@4c1Mm|JC%RGBfLtE+-gPJ^Z77ZW`XteHQ@T-6_Z^uidY;<1twOF>pY>2gTURFh zEJ^K%m>wI&9$)sO5q|VCm6rF3Yj3S<^Z{5&XnbW0*8~KWZeL+i5i~>9Yw8Yqacoq9 z1FCTDHD-6KPRiK&5->PD;4FuXxp}2a5H9uXn)3#+-U|=lwOqR?YOtsN;hzgv;cG)V zMQdu-m|MY^W~mnTr=?-%7j$U>kS2w5?8dHi$yenW8CtG>=eUn6bEMnpJYJuj8x0e7 zLl@sH%1LngbLgq~szSa*C7_?7Ct+SFp@`o7ya(&v{%syk|w+?%ymV2 z_xo97QDO~cT8@8~NA{kFoiRwq`cQxVx#QJxrN+#;GLqstNB$sH?t1uz)l{O@i?&P+ zt)R}17Mp`0aP}835zP!?+KaK^2Ke0AZfTj=&>^ID`)tH?t88|Y6EQ+A-6jv3vpofR z0*1@=_s!J2Q|^*N9#Hc}s6r z=7co}O0>8Zp*BppnRK=qmou)4;p74XZT@YS(^s?-1%vvO7tR>O-651F2fz%g(Pr$qs@AGf-US_^MXDsiA*A9u zWkU-SCfj1hy<6C33aV}>1U{BBQGNpknzdoGv;)Q!x1s+ zQnG4&q4qi&VXchNok%BMYK5)8%7X}Ef|=L|SbpoP^3ersJ0!2vHwWvD@J5+!Ta%~ zor~w7RM3XuXN`i=Lwnuzv1kY+R8VH_Zoxs_j!3N)*UQT3 zTuY5#NOShB@?1{yEZ`Y40L0tT0shhQtD?aNPE13v1SIXStC*iHKX7|P%Y;8)F=p+% zNcD2Nh=#q_Tl+gog|jUr75mOl|JM>;EgJ1NjBacbc86Rd{<7xVF{t2BoC&dB2mc?v0>iZLTfjiLqPhAkSjz_@Jz@>mWjEfWeRzEfsg*EQ#$ya(&KB;}B^*(0 zU*Ag0$%Rb9WfO^*W5m$rAXb50`|VZ2JbIPFYOcX6KJ#E|88kri7Nu52lX-#WBE}gz?PnrXZQ;s? zF}p>##*oT>uks?yH!Gl+$+4AIP!A`MP0sQR`|{m|AsuS@zvS=x^=j$#bmM~j?7a3| z-xwspWtjNH8SWb&WKrcx)?C`mi|~&65pDNr>J3z3%&y=#jE~0}a5#)>2j|xG#SHYm zLGKyFV-J2rPd_uLl>k&{1O8m-){a41zU)jke=KEGV8C~Bs4^sU5#)*9U-Xd=Q}S^N z1dNJgh0~uUE$uskc;xsIRkqtApKX6KEb_EBVgKSz->rlx7h&y9mrStB?|zvLhJ2+9 zQ@$TEo%Ou>HeF>}MZ8=V=`-W|6sf;h{`W{d+W#d|fB6x>e6T)7>c=6TCUVASC;wxl zo<|R)!xr>Wz#Y9!`d6fWE`juE^bLsAQ(BqF{#&Fz^S_JK2RX2U0M`+1l*;)tzc3o+ z6)Y_hx6u{l1g$9(8#B6Ds4|m?-fFk;jzQ?oQCsx&@53lVVBl>FI5nhb{?Gs0DU^8~ z_L*vE7Vnoog3xVj0{^!&D?jLnd>ZaEzQRRzVn@1t157`KORB(FlFQ8o=W)4W|BW6l z?%22@GDq=RwBi+rI%fi*0?E3gCq)ZI^Msgjm5)9VeN1z_ zU^$+MJ~%GFi9T2EJyFpDnuKC+pfJ!M{O`*i7vJF7sR4!XI2ay)PRkhFBVItQ3YYTT z_kKB5l7ECwFZy90qaRmii}77d91m|zFPH0Or)M9RYvk*|W5xb&^7N5f|NnVSj)hgj|6XLX&SQf2Oi(EFmJ3$ekV zSRPdpljTT@U-k6^GJ_s$Mm;d$M8}dhJeJj1*F40as$de}{bL0r#vn)`SauRO?Y?!Dc~>hSL_FsPik5VBnw$?PZ%| z(|E=nTrlti^u#OBief}WVQK<)X0DoGnbNChlAn!bkJRamjRzQ2d@d}^8V<~w+yKgw zW0TTjW;>pfv*`U}Z1JpBQ(~YD3~CLv#ok!wd~s&s+u^*bC#n_B(aBg>W3~Td!+P=v z@0#O@J7l%_y=J{;WBo2)AV&Rtpgm;!-Ut-*D2DX%mNH;prb!XU4Gs$4JC}dMjNprX zv@f+E?MqyFn86a7?Ro7=hgOfK zlH4iSl~YH_@(;jS!XN$gE&*?yXcCV?T+17J+Igd%1$N$oWIjiLnf)_Rzwoa>JymQ1 z3Qv>7ggc5+4%zKmNyem-F!YKr@dZ|{K(UfQ59zv71w+7wE?PDynRECP^KxgX> z(Ajd}l-yl*IsO-$@|J%VU{g{%{#(GF(eU|RrjyRIa$sevS?KTN^1?N9c)<=IrES~Y z&lM+>7tL^Hl!oLPB@JH0GaR*b3XW2`gL2SjlzJ6T&{!@iZ^*%qa7x*B) z7u}iVPUJkWg-;QFC;qs3RyvKt63Ax`2QLa_`MIDZ>vJ_m$K>Kw`221d*nWyRVvR&3pIw1DZUkX>_kQ>0fg&E(lYI25Jk2U z0QZu~8P7ret!L(P57Wp?1WWuUg{QoeD$HnEvud59wHQDh;tcJmrF-y1i-%*y=e3oe=1 zvDMyJkTXOib)qwcp6< z?e`U4n~bptc}`QNHzlgE#9LpXf2Lhgtg+@8fKoUhI-26Z zxo4+O+_$XEay;m*_fIH-Vw})dt(?r~Tr{wgq8D8(bV{z!WNQa~gM6&_YyG3%Zvza+ z8M7EyDK!W2lv{-c1B=BkDg(>B({X?&?=Lir?ZmhGrtW`6mKTyWGO+%r^}`lC_R5Zb z*!^?^0qiso-0*D2yLbPo2b%X6!}{$aUJwV;r@!t>si)~U;_luYiXckthjk*`v7u40 zQK1+3SQfBTe|{6YpOk9$ejD$sAZj$x3wi)pYhjtpz3ntC8eut8plid@09c6&;WklUZMF^q^Ho& zA!=aRzN=qRqh`(ng?2+$Ob!Bl7V`ZnA=z^%l=(*H!0*|7;dAM3(pY zSkDr(rVmFcvOyOyO0!`!#BC8#%h(?cM8RO%6+~I`!L85tZzLp~8&@n{_ z{7aPBzOIobH<_e_{_|y>Hzl(Q`mu8$^{7^@H6>i&jD!P`g|XB3l2ij5Wej`< zEMwp!@IC(bW565!7hJVt5Y(1bnITdd%5^G6 z{TiTsT8krfk(oMCu|UlaVw9I>%Da8hnf0r&&Uu8;TCk?XF0pPwsx|<=yhRN`T?(ulA^ayDSe5wTm+8{trnft z!cxzSCHu^eMw0p>*157=@j2TTXj@OPCa@V)k%M$YTT(o!wAN>kG}ALkdYfPA!%Imn z4Q?w=J~7GG0Wntlc^c1=%G(LvweP=_z^6qI7_{^ze{9@bAaPrjAe)(%M>FS1c+!u#}#Bgo`+T9xnxMmq%WJ$Td6 zQ5@jK!5RK7h+pFN7{nJ3|F8DY96Ltu5sKWhpA_5YcP}wpn#FVd%2@}JeS~P7PF4J<(Z490H7#^CPYq=*y19Ba3P#a1%9*<{5#@s)j0wJm!qCSv&3PVoA=5bcC) zOscvhYlTA)u_xYLm|D?gO$ID7UpL<%u(m$`!XWr6(tMIUW+LI*u|x%^Q;e!9JJlfGK^6<-62P9}T+XiE4vrP7gxasj3wIuPrWP0U#OA@o87Yukp0W@g<2V;)}#4!E{ zU;w}SRCoD{GX2=>!h`@s31AWq5S{^41;A7BZQ5c!1hq#3@YI+Pm(&;mEcQrG&m%=W z+AVwwa3G)$=mp-J`@mmNPg&cwZGJ!5_Ej6ZrcnSg1ne2o4)CfUpD9Pg6 zv&6D3liix)o?O`5-#S(vDtmLYtw*FK>QT7@nDG%vd0wD(A;+z32-Fu3URQi1r5n^u zfy@nV65x*(K=s+E#V>@E2{@Pp`=ESvqg}RpzO%a8BtR)=G=-O3@f-s93po@`n zUVN8yc)dkJ%jro6pNK=2GuSSU$Ih+EI>Z|yk(`rKXE#Eyh`fy=O_zsgVBoYp+@wJL zm9nmg@B;hBLLm;k<`a0LuX|1gjVBk|ipKyoO&1L@IimP|kb%?U*ok^_2%1`+ls!qTaBdQGrC*3oNBQ4`!XR^!;M4gisE>v5a ztkx9p;a&?w;waZ8`qBJ`TsS32%?|%C{a_s0yJJ9^zg-~JiNGV0ZD)<3xZ6X^fP7n& zY`kj_b=#kvKjz=#$re_CwqoI zi*Yakx%dJb@d;=eiziyA_6k7j{8wrpB=!?jXXe>aqW*;N@`{Y;ErViMMhiQKkg+3DoS7Q7iR%Ur(RoJuG z*Sb5g0AK%piER$3fyBLke#Hm=-KQFeLhiUppaLTr!nRHPKlkTz2Ba{O_2W-VEqFYqn`3mny_jxYsVHp0<`K}DIx)bRW~yxorK zV|Hm6(b8h%L(!*=1}0l`T848tncBDSTECgUu8;pd@u9^HzQxi&@4NIU6Xd> zKq$=A!Lf;a<`%qji4Nxz@*`TgkF|D8{xJWCxVHd`bL+c313>}= z2o@k%f=h7s5Zv7@1Pku2!3pjT!8N$My99T4cei;Oa?X3+PiF2nccyN2RnviLph7>r z_g??CehUU`kak<#VKTZ%l=v@Q3lO+CElGu{&uN!^_+ke66i27i^};nAWmv?CZ+A9z z4fU62MEBmT?48T9WBHHeuT#zTvdI&DVz0j;B~A+U+SNlw@Gp$wLk~hovM=^)l~|&~ zn|f|l`&Rtk+o$59O9dc5|IM7PgC|n+vkiU{wE&Pbp#Fv~Vy6BFA?sxf>+?9G50t0{ z0Mnpx1Rc=#3jo8hlt8Mcv zjI$v^St?K8$*s~&4qHkn3frW{e+yN!44h-B?gxpus9bWuPjfG!1c20lTOQ@*Q%)80 z|1FT3eD;46NIkr~yk`IZFOZtn^xuKh;G6$zKx(3iQ)6qJ6E&rw6HO63CQ4C`L^f}@ ze2@j}#(!5VrBF##XZOL$&S2|Uwuv1@k5dUdlHy+dq3v?gsuO+(ZFMx1@a#WZ>~A`) zi)6cDu?n;%8>$mf3NI)ugA_R30YEq-7;7Li-nfm1|KC)tI&yQa=FfFA)nN&5<5W zLWhhwk5kF3);Ox$L;Rt4IO~WOjgwNV0vpk%&q6eIzO^TFky&*-br>y!fi(MSfDfgX zv;#E*?fUNIWPY-XH;G2p>YZYjwR2knyE779b{$MA7RcC6+eo{4kdJCADo`UdMG&PX z!tMBdC{0!=K+ZhZ)!H9{Dqlf1(!3|Gl2^`f#4+cn#Ks+8at5l;jEyXz9Lj=o#eSBM zLs)T?X@^v3Jn^&&Rvd*Z6S_csCI6vV`5S&~PU+7qzcHw`WBKj(>xV%eo1zmU>s=UX znoBnx+PTY0XAY1=jYYN!Q)mOT8;rT)NEd+nn{@MvE`_S`LwDzAqv7&bQ7ZGtq(wvn zqi&47M6)iYv}j-R2ax^Ailuw-iu=f_GIS_H-!OI^5_fcEc`A!p9FtW?Au~g<*D)0YWxd@5eva{MJig+4~m{24GS@U-NWuz%>t0sex-Api%?Zyp$dodNY3# z{;ALg7=|ijV4lq#%dG2vAnz8R^5jRsSFr z*@he_kF^Ggl2ZNd??W?q#H6b9GU(8Z*Kj-_qZCqnK^l=S%E-3D3QOEMVM1?j?HoB^ zs#I81c4we;1L8LuSxMg3egJ2AkJWwfvQ(7_z>7dtwxk#|-NVE{0k;YH(ASfhjG{2F zilBho(?9bKHUzu?ma`|;F1gSrA6;@5$eebd^H1Dfm8i5 zIh>2Y02n`MBNWj5@O=O7@8DkQ)w`$vAmAgQ>i@iY2OtK3`{&2NRup&)obBIyA3pzT zK-%at1b5^B<{TT`fzu3C47P_0=0qc3q&T zotM*@=_N^cZDyX_0F%0!0l1Yf)G?rHdMbi$Ska20PXn-RN(*h$#)(Gyx0q?=K&jbu zB6P45<>dq(z&j}|Io`Ni&F;H=R9s)^kMD194NOwgVqdjh{C6|062#sE`=hUWkYJt9 zkmG$j}_>DBd=sMcFU@2P~as%q)H!Z@My5v5S5S>(94<3~09$a{ZVKnK9m2U_+ zqy92`vz5ypbA83MV!Og%3n2VkvE`?zku}ECuc*_`Jjp%A z*40D3!2C}fiiEB+cvzZ^d9MbIHx(g3B0pLQZ{e_*H@k8#W2+|*(hgk`6`RQ~Y$-TA z+2K)Sg}&oA3z7K!rps$1hgC&8zgHv_Qspwd6wm}Uj(b57u+T!K$}mHBWS1O{>dniq z>FU%}H&fTwU zsUizTzOGwujK(ulb&_M98{zxOZ}C>-YhfJv%9eq0J>%i^e$pB#-wb89R;fI3-QAsZ zMr1IGoQf?4NvUAM-0$fEJx}=-26*jiy|r3mHd^U;j^6N;Nd>dS5S=tSNonQB%+#Lm zwV#k}MqA-E{D;E&S-0D!HYg9GAvYR{&H&>o8hx@CH&$N?$>9Ch==9H7>}(&?85)wz zf=({-8N_sB6es#U9gQ-V_|uSD6<^ONN)@@Z@HYcDoq4q8psi-$H&Pns=|EPUU1Fr>^j)h=S^1}OTLw|*?}Lo6aw)8051!s_%nEHsO?Awn z%d)CLh8&tCsDVpnfwBb|h@%w!g|XL>74{Y8S+-18MO^Q{3>?4ft&f-{zWt6@ANV7?)d!*|O?f&b|L-^{B~o zxlm{9;9BI}z(zo6gmXzhTH$aDU9ydN@lsRoS*mcz7x+P2Q$FbH2MC7Jr6)jOyyOn%=x|0EgLe`mf4PK zdibFh^_>6YN|t7z>y<@Zt$P#tVB&cA2U;)+GauUNkr?q~J}~=sk-h@{>JfBdwv3!( zU^L;)`zibl(__o&=?b2&xTy>RS_qu{td)%HxUVlfV?@m$f0**hy=V-QFi&QHV5+(l zI-^W~BJI zqHu5`ymGY9l{!`MTMMKopAFif8o;6Ur84GoSdAph=RYvklaLcIl6S8}>UF(Yl+}Ef z%()sMRq^GQ9`$ZF+>jJoY7CnCkOwI`>6lo~d&EX{CcaOR^*Oy^;>J>EG};c+>pu%$ zTbh4S7dmbgWc~`cl#ZWBM0bYur-7g$$>eQoEq)^{(&pTKiYrB~cQ`ES`dmd+wNRa- ztnEJjUhnvLctQb%oxH)iiVRLFnyGL@@xozXmbatAobPgk6)5C2*3om-OLYIRmagv1 zL{M%1Lcim0AB`(gKKLyDq9lcb=1n|-ja7!IkQICW1-Lvr}U#!uhb6bkh~p!hpj>4psmko&WiAS7tc zW2hJ`sM(`ZZ{aNMNQxxx`IB8)$QdH*}kSe&&>J2Vu}t zG^c8+V&!-Sp(Y)4-K!({D!|Yin+6eIUL~?HvJoOhjR7U%t_c?n_AhY zi{h6rGu~9Z5hqs2U^v-bq!3CdGB%{PcKPGi$D?i7WwH_n#*VEh=6`H(6pH3N7sm@}YPr>+rgmVccRF z^A&lrV&UO3!CnX+pEq+Q#GB&IAR;oVimEL6{9e$(D^KSnQiYzu+ICv~G-pk)t}Cke z3alG_A^?-=wgr;?o5;jMiFG)9^aCqB$Q@hxPj~Du@F0x-N@_g{aWyVAcC+T@OhI%2 zX*o><^m}gK7en)fA&X>Vj@BkzVO%J^{_im=EH^o z_RO;{UX9<_{8uiIf7)Y>t|$j%vlUQn$xSd=2Z)qBHbQk~39v1vZ| z%6=2Gl0I(l&a|LM8Uexxq+NS5$^2H_pB?%SHoCRnkdnTdMnm^B~RHf@3z4`o^W}Y(ajys+! zG}Gb1WP9pAe79Q%$Pg19@-TIYXO)$HAV$fXSuZuea#Bh+{=79p_ov3 zO8lm~Uot|~#ykGJvNF`nAv(OZlIvl5-Yvt6Sn~uW8n{X(?@!hvLta{ZqL}uOtH4hv z{A^*nbE})Ehlwd1B*J~XWCI5!ioTcKC>LcTV<0+-;!3K+F-=!}r;wGtA=WQ+1TSdNZ#M%1kUksCK4tn}4 zP4COfR*@bLXE?8{-v~R{UKm?DS~@%D{D{N<>@BWU=_TclaQo9QBbeOdlZ@b`Po3yC z)wohA(-FA|>-$1Viow()hz~tH*;@`{5HV7exqBZ20ASD8UI5r*9TqK^-w3Y`hQ^0e z76Y0Jh?ZFo|F5xlNVPrpw&~`tV2k#4GB6Jb6|~5Q)V(zbMJm}04O%r>EtE7X?}<1J zBR^2CSEj@L<|1OfGkTeXw9?o>^#fK_I_`l^D_$(M7-k*bh*(Re%?>3{?l@L(=j&Id zw0z`54+hMR${hW6a!$%ryYW;?7=`Jr20zLz$pwSiKzk$8*rohs84qWubb_M{V>6NZ zkZFbZ77ugqr_<;^+_3u!YIvE`tbbJh3_$eWyi|pL%6%-M?%}y81AQU=PCs*`bAQG8(mT3|ZaSvZ8T9WLE z;lY_vL6c%0xwj;TU3mpX)Uhv&|4j~+P}+Yw>T(BAK<+am6V*K*Pp7Vo2h~k`K_xdf zi!3Qa4K-IzEKpQc2X$MFT~_dB$P-y&ALnCZ2P3Dsu~JO27ndICvlsiJAKW^}S8lS+ zB+MgR=3tzelJ?$Z8FkfEdQ-dZ-@;XzImgnC>USm%)H@u>zUQ-~y<@Q=Jv-Zwyvz2{ zR}Q#N^j9HzbQQD8x=hn1u#nbXg-?hLF!(Y@b=O?|)n``>UjO8A%4u?=+9Xr`?DhsN z(00FFBePEleKwwLI-5{+F#n#ovT9Fv(!Z}F0G}3_0+saxYbk2X<1Ltd+y{Mouun&9 zl3W>tao-r+UEh6So(j>cwmV}G>@O-siBlpama+s#LH`cp1<6GFeeSktD*0Dzuz-O1 zaBdUKHMuUa??~o#GMD-|ivck|>hmHw6?2j`IgE#b1 z#5&M^>(+}dvUtVcGbuF?2U)#pIVC%Hwm$_%EmU<8`U*0*0$X;Y8_XDJgaW1PKb)b{ zBDoU5+gGYY>~p>DG|=D!*xZOj!#gN=<~5{*%jsT`Xh{4Vqd-HI`Y8NLR-eYZL8ysN zuK1(>l?AR*bO?Q9h}`EMa^?&lYkyu1BBaRB*DbL;MDrtBpR3CI^=_+bw_PJd4c#P4 zVgQq}DMnhU@F!TzM8{B%Jqy!D=$egRgL=UXn}MR(19Ly6P@n`#UA|?nyLxei8siye zA>>LB&fao;FK(ua*f*5@+Py|_=~ZxBsXrfvA^6ofArWN(ISj)r&miCbSY=v$9AooTYFgEj*t4;J{G>m@K;YyM^ zxOZJ7$!jlR>`o*^kwEJPwa_bOIM zJ7VaE&T#IOTZv@U2=UrgHv>Y-kVK(>Ar>{Fq`!ruaedj zrD#y&OP?@7?k}!@Az^VVhjah8T~`v`^~x%yleqr)Pu?-pdG@rpXhAot20@dJ8oPHE z2g(^D{a(2mMhBJ(2X{%Hwn+;8B1+ z>_g1V(%juFw%T?(SD09b>y|>>hF8&UmalMxmJWA<*k(UNIt-|!!&ke~O{q~b1RowX*LT|1v~rc|ErrhJ z7p)h>?*5ElrTg^vt$wC1jlTgh3Q$48XFad#@c?gLiAD0CLV-hB`^y)996ea7h~VJy zM8F%fQc@CbGc0IUP{J{dSP#5yf6aKZiDx|9#1oU=B9M-Q0XFfce31RYs<#6#6;7K* ztWI7-EfLghx%Vpj=yLZekPmVp1M)#ZDY4Rm{9-Zw?n)`Nb>V}=0Cy9DD{%8it>Bq^ zBFYUMSmSrIB`38j1jNIg@0=>1z|jes`JI_023ztCy@GcKbtR_F)CbD-%I2#X(#jN4 zv#K|5m4>P}&-Tg}w4{eO1=uAOWW{~!5kjKgfQq~U{Ck&GuVhRV7!c|&k*6qtmHkbl z+L04oi2Sc)5H1yv3{v@SBU$KIL!-n`@stemeWjO+JPz1l+25a)rUjn?cG%c+S%2gJ zin7n=dL}=)ND(3^9SGZoGhY!>CsmMVVTnW5!1v9X6?_j2Jsaz;+}sRC9U*GsDlTWU z=@9*)y#rxaBX|Aff3enc`NML!h%Gns6WE8aR?5DlK+c zNUTPyPbDQoNY0wDHkVd`nvV73?XuCHuuZfu#m&^rYpr%{%g`=F0{k=&emOC$ zA2!P$#;531zx8hRwM)Dv7k)6b$7OA4O_a0rKutpI0Lr*u*<+UHu}fK+&-uqUT|Kx zui!v_Y;(rDmUr~iZ);!H=;jGW5+9c3y%qBiDa8KTQDBm9tBlOlscI#Pp$Q*_j7o|}pkTOcGJ;70#tFc$fTvJ(Ms6?T4!qFI zub?Dh2SMDN_3D4ZYrspiB?24(vJrsM15aHjNk``( zoA08BbC6l-l?UgS8Ewn!lfhToq?N8T`sx*lOE>pQ+`NgD;HNbIdxEx#5pMiG2){z1rpp2;D;Q1!D6y-b2IEmV_^I*z*wc{gFVu z+N5@$EE#adi!tFN1g@FzlTi;b<9xoFMf58O5f8Ib3g%`8P@~1NHunRy9NnzT*bx^Y zYPQgg$m7{K+nit8BZ=0zbl{XlMJR&r;Xvz+0yIT22_$6#%Bl`yXR#g zjEV(KeeJ`hPJBpv1ATJ1vtOD)y1c)g{6RO){lv^>^qx4z_a+e^d$1cuZNU6+g9%0m z$oh=JgzR_I`i!SWGbdgA(6j6Y3Gy)yl{*->yC zH52TLYeO%-C;5V8)f2)rWfF3mM0!0kt3`gJ+cDlrCP@xu@Hz8C<9GNdH}PHry3a<* zA=_Kr#QZb+3HD4_lc-x(^p<1eS)TyvvCLCT2m2$H_0JS+%gH`y7sdjcR}+qU@xu_H zmX5ZkmW~-vOUK|pTRJALm_DXd41M?UPz1GfU@)+ako;-s2s-_%r6Z+P2Mic&fWR}% zmADWPJ+uow6)p)s7cQ}~Q)1Hu0WLPkz$EGEc!DyH8uX^08Y$@P}qM8=2^>qMrB8z|5z= zfkrkw?KqW%9eSxhzJFzmd^=5RL)l~s`)GI_oo^e_#nh!(SR%ms#;w?AxYk%E0!D0=?c8{PBhC= zzvHGedtLnf0QAkPIH{xqGm2i0@1;YmNyexVrk^b3>eS+vs>O^IG;{}MuzC0kNh#ud zWjA8eMXJ`W;QH2n1UJ>z*3-db6{|f2@j0JM$W1E+zvtJJBCi?TS%0O_vq6m~Z{U3u3K~ixg;JEV+&6Wz^Y|_eQ<8Avk=GoS zkQ8bS8^wY|nri9|b;v9=2c>Bjvitp&xah&Q7Y{OZU-%EQXVBQ+-?n73jw@x*DqYP` z#JEV5Uw(Y}V0fBv+gs^}Qa?=Tw2ZWmxN>^~f3JUYkTws%SyFJZ-?S~#CpM_g*IaK- zs26Xiio?4tnjbzamKD=Ixt0Awbq@kE)PTg z*B5&?tH11SvoD7Z4^1V57Ky9$?cAv6xSQ@TQw|k}-0*ShWR7>s?t6ytezCJ5-pt=A zGA%m|AzUUWxjNgPaEA;M@3{#MJ>Ic8ByyS8GqX;!J2xVq?+$l8I@r4&549ycx;|VT z=xr=Ow_KaWG~qyY1pw6ZM-E5Maj^jJ*MT(`d-kPnEUeRRww9%jiMZjxAzAS@ONLd;1<4;2y%0VNZwnnp{GAB_gEqzlEgVUpDO>}|b=e9{Q@7};`McESl( zSJ`{X!pKFo+etF)ZFk9|1d-5s{Bq;M$0|}bzUB3Q(I302XCnag*g^sIkvU?p>BYrO z%pc}&)x?A>9tvOI(<$!-Y9J4rEnMh2tgh57_w89#6~!y$_J`(WA?F$%Tp+*P>`x}G zNQuw2BvbDF*_jZ#kwp7Jb#(~*?i3Ppb8ol@mkb7WeRZ>92-62WXw$q0C4@$36;NrBB|SoH{8EThg_jpjY=b}5e-lEbnE*eXy?J`$W5Sb}p)|bYD@n#lnzzY<7-Wl0%Zt9di`oHkvjJFraaq(V z-)c>|-1HZc2I!9YJ$n56Js+>Ey`T}J4<4-QSZI3gTTaT0Zw#nQ&Kue%vW*&yyiC~% z^|c*rW7~(hiAOLXrZOZddL9B?ZrqtZbO~lzTshlXid)ayJJ^c^j%u&22R;Y{2Y2^u zuXQY!wJFD4DK=>ujtSn#?)NEje_8fAWOq(aC!LF%tt~qx(0nLWGx_wIO<;ebfxudq z^Cn@J(~y3sVbsrR$OCd01@JeYz{dF&=9d+HSY5hs$=WAD zft*}+4cX9CEAzg-lv(Pr1ZfYE-+%+f4p~LgZp=o7!`kCHC|FopcSIpD~f|eB9 zyYsN>eQa+*P>wW6wChCq7W*5iJtK(T$&*gtLiagW6J=OOalP_<{Fp$R#augwGwUy=kBo<@B|`nuQ^xz`FAvBaK5F0A5mA_5?+ zxov4}g`78BwCRs0tU1rc&OL=Vvv>%aSyByF6!rD(#!gMfz1%9&o0x`s)&@f8y)b__8_OcqGBV|Fv;zSrp%y1nZjTA~i`ih1%==1lz*W(4w7CP$)=UxxfW#Gt9{PJdwQ@DW&(((^&no# zP@)V*-dNxk5q>E69lo{-ciwH@-Xk<-PD7@*j-)H3O#Jgsb>6zEXFZiG`;{-fCAcE} zTwRSgxQtM(huVZa3KeHBp4rv6z3Xgz2^sxQA*>N9DbQ&TWO|M3@5-#cN+bGnz4RbqnKk-I|N zj1g&kRQb`CB33$iqg>=xrkn#oFn_Gk810vCPK6%m{?MA91!-q7c;qK zMXfnCZ*&QM2<7T}wcDj8xuLWDmPDv|ZvA6}Fyf>i_~rp>7F!0I#gG+E_L*W$HuVDD zk&8NzO1uJ$QAW;K(L*zvpLkpHL20koK-wz;saFdm+;=*_7QguTBA~opQmgcz+K|Ui z8!!wjVPofX|AP=HeMSiUCvE7T2!Vz_Z~PHrF??}!cs^$exRQS%1h9e;0ebrZ66gC( zuZNKr*(^`vuq3>N@t02JK(Cm9d%%@(Kkqk9a`%LG*9KndN`Oetv| zyR=9je#7Nif{!bV;L;i?Sq^Q!{Yk@*bS3ws%!Q$w{DCQ%T@)w=nx!m)cUK$7{et3j zImWO>0u_m0lrLWL@MLDYw?^gixSGq2ZdA|2@Um(`#89v8B&A65H=lKERlUML$x3aQ|kLEU~j#-lK)bl{vyow17=d4F_Q*o_bLAKzxKIMJC63YBk{x^`W z4;!eT$4Ui>KmGwgiE*pzUdXROd`-{OQlexqab}kJyeG314q>loPbBA1rgyE&H>XP! z<-sPH?rxagPnAJtM96@uswv%x9(InBI<+JxHm#u6;YI7mS&%5?bNEG*CwC;oLa0eB zMiskA2|*hPU4DCXjZDc)2i04g)$7&q80!1_@R9Xz!rS2)^>++aaFQEFp~vpfR!jl)^^=Vh*aP?Ulr% z9Wg~Wg~rLa!W%L@!s;8$5CIQ3Pz&IaROr&@=Z7;Q=I6AJW1fUO5z6^crGAteezNgs z1u4x&ERPD*%41SlS+*mV#yuTSC*qtZJQeifu=Gxx1D!B`DgZ$jakwkmkCo4cWE98C zhMRbW2*(LRT%M4W90fKk|9p8UWiV z$Q-|*X)(77ZXLQ&`{8vzH2}6=g#FIFusUliT>@z9TkT>bbu+4Lx}!;Q(svvKUG@>I3-fpt`hQj@29b+}ywMwtup23(-9qSp$i4r?#Jxfx6XepWa zWxptf8NuZYjqwy~MW6ldn746F*4yFrYc7iig5eM3%h-B6de*Nbw#MOh8uA+F&<2c^ zjvZbDCD^zW9dQ6`oxUee7X(|c1Hsm9`T*FvgwX}^L{sFUPr%B1LGO#e@4NP@<0vUO z<6C+AA!alcLFkIJX=H`=(EVqf@`JS;lttD&Liit}+Q4^!a*Mv8uE(0DG)(|#tL9}(HKG%Y0+BzISTTfUQ{K z4yG7PWC0+p!?OaUbq?(RKw1w7wnrtE{Re4%sBpnmU3@q)8OnxzZs+4WsP%-d@&!ziOlS#8S=0M@a67lEUl*qMb{O+PktiOBi776 zIY+BPIY8`9NHW&4LH3xw^An132eZvwToX5XsY>Ae`Nf;&_iU*hEB(4 z+gCLP+JM^voqr9)GFmb`K<6K!x<4DYIcC1=cK;kD2Q?YX9x02ZOWK5v3|cj)^Y6!& z9?L(d)YesuuZ6^d!&$e#Gt4MKGOeo1ZQbe=1|F*HY&@@4dXkPEt5$cC2W8e zfvQ4*&unnm$UhCX-TWP3kCTr5te?aG2{9*d6V3$|E70j7E%h^amq zKK9I1r}S34n#$790H_26f2ahH@=0;k&bJ%tPfP-Dl!O!;BjreZ0s?c!wuXdR!uQ_Z zFG8_#94+N&YHE@U{A$T`GiYyWOJ`#`w_LKyG6Si<7USGtt?B%*qGL1-)QApdpCD5m zBF}Ru=MYeca$cd19G@xfREzO7;!(}lXstoW>$@(06qejKP`q4+oJ`af-E|e^wKTSW zYW^G03A;($G9avw$@8wQ7kOM1f=plDC(O$m5#>#m)RlIYH-J3eR)BwFd4W)-bpLCU*v1_CIc`ags}^?w)t ztp8i0bT0Vk5OS{k|916qf2#ijGzMS67vKP^3g4l0(hMYJ|HqAHFo!$=;M9MOgmKI{ za>e@{?$k$(&8LB;M-Gmgus9Jb4(sY^4GX-H*#bYo;-*axeuflY`)RiqW|YAIl)~@- z*n)lrW-|cm8@;iutlzr3w^9=QNz)Jb^hn=lzbT@996h0x+MY~1&>2Ie6C;5D#vve0HNK%M+C%Tu*~b!cp; z)Iqrt@TdI`Wr*9p*!nVMw&T0-0b+D-q3eujmK$~4!?w!dJBMQMaM{hhBSHWuKpWP% z6Ig;Cm&-KmR`R$fHTwTNzmJu@v>&o>hEOfL9D_w`g}IP*s+{zja!3Uwul@ zP=uYT)A^wW&{sE@oovjp=M@G);w(@Bv?&?udVRF zP!bA>8Vmu0nTg)CP8+EF&q*M6kGZ@;?|Fp({l6IDD{ypeU|~8rv)CUZ-R!w2U4|*7=o;k>1 zxOqcG1bY&+w?x;>88-SwdiwPQ8@3TY4_EB|i-EV0*8;|svv1Z`%dK>V0ErD8@){I^ zzSHr#efMuKc|_m~fcKCR_y{sZ0r#(7fbO3jy91Hbn>{ZNXHYPYSGT3^LpLu^BUddl z>YfLpk+qc|4;ScVO5G?Oil`4D>AmA;?8t=w%*RJsZ5=olyBsZ}z>TZhPV(&GZITPJ z;t_yeKae?UB{Ia&7WlrX3sV5B;;F$55xplz5Bhn=m^Tg9pe;z9H*JGAboXXUL#dq{ zJLy8a>M`EuvB`RBEt0VTkvn|YRjaQ$Wnw2r2t0~>f-5efc7N0=PwA~zBEIAMk5r6| z^7tzQ%YBwejfUuAam8yGCi#%KAJU|iz*5zQ<(Z1Vk&c!_YW(J|{{_1*QZazl%q!C|JzZKCF<~#H2ZKEq%qh)P-5X)7;7kTy-8@cCt7JQDqHQ zNeD(4nh_PxX(iQZB$xMY)I#!&c^UVa+K_$sQuik@337kbu?{(Jioevc&wr_7@@AP$ zefdMHTrodnONf8%e1&wt|lmS>Nxsq8^*a^PS|x;wub+FRcyEDne-m^dyy7ln z7wWQ3mADjienfISXNSP5f96zssnkW+p&w(mkGD~)J<-Y3(l#U3bX`P@YRG;(B>H)=w}yqzhLFcJRFIzE)?ziZtcMY^E`mrYgtbxZFp1+my0g=nW3KlOtX}xB)5~orxj!0Ud8m8qFOdv5Yv)M;=xd``6IepMUMG>z}biMlhz!6kc{vfH8KO#uF8 zfsNb*Y%=Q8HO4_kz@(~%r{3FUrwoe00e-%hThA~~(!XVE=wwu&m`$h;z;ObxE`YQN zEF*x+5J-OmFirs70UEQvfFhx}88iZB??rjZ(PsQVIV^z##4C@`r6OoxC(EC{X#JQZ~50QSWY{kJC>?NRv< z$~ed!k*FeaM0^>gU+DJ2Xi);=#Il@wGSbE?l<8#H#&xZbu*emS>#vtBa)&XbN8XyZ z9AUXgzHYF2h}7%Vuv0&^wvNOG@S8Z}zT@q$K+J(i0w@NYio0ZcnR6D7hU2#4fqt}OtOoG^>FAqW$`09}f@{pW}Hq!K^J0DwqWxtoAgR;rN2sx+zerJn>H_!g>VAKBwd_`>#ycA?p7@~j&&>@`7ib8o3uOPdy1>=h zV&@4i=y$Lh@E~B)?=7K}KM(?@!N2MPU0@?1KS2QVdA^f{DaSW&cx zB^IeKq9F_(c!3gl^F{Ip6Qcp8Fzx|sd*$ZM3{YQ;(P>ulb7+SS{x(|G5_Uyi(_~bg z*oVge`jscT|Wwl@0o9RS_Np3ez5tT%epPR+2;oH@C?fYz^B59`!euL{yYpF5%UkymS zTSTM)TP=Mv2Oo{-c;xdwBt~83Ml=pt~_ z^}uM~lT-elV$yCQL)$2Rh7}QeQlgP>aW_<^Bc)*4w09s<bR}N9|iq`;|3IAN9 z7nhgUwArhu9|Wc4F|QvqmgTwI zd&q=2ZUg&^JTBx5GpNV#gOksmGB-Bt^W%wc7h(>uIj$1|EPW%EZzZJ7Q!Fc_GT=q(uNgI?fwMWf-FWIPo;bL*gsuWj z)@{!7uGEyjkyakC7(-Nu-ic^5_bH~elPi0cUC+7d!2gp9iv6H8hc4%`IPD@qIK z6@yQOhTAbk!k%L(DJ;=R973Fn*1mbC@%{eVH;33W?TAX#T|mD07bYz(e1h0yCn#SG z6dGm$hc=j9&p8wlrQ6;0AYMEd{((E6t7!zcyHM(DIW63mo|1`=Le_qHAAOZWF>yRl z9M3(FEApuA`026U>f*L}4f=KUI_jwjwuqH_Obm~@9v>)Ev8YngQ_xh6#y5DKGpyGw zfBn!coyw`b#I~St-mWjmSZjE@iX~>U+A&`F?FYpI7=43!>Q4H+XD8y*6*CBCOqXnZcMo^*BNP{P}_8Y&pz#H7uca7aVKxEmo10 zIH9K7Fj5G(E_tCZI3Qhrig7TK>PFP9P`#)F+2HFBGL=$MXUKgB6v}px z`{UxG_;S%KGh4jN`$cd5{Umvk(L1PKTE+iRLkPdf?Hv&b-72w(9VGwxQx;JREX5!@*}SC zJ*L`de~FZ%lOcCl#E}ymzDBn6+SoG9rnXd>U#%?^{vw%PxziD_PNB-Lrj@pCWO^YM zn98N-xiQCUXG%&Jf7q4pdmn_YMyR$Zo?1GgsAsT!KP((VGbD~}`vSQxH)yaTKRrd) z$WT`|7S)2+H1$TNp5T}r)Cv#{^9`J#zRxamTPf+F;MqZUIhN(oD? z=l(7wq%F-`Lvc$jnDAlTsO3xm103qS)4_ z2~QI-w#ygtZRG2xYoAVnY4Pl^cOqaP>YB}xE5gBXbV#cgAFs>6EPk`$=wF~iaXwsg z3*%KXi7c3Fa!?f31mW)|l#N)1U1&`{7P?9+bbo$faYxM>}Nn~@TfW}`KLMbxs~vr!`#2ooli!8a&us<0vY{r!2s#O-EXZ&qY0FN z4Ev1Vi!T@ftM&pwg;?6Eu$;&Rfh;n7U)nE)RuLho{u&a=Qr?9&Tbk;aE?t5~x^z3D zsc_w9ckO{}jw)YN{N2|Gf|34bR{!`Ixi98wc2qXKm3s913YVt|2Ze0Kcg2mKxEFHd zC-4M6VDUz$d_&HrxJFG8Q+L zBC{Y8NwQ2mS3(qmXPZyk0<7=AX<9f{J{-_*@-<&UNv`szR_EN#Ho2BIfGwKS0hRnN z<10hfF+8ndz0H{331p>PZBu`1o`{ z9WMn$Ap4h^y}X8SKpIsFC;)uH_I&>@|2S{O$EO1-@Dcbse5&{e{0)Hn=f^-JB=p|BOd)R+^{S0ZI>n(OY^4bDr+6rr7pyrRkih4%v>F1P3RlU~{El zb8}BOP2ij+=t5PE1~HNFn}jPrG+9lGW|5j&*`Il&$8ACtF3*hWmd(D8MQ2Qhirpa@ zogE$y*wh_RxYvn~I^Vdxcw~H4Au_vyensl8o$0f&X{ zj6|W!q1foR`oPED4Cf|Qv3tR;sYlOur)_pOh+x2Of-z-od$gx*t*g-XEkv@7_~UfS z!uBaO*Zn}vbvzeRdr}!RZydV=aXmeyQLEv*y}dm{vz{u0iHx?Uh9ARB@DZ<nAac9?A2%Z<9yLD1h-I~=z=2;agScL z?~%^+Ii}rj5R=k`Qb8f%U943ECJK~^l$LqA`{b7Kq7T-0hEFE-TOzt%dG4TV8i_oR z)vRaG_gJ`b8_F5>CU;ijPa6KRo~!vo_cP! z-{0RmhNnR*Fv|rVA>G_A!qxiGT}}Y)gHDWp#-1b2O`}y^M=W>INvn3zUE5RDbiO7x zT@*?0&Rb!!Bp;GqeRb2~=5(QRpb17K5scVfUF{jYnl7F!^*uLA;!N!IxE`g}jDX7J z?kW~qxaSRQAhueGePH)x@|V~L=vS)rUpd~LRoI)<_qkmqTTL^A4LODUa~JmO=xG<$ z&70rLAvbI19Hf8EhSrGaJ!!31j_CWaNa5Nca|`o-k@wa?Sv_pqu7I>Oh;&PLNFz#j zcSuWjcegZ1Bi-E+(jAfl0@4lA4SU_7KK`EfdB6Sc?~iZxo;}P6-Y|pCthMg7uIoIH zLy+YJNy7U%a_SX3DM7EA+gJ9MnON?uDt#%8Uhz~Tj~)bs+7S3;2 z+wqY@)49Z%NygP-!nIm>Ao-w^Rm)ikVnhAK$~WOVt@F)wrZMlHz(j%lZSQ;GC@$>`l?P8C5A5uGKct^+ z;irDhutMMRyr9bJ@4hK3AR=Qa`Ap^9pvntchXV3J$#?sW)4OCw9|RkhX7-O2-Y=rW zX?D#%Av?e;_6bNC#XJeuzzjXwkeDc$+BC_=PYiu-x%6~nH;Z!~?ViGUx7$M)+c5(WSH3)uOPCZq|`aIbN@`BH!KgE8E-KsG< z<;XgdVWg|Ki5_wlbn_tmN8@t}uJTOUesOoEK|$EhTQ~qdvAvQ#<)1E5QiS z-wBXNOS#19pHQe7sH*mjQNPz_#j7B{uheKSn}x)IRo9(Tg6Dqo)MSJKFH?)T?v{R& zU_oi}V}YEmUV{Pa{H+)DtN*z|9l0?^cbX@$IY~P+D^wK9JYGE!I$j;dfVLuLkmqf8o3|monjg+>L zxBgzw9)$Kvi)0ANOh0CR=$&|=My4*`fIB1`zoBdMPe-Rl#y8h*C`?GpdkPt65e~T% zUB!B&HSI1tz5v93I@p2u55ej-k6V`e?|aAigId->EjNeDsXLugu9tf|glyzPZrRB> z2Ji2dIo%kk6dSnbJ-@y0y3|IQ*C&V)rN{OZ_m3woEA7-Gx3gMLJNdbTtqPV-u`W5AHc$}PTTpdm zPaEj&Ti}}gA&8eoQ1+?aq>7O=dcCnjAz?Q$cN@AWr>0mEDEBQ&WK7RNb3ithrSSD8}(5K-BtboynUMBciA&#Zv_QUntqtGG{$I85$2Cw>Y#xe1MF& z+^TV2=Eitn~*I$_Wt%(jB+3Q(n$5Na6wu1MMGJTWq?Ml0*z5qHW#My^_VHUn-mHm*^l(9SH` z<-kft7U+5xP9c>rZI#k5nM1x3EhrCy?22+z17q(`hMFZQVas?qrBZ^TpTK1*?-d)G zLW_zVm9=Z?O3#3s-wPX`_4=2Gs%;QG-|v-Qpi|K$*JI5OFDz0v3|Bem2>%cpzDi9F zr)IrZy9ZQ+)7Th1wH)1pFz`LfQDxcVo}l~RiLMV9VlW4{A0`_;r$|_2tPK61mUhD_ z&XN*d<~)Nl6W&&X45T450#|&4z9+{Go2JBV(={LDhNS^Uv?l3VE1b~7z)s#ulix+= zLyt7TK{V<7jlWpJ0Kc|#%W8ftn$OGH98en7Gx-$lNB8!xHjB$Cw|tQm<`yTV5c#S^ zgn#?q?lXntsZ}3EFkjIJOqrt9s@tId< zWpuXQ*=_aFP_%xt$)W7Foq8z73b;|GBw2ENOGJsuGB275+T>ngFs&ht!9pudc7-bL z(7bS3JCKHY@n#IeTZoW_+C62U+50MaPix1aU_Y)TPc3_kx!WYuwcFg=buN9E;pIs~ z3FdMBM8)^Um>1BY<4c;4W20?`y;cWmkeIM!&f&)KIk!#-sx~QcYA^Q%(&XYl7Np%( zSCj|XQnik3H(sk34J#XcyB-qRjDr=vy9&v>t=~Ky@{Q4K-1M;nM~uj)1*XFj0`ofA zdDyguH!W8;gHlqYv~YR3201uOJ6o@A<;I(F_!NKI23p!pusG%$aB^|fR7WUlvoz(7 zn~XA=)a~Pal^DosZ5S-&K9?dH<{79jmVV#xdf=4?C#HgaCGJ4eRM{BypJH=W_7tet zOedfr>S4&}QeG3aOCNV0CO32P;D5`} zUo>~NNL{x)f~3CH0LKSt78pzj!KdJswjS5!5tLH$LDv9j&4d$;YIVLzL z#&%x+6A%9%nbVXM-$X1wX;YwiTokk@2fomlWISZ}xmq)0OUJYYvDvA61+gpQbc`qA zSxpLN>A$)K1|R?G7MxarJ$v%jY!c2?7?WiXkvp8htVu&8HAc@T0$cx5|OT#($Q8*k3P28Xm|MH9ppF zyE6{+N!-GSF71GXDfHBV!7nR(fQTGLKRnN0~gG?VMb-8pw_Awde2L$t!=PI zGWTL=rEP1qtjE)kUYgw7JcV<(?>o!>&G`5V?Cn7}$AuFBVd1~$O2kc1>BZKv4nA@? zJCSohSkU8_k*I4Y_X(8`RmKUCT_QSk@wG@gx+JWy&3sYAgmfDhyAg#((??E5CGr(a z>!#&xR-lu0^%TQl%>umGmuW`V!%fNnkJV+q-%?nPqteuA-X3F4FX805dC?$p z`^6B+ADdiqdZJK?4(f*F2ef*EcBUsXf8HZwk7p5Vk}cdojb$nkzmVP3bb%mspjDs{ zHTrDDPfLur(-LSb6FIOHoAUWLQ^0oQ`UQ7=K_4wroO6Y2#xC>91t02L6-_^5nt_Td zW79|?qwZs&@!V3uu1#qYL!_e2ixWSq$>A+Bp<|DKnLbtwqYErYN5j7P@NPXi3Lm@6 z|1aqm083Pm!Vp-E&MobM5H1d2Yj1r*=Xw#K;S4-MM?4(_cBU>p0FOP}4#Z;@ z4W?FxMHiK%&h>inHrqlo(`?!gmXc@N_GNw+PJt-q7#8#gvE?Yki#OO=b@{dLOU>RM zKOd4dZs*HB6XB{4oaV8FzE|jO z%)U*M&;I+5Ed4y`pk=8(_T#cNQDCPU_hDK3Zv6@KKbEBzVvoyGp$p8be%~?F$pD70 zVxbz=tbMO9+^gA9LYPdVhEwdrzr4{zj1;4SSz{sAwbUk-Am+)iT&meKCqkmsG{esq zhmn6RB!^18|Dh?DG9cCfyuWo)@;q4sf7>?1ik*b~PyQ>Ly4Rp4ZeW`xYC>Tb*LvT& zm4JA7zKGrNxa(VM8%Jz;;LrceRi>L2v-ZHnN;<^ z!m{dnYENRkSW-@aC_yz;RUrMAvajslng`M;znTZs02bs|^I-hc{;_$0T=f5?=E3U6 z|DEQ+cl>{Lm~8|46&VVHfu04awd3&rsn)>)lXs_Dgg;C+y#Q3sct0Lesivf7PIr*; zv-*ujp63t?&X{D3n<*a)Y)TvCIKxNi6P_d35IqYq%e+?mYRWchkMPwa7Du`FGwDde zL{(gHHN7^j79Wvts{#VIs8Ia6bE=KvLC9@}9SMr`Myh5OO8vI?Pw`F>1}t`p5I)gT{qxuotgJJg3oZ z7ipO5Q>O~;=-J@sQ}z{Ge~2Dv4I#HphPvMK2@fkP{RQ_I&faMCJ57lHi4C=@wc0_|!*^@PhPc0I{mRR&c2V31*&TyA<>tzhT zdisF{XI$V9_Db{KTB5lCGn`6uJtq=#vbg|(>F1=4v8$i3N8%~_C0Rf66r~H~r&5@( z!^={GO}=}n_feH5TQn~8btZf;-QHVH#x?q9z864JlK#2u7a3X{vKG<3o8$>^kNsZF zB7zA(O!#jh0=p)Yo$kjFfu(ByTO9$wC#$k@^&dh6A!xwx@b3@-$?e~_PYS%hLj(ev zzd{7=|2afp^B5v<&1UR5_$x%P6?>VM^%x>xf{x<&_YlFw;C~Detl|y=wk0)NU|1{D zv{6!&IAvBVJ_nhEU%S3D8fwJ7FLR?pKW{rM(-7jq`jyC0m3n{J84v$jjsVD@Sx`5n zB6o1SaRP>v9Ex>d-Stp#q!4`_f;K@K)aCnq7>t-gua}+y5a(RYAKKkGBC-1cFDO?{ZYBp2xDnG{#Dd9rI;omc9 zipKnT6h~1ZHuVEE@KW{t*3wVIba3gSjDSWP$Tw9*I2t~u7`27K#Y6roensXjt^UPQ z96C>VNIA|D$;o*uW+#$rRJESN~_Ej zrQIA9vWjW}H{ryCn=rS28IoZfM_G06D5Yr(vcS!KXc~V<#St&GNLhyix<2K=zOfHl zWoW6`@a8DFSF`!_<$*FV8mlR}WQyF(I>CeQ{)#f88p#&Ss7rfY5#QCeNm@+z!hhI2 zp1@&0i`wuU;K7kf+%TryOY&&+D>>J=dz3dj zP#8J*UK8G|b9=jYMuq&;aE_Z`KSxh7^2@Qbi7pLZdz}WP4*i!dx?}=WvGE~89~pPu zUz`en{v3=)y zE#;f@&pC80gstwUM!NBE2J79l&a1*)))wSB>Wx6$rI}|j)~1$d0Z~tw zwyiZZxF8*bwpQTZk)~Y_6K$L*%b(+$K2dbDxm5ZyR(c7Hm8L8oi?{(|_H+?>D^7V@{ov%I33vvSqZI zmobH3{NqtMlm%XOL{RHPHUaCNEj)k4?S79e66)rQ0{}1A_ zr>w~iO1m=W#iMl?9_cS{wT296a=yqkizsY6l%Yg4!@(6MjdxmCR2Y0$oVuL;y_g(2 zR0s8XwHxajT^uo{p1I%d>!o}i$Gx&xF=alhZ2~#~rv>kOKxV=nj77d5137_w@s~g>Hes z2QX@m_mp!Gt3EBK^0eD$Pz@T?8(gNwa-~(%$#)0+a9WcXPoG_k38tXQrP5({_S>b< zVao|Y>x>~*gjS1M&t`Xre;pXJ?%^~DRTYGci0|obkI_=qvj%5w!Rp^C4tF+b=|@`p z8K-=?#1t`Q^2v(UXW50bsaN63TL#n}z=H<51HQW+z5;A!PBY~J$8tkC;U@Zq$Ze%K zZ5A)b{oUm5mDChN_*I9q;<$aC+I{)zkORMr%bI%XjIN~L$DHVxW_cl=|27eFP}5Gk z66jJ|N7d>3|27f+jl5~hG0~~zVVf)YA+-G4L}-a+@b@GkqX7~?6#UJ)5#jrrb@N~% z1a(un!I434gncp19`L_s2!E&PR)YzEr*gfZFF^-U-2`wNzb6Q!t+G3x|I_54PZ*cv ze{Xa^4f|FJe4$*8cviO*#ihxx2HR%CBLP;wQ}zRnAJ0$9n&PJ@lw{T?br0Ll&}uaY zhl0=9sda9T~moGI>vGH);k*INah-rl?SQ9!zvTw)aS2NrrWb$tIW**W|gVT z`d?O=su&cYNRKjyKD=8M?H{sP&Jc+CS1zYPoeKkk{#6y|VAKWCx4(!2CSnoN-1k4) z#^yaOFri;I@TpLJ1`r*Icg^}&AVkM%8(gnTTeVT(cn79vVtGU`i;_`TbFT!5({XDQ znDjpYb(}UvU>%luv`%a?`NtXEdd1aWw{*N}Ow}|em%`t_={?YOwk9Qn!pn8o8S`hQ zjksHPiI^R&b(32o3{o|yJ)p#Cll2dLW6Llywzpw_{@m^_5S)N133u0jZWC< zb{GT=5U7Kp>Jij&jbL7qNOzd34Q{CG0hH3|eb&-(-$0|*U(c?EuT-*X*Ingy;}ak) zxcc$B5=`Cc&H4*4=tP;!*#vpKt^U8|bR4>0_RqnYa7@!Z=v6K~<5*VU0*$ful=Bb$)79dm`fIzqvL`DP!(5OI#9 zj7TbN)d`DC(uz6(y|0`}g*8AhLkp>8hI-rlveH7M;K#Lxw^6yBK^qy_eWs1rtF{#BC4T4Ka z0m;NR;yCe`iA^r|V&jtIdZ&0>2S?ksry64(g@-Uf)Nq0yMNVgbeDsi**c=Bj+9-)uB}p=xQT)XwpR0LU&u+M=|f3!uG@aBt*^?lk-sG} zR(kEBLW3T#Teb1Yl9vHW0xJrjl7RCe{PS=Nr{(u0I?JZbtu~laKf7HnVx0E50#lgJ z=;vLF_0E>72I|g`Z08w`wV^9GjMqtcVATWG=}S9aNnJis#dU%qf1CSq_{jbav=|zY z5t>^RE|?aea-qnFN(nAzmfe7*)0?DEO`OYXt{vfOsxF%A>weD6Wp#%?WO<h6nN40k3e4A4mO4N++k@??*_VT&=6JGd6 zN9u@u%rvIjeREa)u}{g)?p~z$SS}}h_=@?L829^-hM%oANwvw1(F+1Jq?xMeJutzT=|r&Do9RP3&EoP zlOpV!nYFteCDNh+7@)R&@DW0A!vVwTPd~UJRh~Te5>&$nUVZ4{aKizo6W}8R70x2S zL7f3%Fd%^q(hNR%k@97e>n|%IMl#4sSO?fWfp6nO0v5zM^T0ZY2yid&=(0J;U+$|V zVk~aB$8*M47;F=d%cU)A)SbI3QkbaJj6_DDqH6hUi&!{#wx&P7sAOmA8RHvWaM~E9 z*b#b1yIKdG%UFEYd}{Qm00II7f0y_mlNIB1ip;Udt3Hf2zBr!5mZ?jOqyml5=-UwS zGjb?})-gT80?^#+!;1!{yUFq>nx#cjzSl_9mI+*nhv=RoRF~8a1^{IvZdZNdDTRO3 zg`D?uKuKsN6_SF6C~AD)Yv4#IF5?~>y#SH4B>ujs(bLH9S$O2LpRT-MnSrxz5MtCf zp+^`L2C$Kd+QZ8~Pbr~YWTsp1lT(mcz2B)cLGD9cD(iae`^h!nI?xh@PD6Y16B){= z{DZS0`Zs5ze^jVeh>5(srp?xVE20r-goiEq6x4dJD{}R{I^nuk;l*>8R4oz-B0eO> zk$=zH7LNNtVZ6KYvt;%goMA53M0^vvf}B+~i1w~t1EK_fjpsJ1B#u&ZR{H{U@D0WM z*-6-aaP|my-DrMrM})U|xeIB}gh?)y6tUzC!giZ}S|r6b2Vyl{Dg3ZU)9W=@`zLv$ z%omt{rA@Tw#xwnyf62RAKhD20y(QfA7yS`WV%|mucc=*)ln|l?&LEDO@xSENtrA3t z%i%^Re+tMj0Z$-+#lGl+Sz`cI16KYVd4PKG;mFemIh4Tw{~&PWC9;E#JPBOj$jcs) z0%H9PwFoZ`6(<&=J&WR@nGSgyf3s7c=`Vkq^A2rqM$=ygelw8P$7!g8NM!c{Tj<#H z`E%KR#$^2nc*!vt25WwO;59l>Sp6Qb8Kid2+pVm4|Q z$fhgT>EV0l=3~k287OXI^#~X2Aq2s`hLP4lx#&LZ%n{TZ-25(;$76x11wFw-?}8S0 z>?$wrV7}K_s`@R$rW3A#dX$s0GDq5dN!C3oxj4;d2JrNWb0lW@DN?rqV})6(Dk}1|zV^cX>f^^?kQ@vX2X>%KC0|-8 zc1L&?WHa?dO5W_9)NvnpH)BrIenL-sr@`dM8D$I>GJ9Qc;?dH+!7oH*(Kc^Hgk-dn)rA~W~g%XXi>wvW@p#k8AdVwlQ%CjXyR~CJ2f@+ zmiw3xbi+T7Finm$pVQfrMIV$>6U@{uJ?)7ye_M+%!^V!TKX>J#^)TB?=m*q| zV(Mu~sKc48F%z)^`T4`@#Hs-_ikQURF|u+WgX9wlaK$r2W8hN{x`^SpynBJ8Jsa`R zRv186kg$Nz6>!A14{T7*O5D0$RHQOYp9hwkHVBOvB&(Z^0QWK|0CWj=s`tD3aAL9Kq|tNf2jz8%Q5g9A8uj9 zCleqZ8nX#;DqmmgFBBlbQE%tqA)E@JSo*%J;?uCzi77bqFWFXZ>kh`iz@Sbx;mgy| zyRo8JzZT}S9+#*})!YM3YDhompm<)b6%mv(sV;i%njG-#NS)*7Nz0ALqdPrY`<;4< z!HntaTQ#N#DXkUFL1ztK6`Eq0|J1xln|qg~w|id!)7#;g&cmF#Yr@+wo);6?7eO%g zxxIVP+8mx}(`-Iio^9}D{dRg9q_@TQ(*7?@S^YF~91Rt?fl}r~(9mVso8F2t^DqRT zTJM`P#0E%nfP9vSJ8A5!+)2k+WQ+V9g7PM<|AZa6;#1D)`ci;JA*x{_5KMT6c_y#R zBRpc*!&#NK)*8zz5eDnZe)@d4n9Vry>KLu3GQCt4BqHQ8dJqvlM6u9gO;4PF3u~X!rJrO!;)~SI)SW%js4u%HAfTXL2fHk%YzL< znV+}hLd8zG`nYf75I$Ne6$i?*j4bC~nqy~-XBd!Ap+*Cu*Vg5^`Ff&{-x)QFL?WoxO{ z_Q%9+2MxUOBi8Av&6nA8AQ9n1Al-!_4-gTWKZpoJ#OQJeJiSLw)K%aWtRVEH#9IxN z3SvrOlhxq-&GhV@Mio%vt;551OvJbftKX7;R20O>d1ut*p81wq+j1rtb)~7OIsoi& zq2`NM7kL!{sbYstm5hL&tUoFTam@$O5uvw)N?XRAmTf))66oN|w$F$ek%QYRCah?- zn|#m{RbI`VC#`GRwoj}RxS9_ca`XEzr4RE0eM~je!*PZ61n#oS%8rWgKGidzAPc8?88ex(Mcw}8c`Gv3PrG`j*Ae48SOWhjD%-{N< zmcTj=lm#W7ccpL zEG2CDOG+qT(eNN8x4VUV(!!pvb!)#6VqimYAE9U) zgEzMVsbdq2vY5a3RmQb$NX3zqsw$*8V}z+~qu|OX04H5N;5t?76Fga$ryzV|{Ozjz zRA*=BNaDKiQA&spUdcr^w>b!s5>oz=5-LZA{VgQ~{k4elkCc!aBqg+bloCcc=N&#u z2?O;?025#pUdBx3#ca@jCnbdY6O8zuNeQjMvktn(0NG$V=8t3W3kKj;)C=Jq#5V|V zwve(<7iukXfUt1c6!iZJci4G*$O?!S=~Bb@ZeDoc++c;2wdH_55EgQPU)Kcsn5}QZ z23`(t3pWqOd3#v1pUOD!JO_{Nw6_scWjI`==-1WoYPqAQ$nidXusIPSG1%XjtD)Ti zSov#rdm6~K{NqaYzl_vQ_-#eD=Dy0Tw>=|_x$Lai&HF5BVTldIh;R0?hLy%s0z@ka zSgCOI$ur&`P``euexdS-tbVSsw@8GHbEeTcnHyWxx|?*kFMN&Nt^nw;?TCiptB}17 ze~hOVmWSO6p)yl`|23%xOoWsm6QMXG_dLi%cox;@ZQQ-^K;$X=?vEyse#3>*dDrgY za;1Ot4wpdcL)y@X)87HrU`8FJJYwCulY!}M{_T;98S-I^#;T5D4=LPn&yCTSC|1kA zU_AK*Di1K8)*~3m6^>5>4Uj_K7_9>(;M{}d5+fkcEBlg#$6gCf=1%rDh!U-FhO zs6Tbt9yZ`@si{wA4^IWImkh!fXQ6(|;S_h?r&{GJU!2>VS$CVVdHjO3gSZ-qnU_&! z*+8b&0^-B*JJ1qD$ZDOv_2omes2t5E+=A+RpIqtlmKW%s=$JP%>q=@cC;nU>{m#F# zE7e(9Ujib+ozW#xrA&ovFC~n!rs6&j94VJNzD5o~=H0V6`u)qS_x77zf{V2VPzyWg zscpUFguK(FHpi9sN@x#;W3_4 zB)#OidM?z>Yp8M415PCgSJb;wTF-PR8u5%5-4?29#X6bi*wme+IS_PQk%3j%2lNW;r zXgR+<5*M2WGOOy?Y}iw3pv5DSgTso>F3(@Hwr9=W-CsbU(&se-yGV<8KCZ^?&#YF;Jp3iArS* zgiQwQSR$-3HMnOcExx~I?rWma{r6ak(^fgIe!yw0F6;JodJ0(bnz^I z0_GoCR!fM#vaC()!e}0Ry-VdE+3|nmM87wNu`uj#Y7AnV3-RbbvviI!m~%RuST}>h z(2U!rG2nIE6PNA#{0%PNsY>-oM;UJ^bNHj>aJ$Ht-z=iaFxZK9Exc>wnyrVp&b{vo z2A~ghE-)~9(`A2wsLUCARBd2`k|u>hzGyl*9g6v>u~XwdL{_P1)3*Q{te~64RdRh| zd5E|0<(G`HS`$_@0Gq4hyKEQVwUOn(Ru43yc1igt?21#t~G-FWVci+lkd(aPm_x12S{sh?)qE!m2L6%t7R zUXsizo>-4jF7~|RxSjrn?PbUOnwfHzXU2V@>lw*unNr(UkSA&^tb|eyH3H7!&Q$54 za$4V0DP62ST}0d4%2w=}?G(YBZcTf4AN4nr&)eeiq@k#@WD^+-u9bDjS=UN2;9lhW zB5L!l%qfn&P^eX!K}8@*AFF(oAffqLjW?x7B8NGMNrpF0>U)0XnmpfX^YxPRCBXdc{N{a%9smjX8`H-@lHb{CrU? z{QgrK5%tU8ftUut;t(_Obr0P0Gg=bAb2*9;F9n3mhALr4@A^tET_<027D-|>$hZca zE-s=t$E<(~rg|LziDR#~>LNTOr`%T)wl!Y&gOWT`OQ5XqF!MK_dcy=)SqbApNzK}@ zAthK_uQA7kq^}xZxuAR)%?ey0sj?c|53Tyjt zH4sp$nYuxd>?@^eX_1>l*~l{-` z(}@WyL(YVB_xAK037=;E7AVlYi@x^FF(?KIKE#x+eT-&}Q?5?O-e$YdJ>wlK`mi8m zcJ>S5w*Ln?)$dNHfJ^Z7I!A~n2ILa#c*VKx;ILl1ziU>GDsZ$y=_;8wnW6V+;5iYT zg3&N+N^{La*+|QgnO6^NzEC$Wqx#KYvMhT7LP@u3;$!Rg{1l;OSv%&E z3uHvxkQs81qptrsDd7DRH3>7(Rv4Ju=jk6_BZlM4cYQ6iXr;?X)b81Rfcf}i`CRxAV!+_*b$wmN7GWVPl!d5!NQN{Dj49 zn6?E5UHEY1dUhHqsCETKHMq!%3%u;c{7<}DV;BmSK06mH9PyO7g=KClI>&ZCnaPf8 z?Wd)clhWcaP0kOCep1MuWiUaf3m|#|O}9}jhiG-&SQW$~JCdHJ#qLQ3BeBc@Y#x=J z{9+7lN`^VGBw8mqpaEIW+ZY>oQ0WCwu(&CKq5C7KN5<;6dI|}66|~g?P(7d#kTe{i zC;{@M09_u)k^=2h5NhYiGpTncsnbunDSyZUV|kzeIaUO`-r{}mbs10svV)X>z)u4N zpX|pOyfhpzgC7RX;2|E1oLGQw@L}i!aC#nQ@G)bc89X^zsRo(oPnm?ZY=L+Y#(I5Z zDiI`wLxgO*LUE2!I2}n>4LmW*cM36!1 z9BCCsv}m=|fUL>ZvN)t6B7^N04obj1)02Hmk}&UGCU9yj?1J<@lB-J#g2_=s4c~8YO9~fjdeTO;3r{11u+4}IhYKBI>6cq$7K(P0uKYe)(NU+m9AO6VyWQtiLLElZ z{(|?!{sr#|eT4U@DE$8f-c$JQKfrr#iT@A7d(P{>yEWdZa0l=z251*#*D^G*2L^*L zpcFLh_uPaGiTb>n;gGCR=FHQG`q{k^D-NG?gVeD>yNN=sSP+L05nTuhrzSaS8rOn^ zI?)JyF;7p;0HMyM=}bI_E@fs$_cwX9jIJTYX6iFvIRdJ*!UHVkP)==%p*%T50M7%@ zi7^V`u0=S3eZcDw&TJ#sdY|!0jITpT{NYs?(#74OX_O3U1?%;M#O~Laj-f!1z5=b>I9f?3L`~z_S#qGn&-B$Mc7Lt1gZrJ}15sm7vt&ZfW~< zYw#-FCX$D_$_{ez>0d%Y<6*6uuC;fysmZhLHPw7AjXd!@%l%30HyXP#Kg{)W;`%3{UjjU)b{Sl6E=DfVqmRLYYP-t{V#}t zj5iPrweTQ_=SP@}_9mi)4?6S`Oq!LB{ca;))t$Nf#LH8C_i%a5*#B|{ibG^?0AdgX zH%AXAWu)D&AbBzv-Ex(+4Wc0+PcGpo<-*K&Kb@MCFl7tnb)D{*a{D*p)y(U^PMV3| z#47>cN8;7;ypFBs`U97k!Tig_t%R)xt z`~!4%K6D7leC{k;g=}&o6Dy8pWltX26P_ZhEn^BDbZuEs><}1Oia4c#v0HIaY;ft2 z8Uf9_CS>4^eVxbzk&20Tg)U%jZx0|3Arh(SqdFzN@uG50dVKIunOeQOgHHXprLXKN zo@dq=L#28jb>(NAn?`ZF5_d;iyv*Z%bRd9y4S&7071QTy?Z(c*$-(=TTzbW*LrWar zgYEwSg}SivUli()iC+}z*k2Ut9e_e@DoCqJw6jT>zMs_4LGw+_*%fv`i)~V}MOEK5 zh7WGlVK$A?3eU30Ww|N~TeIf&5FxkraM?tir3>T0D{-No$}}NwEsjoP4acnIOr@uB zM@8cc+yKXyHMg>HN!~9eh(@d{=ZipTyX%#6*VUa~f%#B^w*Wy-nJUAF4;jPD)uJC! zOlKl$Xg&y&N7=W%wgV zm`qfdNm?+9M>fU=7z6v}^%7&Zaq~~V0i|mUFHq?^P#FJ%u*G&MyqTN2S<^6Ul-sSK zFgILmK|MYkko$R}3Eud)jL3a2hUC$RcYLGTvhFe!zP5Q*x-R_8>%?+ZM~Zvr1N&OT z`Wi;D$O{szuzaYQ?IT014iyRh2+VI&ljOI3p5s z1A8L}$@piqmb1|YlcF^VvN|0D6rZRqI=Acj)GT_yizb(Fmb+K2qq5d}1jft?{OO17 zr%5X65vRSxAsY&)HlocVc`wE)e2#isChFrWUaU!+n~D}f^F8^-@2SaC`{u_*aQl{s6YfDD)HYlzCq-W2uvaSv6XY( z3}jS!_&il@&R#4G6qB=-gcrGU1RV=nIrEn;bWpEY76|6skeY@`-!1etr`-gi&ZADO_~y8 z_+ltPlnRW+dv~uN%lO_wLcw#IYj&GIpT5!;}{_!lb_*2R9XMf zM@vEQ!L01GfF}{>iJq+>@Ac%%rkj_mqazj6yiP<){1z3`rOVSLE_)hVAkRu^ZJcdr zNUc!yVYIk(jOgWYs(pH1ijQ|DzShV&F_;MyCrQopR59F%Kf#Wu<+lO%7TOwiArToV zva9I)!K--K?imJJ0KpkhSS0_ILmw>T8-e0$?$(x;MnnL6{rL)gW1l5(-`wqNzE}_c z2Gt@x#BetS_d;m6k<`CADtkEhQ>a`k4&`clPwnZE4O6{3?5u=b4MWG3coz{T>+afh zXwd_V$L1y5G{lu)osu9W6Vt<6qrW70v^I>FqlXex->X;j$JAtT`DX8#R;BoD_ZP+F zahNtTh-XR~j|L2Cn|QYR^yw3NEKG|^e|+(xKW#T2-JTHl6sm14NXV@ufnzFQf4MiV zLooId09#?d5~3Hs#@mT4F~XSSR-+|>S9WRluTWLz?!gRciLL#f??^ zveXql2H<;=HRlsBHQ0J$r*2~dZYF;oDI*v~CyDg2#PG;@e4$JJZcKrd_0n?ebiee7 z1y>YFA5%d3NHLq@q6+Nrll@i7j5fC2<;Fc#^S4uc>p%b>i#ffzdDu53ynN8|6u_J8&*!{K<-b*2%}n60 zXn~7@S;=qZ=rwNI$uAYx=kilR!VGX|;LY@Oc54X*7)U$`5=q-VRRGrMY}%~6t&eT*T@Wrh;vBe@21Rh=eIqgl!~BqCr?b) z=>BlBU4s)Xu1`xe_?^b1yKK=VQc8Goob=eJSNy%gy_(U$l8v&W=@1^WuUSeeiM2nv z!ah%qE4KGI0F9Eq$3{sdoP+U$?#14 zDDt}cC-Pb~T5tT-#yj4!e&U6KJ`+Cm`CY=vX!Ts+DSNY66bYfR$?{TvoD1BxLM7CHPQkE?QiCeOOX-dBT+oiKuQXI6#|qQ zi9^{p1txJlQ!c+8MBKnPh`oZUL7z#VfPRed3Cy3b0g&%j4I11L*o)6l&*C5t0tf+~ z_SFk9nR0l>;;(&p0Aw4G?KWKq@$(kXJ0fD%z#Zj0ZE|ECPZ~NC-A2y=YFop(@;8|7%D}QGcTP{E5M%tMiAxgOPmbFz;iKtj1-zR0CSYCD*6&zPE5FLb zq$I@^xqBTWm}{>EevrFKXMM#WB(kC5llYqfC3+5af&@&}en$Hu<4R|kHUV)UBN zN7FG&OQ6TpQfO)CBu;|jy^dyOis2oDUlrONQ)$znsgu9-&a{G#f_<(oT@dmHNW%IH z07+N_O<=}$aXJ)p#!!5DeBV2s_G{1OXH}d;HN(Yr^Y;#82TgqXfPu5D$KBG zai6PfhA#YU>eKTHwN&;QvkbtuyKzYXQ>a)$5qjwT&0)Z;EXEOWW5$SrynKX{sRisx z(I+_{jaky4*}FDkZW%L5z$i>zc&+4zsP`P=s5#(&)2-_|{Tj#ZmH-Kavw-OtaH0XM zY9N&8bNbsH47~bqmI1G$dOdZL1JUTYDM7i!G<4A02ryOydk4xMG`$3VGy>cS9l2vW zUJD>|1J7R(&li7)w#~Y4Sbe2Fiqx{sCG9p(%?66$K@LF23 z68EvS6J(PHJ%tkf+TIqBN#VsN;XOuW3SG9i`y?uaX(|mHT8An>d$khkdRxX;^;BLm+^7Rv0vDFN7kh;FltWyGOHzq?D2MG;aM&cG z1HS=X(d9{u4=$FC>R3au-FBZgHq_y*_JCGUPn!*%(wMQaJVDB60CynU>cM!cyfk2= zbE!f}&HN8Gx&_XsmhO5%{Mf7$5j%{MFn8y)p88yybFMz;X6TbKN{(Z&6; z(fxTRXd3yKjm{s~$?%`*0QWj7WvTeD;~`%^L9XDQ!I=S$SVlP!0oB#s{4WoRE7QCj zh+M`edUakX+JfQE4kJSx4R+65jia|haOKxKA{Y8sM74Y*aP^mibUK1{xRob(GND() zMhUQgphLF+-Pg~@U7!D@0x1ihBdJ(48x5t!e?`$!oM6$AUGyDtMS(3XEH??t83O-$ zo!~3tR}1pYROA8n4?mxTMI9&YgNIoopo>67|B+dl>e3-7`GEgYi>empl8(ZkfMiy1 zXds!@I!I>qqd!cBrwD-ss;ftzx-`Drkuo~ol<7D~TZXaTh=mx)>N0;Uo6MJfhd7*+ z`%xC%wmbG}!3##%@8<-swgg7)>ZWcz*KEY_sgE8110Q>b(=tqXx=Zp}7F&ZTHJtli zd_)Lfn7$0C6(%1i*-pUAGNeou>6Aa^(KD3A)Xuq7=>I`#p*>pQB?Sp4$dr*E3MDr| z#G{T$p;DoVHh=SCLZ$35Wzy*@GvqMyByIwD)Hbr_$B^{WHdg%jEzBLoS6ojfaoDzI z^09Oix{X!Q8G25FzWH-O9sguzX-~+EH57_<%MmTama@fUQt3_ax2{-VVTW!mStr@L z7f|UNAa3R=<>GGMN~FkxPcN3D)ZDJ9p!gJol4~a_r{$3}CrERADi{IfcO~-J0k10; zeT|5SI6II=*>?hy`y)x}sFyU&B`HZZY0{KRoweLgxWrD`vX+1+xhpWy>wTnkvqOus z5g?I-VoiL8ed(T{V^D`ZT(QE94YI&JI^ar&r#VHeh}v?v+a@*AgxQP6oP3J?Kg(nq zLbQ*?3bpHL1u6-aavxyGmSh#P&r z$pZh*h?)yDZR=Sr2bxi!bbxk5}&?VO(mO4s7} z5&qY6W!oN1=+(j@-+8zG zXy}Fa#x^_99I7^~0_ot~lFRt%Yn{GlTvTc`u0@yHOxMfJI58!eV6P&8ba0t?ne$Qt zT!0P^_ZO-f@?WZwM^yE6rv~=lsOm29f1;|d0Xc759M&fnB^-xmA^4KQNvwbl&cAjP zq=P$62I=5F(b$1>a8tg2ba2aYyZ*02{cn4F7))3OaoMO~3K$;w0Zq zZv#0)>Q=3^3eIX8dt0|9{RSX}qqRTgIIKdryP*WN#CaZD;+x2ER)|%E%(IZ>VcU>` zuNAfTLuhc6-0*$*jXL!Vc$hzBeTgnsb2zEi=5~#TA66O|gc~%kHl)_M6~8pBr$Dq( zct3(wA77_Pdw3}uo;!PY0hMdsR6mQDSg(up(Kx(F%E4JpYs*VvALN5$pTYzA;9d-a zA#OmYN*#d-*|KM09eAQO?1WoZc3mCX=9mu+y$0xs=Fd~INb;*}XIDR>=*>h_(C|h` zNlif_W!h>F@Odw|D7f_PC5b10srkQ%I}4yH*Z)M~~-se)rD*&fJ+hduF=@-7_%nexK)A-?ct#NkzMasem} zrsmZ6nE(oibASTkY6!mq;&IHA`z^&1Z?eS%=0hDG0^-JI0I9n4fmDs_@E-9mQnl@G zQuW&hQZ;-pqODmY9q(ul>cG#9wL8yBZ^EdsNY)9h#QsQC9~xs44Km|_v1^5iHSG=s z-eT%}aip!S89lyOw$jxrPIfKmfkn8Se7@IC>ue3CaKf+KD_4A1?i#)*=M~8R1j%n7 z_u14VkJ9Ikkp#$$xd*I5=c=Z`igPx`L%wY#^k3S^&Mwl%um8QL?*Fl{2KP`7*Udi@ z2RjHwzGT00-h=+2#QppNC~-B9N}S#{pu~+f14J@QbKRX5h{caRb%NoS_k9O3qUBUEITEjU@^HU8$aK0t|cK-mMO#koLA zoSZ7auP(0n`ZGPU{Z+kkcW&g)1H?KRNQ-+&6Jd#tb^K0?JNHB{&Jw(;N?AodjCcG6 zvNrw;WWAsafUJXmgRDCMkaZz30J2^fJ1|^ON=z1HoeaB#;VFT5yT_0hp<)3Rvq6h; zS?L%kGF6A1Q4xmK!+1;eT6VmsGgF!a>z#h<+^DgZ()NpitpY*h>N)A3&Gdrku0Lz2 z)w^B4lv)hY?Uc_lvd)Q1OO~&id-Ld(i=WKCxU=F7%*|^ME&nipUkxZ{NEbI(S6gfV z#d>c`4ipw|s67M~#yK2_)wxh5lv`UZVY6;WqMivxvL*V`ir$)Kg~Q;_PW1KlESZrS zycQX6h25q@%YS1u)pw4elpm~@1hXz~QUZArWL6v~tD^%}qE}}JlL$uAg7{?rq(CRs z%uIC0Y)C$VVbjE6K5A2xR-pfo(@s=jvqnK(Qh3u>ZBONS9JF&jlB8e0WXMUE^>yM) zLn^0qf_zO3N(B)OoT#(SG6cgYK8s=ExMf)xfv_&?_IjRRr_WEFG6F-V>yNdGN8QEVJ`o0#CSmRFU#~U9k z^4H@HCpDmbK53vKvP9zC{V{{;OWMj!(-0Ok&@Sg}kj$xn76+kzciAFJS5xx``Fd8- zuwM8Yaoa7((%`QWFu+_&b^G^R%8&dPG=&y4m&*GJTKk$lIog8(YQhtuNOGrJbij8Y2pvEX zgoE|iyrQ#PWwQp@3IE87C;eIwAJNx87?}^8sKdw|u&T4^!PZ&;^z}AVx*cQt~BSMHj%de=5AbEZr-`enel_0O)H^ zBmqz&yiKUVBz3c9XgdA6?%L9CM3BxrI_CBFSeTIE+h>KDio^I+^qZo(LiW)GOO~6G z-`b~VZ0wDAV-x~S&7{X}u*MX>X&L$Wif)k4#3ylgt z!~vD}dhYd*A7ZIsUYtB@dd_Q(>v>IN+y~l`hA=}2XiL)=>&WOf!Ut9G9ZW3SQy1ud zGlkr!$%|zI4x8}KqzL&m0MQ0uSOe8ekI&L&zkMI$6_1h2@d{Ag8So4oHh;Yas?R{L zVV`)RV7io=fvDCwUPf>DGNmnJVayr_H=rpX%$uW#At=$_7J^R{(77r`S(b4dZc> zc%NYs-k&f+5U@cN=XtT?F0k5=`I{_!&m;E0V_#!9Z7-J8S4~U!Z67nI z4hBiuE2syhIDBRT6=H71K8#vw1TI_FNA|2oFe2+s(RA|C&!Lb*s$o)mWcCn?p zc|-b&R`6?vV=pu=@T|sP{w55^PHKUsM)SygLw1vyOY0iN-x(WQzh}pxX!DYd6tx!Pf~9;lbbKLMpKh6 zYfl0R%#t`Td*_0_RWZ{z1;#K;&CX6PMD6$7FLtISFnW8p)RWomaoh@gK78S3eaD^b z^3qc$XB6-L;=$X0JqF=MLo@R#3WMt|qWwB_4I>pZHp6UJS_sj4z&Qo@99(0 zwz^0Kn_?y61Yh4G_b6^i-Ym+!Ct=hUKBvvF~l8x^D?<$Vg@ommAY5f)}ZEqHgSaI2v z^vWgN9E#;iHQ2tL^l$+rAm$zO$I{=-o}ksZQVpq;&LQ98*mpN`BoIz4G2q94ZSrv2 zh^DXozB&n^w=>*Q`!ypM&#dO&mFWR}zZ*67nJtBG7ZDE(Y)B9Td+K6$eJ(w$Ia`B; z+!Y9{91Ry(A&lSQYOX4q=Cj}6r7?~67F-z#VhSc8}!JQ!7z6N|U zKH1yb^AY%>a=V1=?8u_tCtVFJaxrUjU~=;HrRzU)YVcwfyz=KPuiXxab1=dvH1{UQPP9 zR_d>MYvT>x7k?iUCT7H-Sr~yDoD#BaAitA2no7P61-tV2CVsL*Rs%v{E34kVtHg^9 z%ANIOj4fVG%8pw;@*sduE)T|R9nEWv8dEpmC*{v}4Z+M9$;*fg94bc4y6}=;jexko zf#+;#tarUPz5OQw+jy|Ie#iL1oi)AR-R5Zz>KLZ3a1L&9^Y?L?+~vw$PEC&&)oelt zkg8govZH|%@V%soRJ`!1!mj!&F@MEd*io7h0_(%u zq`MUt1-Q?xcjMIveiDKFB=E}Fp57AKXv;f6W7029yb51Q<>0G&_*s95IR*F~FSq?+ zw~kIJ?GPg$4~Rz&gNeg5cP&YyHWcEh+4#QTI@TSW{*4+DVgtfdIrX3KP%b zm~09T#H2tkL7qZ$y-f62_*5W`l(P;dG%5?oyB%_Xyh|;1I1?H&NV-C=hymG#yX$j$ zpt(}71h2enYFyV;XM5RsrQ73$mTG|g$u}P`|}xeHfoeomqEI2 z<=MKr@)jvjoRVwP5ldbeRNY>0zzH~&@|gCat9XmkP=q$Tt)FI^VAb&WJ`J@ zL*J-;g)T|8d(ug|#07(n+bVgZIeGh9Q@pY9VhJhL{(L!O<=YXTQgAaz6^-A9b;j;| z<6HCo)wrS5WO0pzq0pY-ckE1NlBrFMp)XiDzDwNxU@?!Iup$nu0WW1%vc#@s8BH9& z*?ko@Y+(2~Lam|Z)hN}w3b{=5pL?3NEn;!4;KWIlsDu77;8H!2!P~cyq*!hj{!vMD z5HvV%8FCBIa}2Ve6T_=jYVMM}i51D}>z;?GE%uJf!iRf053wM&GE2VeM2wL~vx}tf zbBqhfn`X@^3Clpcg&yba_6yJ`-%uD5Ca}@67qVmfgzKTdA z5YSS-Im!G!td_8~94TXUS9Ikqt*#Tt6)TS@jplt?v1Vek{r_VJ>L)~AJdjXG}Z zWE&4d0@oSy#$Y*5eLejXHsnQxQ~yg!_k|?13AWkNFI$3UX_{0g_Xjceb)|bqrn|9q zcI-%J7qd|TZvg1E0HH^w^5(kQ9^BZX>A7I7m~E-EQ-MGNr+yJxgZ>=4I16MHhu~0i z*{L>O3>#v;ar;FXi%a7+9UOouEXB>aWj5<@mP+Q|uED)e!L^|m2%xh9QLk@6)N5y; z7I=SlJsqe8RvN6lQ(a(6GTAP=emWf}Nj|z77huapI)eFQiuY521iS|BJFbx`C{|ak zmqK*<)%t=ekeW033S!o!l}>>AHl`Vcm|65evFB{iaY84 zJ>^uD-3LuMA&E*zxGFI@rUa$n6+vylCJo5xY@jw^7$W;!W9GgOr__GKp}`gJM1c{c7Q~|r$e5kJV-#46NG*-jqLhM!PXIJPy<9cOiEf#dogmlk52&%$trl~ z){jDb`@PkAR&tO>PJ(fKVFB681?Sr(%`E-eAqib(4XUsEOew4uc>qp(spn2&hqM5^@qfzj(2jcY@bRQ>1<7dlS z#|F`hDcY~UnZWQ7#^4hi^<+OrUQi?fXAG7lHPcTk9R;1?IRRPGXgb=xqa97&q>zVO7N z=Y)#mL(;xjwA{K&OK`ULEQEqrhOfSmXe^Q2GN%F^gRRE0t08Hf)JzOzmHI@~EQRhJ z-D~dk0*gd4%Iytuo>H@EiRGEtS)#xKHR!q*N}O3*wM!#BNSC5;@oH<-&}(sh{e=Wh zx68#ji8)SEJUOL8 zN(j|_V=qQu)lWbuQ=^rgn($*)yZ54F(i6O0`jYiTm_T-TOiss7|QgA z3|i#q!x=c61T$yfIC$G?<>bKmkO~ zuK?{D_i9{UarN+%$7=;;_{zCK)AFe}Yo>Ab0?AHp$EMFcN_hd0wu6ZYa4#aS8yf@_ z@pS8?Mn#o(af~&Km_t@uT7M zjZ7z~fl)c|WZASPCTy#q1c&+)x-i?b)9)Zuw>KA+`< zL=hUJLe=1<47AH(`m*I3~L4wy``z2Imh1=*R)#v??%2AaHV6vYY*ZyRr;{ah>uj!Gb6n31n z6((Uv{*5_Mjn+!{wkL)u8Ajkv!r`vC9920gfCC4E_z~yRts6qiqP6U!rub8g60QE# zIAzd;9PEJ3X6I*ogLhwY2%diD_bhRxEpB^*E;N&!p9IA?e%n8uFV00d5JVUkliDYm zoANk!sowFiCjjbyPqSj*o<>8N0`v~r z5zdXmgrfCcm zoMft&)!t2F5mjnY@j^GAf!{b*l>%&cJvFj(`>_OQ6|+Ob73v4SC;&6QWypvv4X0pQ z;}{)G7SyQJs)p)=|KepQEuwhv`UmKT0A{<@_S{^i>`2EaAqK!!j0Rd5$8dt(x z-aXw`kQ;k7>xm{?X#y3e%Y}$4&nAT)Dh_w#rNrbC3P{-$ASv5Nt@!vj|3zJMol9b- zoI{6Q!zfuYbgHQ7aK6RMTthZAuxOR10_tz_(%^xO9iv|@vQ(dvk{VG-fSD^S|Fff5 z_l(Mf(`^_h*4m`84GuA~Mg0JFiRyPx@7FH(eYN+#0HtPf)12Nh#~nq|ZAhy2)rZq! zOc>pto>N`Cht|%*;Slzqf2s|m0oAK*OM2c)kfFv}>eG%%U0M2q4tf!}+OsMfmTbgZ zJjP5}pj%!)w_BINA%&c^YOOLQ`1z|w;tmw{c3lN!#gu1)!h+v7iTw}>p+7&vD9Lhn zsLuEclkH0`S7UY+ga`NHG@|J9*Kyc$Gp#6h92}f6|6<0<6pK%3iXVPV2C;i|p1Uf< zMUxKm_sc~LAt?n_#;q9QdJxFX=aVs7;Mz#~2%btEiN5A@nR$I`;;C4sAIB;f)9Y{X zGK-Cv(LZAI9Wjx8oX@_CuCqQlBqqg=_mHc@FO0ccLrbqMIrjtjGpZPJT9ogS{>wN*kEm_Cf1$RS7~=jXs zTm|BnQH34KxxEKf&Ap8`JFz=O;IjI~+J*qJwhw3f>Ve8%v3Y078xt=sx86AC0*f$@ zZ{&k+YxZeF1;nq7iaawaMu%6c+;nf6a+KQw``c=NFvTN<#Xt|VhYT1+kBuFEK!f^6RV)+0SL`L9p%B_jWiS*ft9!OA@{fTMs1Wz;du&>9a{sY3E6>dNGf5B!o2c+4P&Nz<;6eq7zwRtMzREa%QL^rWsEm*@}B z2-Ti5ab?_Rwh$x0tSH;HQw}ke?r93NVzzx~f88xu3o%)M+^Wa}FP+0kE@rGmk`fo% zA>^9OARaN;YTS)1 zOsAL~a4_apP)KnM zV+dNfIMf^2+flA9*Fn?t)64lfo^Xwsb-KF?K10EZ_pg4ajTcNvON0*z@7ns$o_QR+ zNzVx=e4=;}A-_(cfH!g|D$OwrE+l`8WjOv()?X@qnGz*OFAG{VJf(&ae~)_ZIF7oL zG^&dR99#jsf+UyPPaHP$Q*{{LO> zK$+{m4X%uy;zoQ$+CTswR-SuCcqtDPIVhAkhHD>{lY zJcSB%b(Td-cXR>RHYd#bn+f0Kgwmt7Ntbm6*SfjPY847Kue3TN1UkkTR|5Ijovr58 zr3zNE?mcp0oi^V%IAa>RY$EdSdNqZvvAisr6LD{Zig#2doz}{A&r#Q$xBR7Jw54N@ zF`dl_IiE?Ts$7eC(ca%^W@D(Tg4@)UmZQb(3pSktLWbJDi+^^zz^5K)D7dBKgNe?@ z(-1>J2+!G8Q@_<3Te0z0kc`3pEu{VDPXb(Uz=FNN*!$;d_z2uC{%sLWZHxL9?!&0T z0|R>mZUdI0{|emxWB+>89tI2?fAgTuFhD2%NO$>Ax}ubcz?BJDX{;uLFV1!4bMtXEi%@ zTpf>-=wYXcqCYG!U34OU_I5~+R2>!l>l6BR!QwJSvu{CIc7?fx-eJ3d!7mE!!QiKZ zrZ)btBR+r9FRT0X*N!+1v?Esg8BTMctonOLEUG%yR}X@18$ZCd7hjK-1**1VXax)F z;N5kRr?_2w*xXyJ?c6k&z|aks}Hj$NGpJ zpZ}KC-uB=|c$94I!p9J&DJgjlQJq->KN)%TWXhf+lPdy)uDfS*ZT$OjW^qH9;i36` z-Pi{G18WjW$KU}D5Ii8FhP;nE zS^6!>>B~ae0Fqo+>kHfm*0xpUzk&w~y4@%9=%kl|fN;n4WA)5>Z3t-0enxuf z4xh&7tm}=94J=gU)xRgyz&9SytfZHq2{rHxOsIdo1~z%Tj=SCfguAokDW7`J7p-nS zk!DU`w&H>zRu?pnIMGr?U ze@7+IISU!TAbkPNy_`Dr$3Ac+Nx(i{NdScVQBF85abGCU#5gT!;2OHY+|YQxMz)iD zre1rW zl&f*vX7uE}i_leNqez_!t%KX&9l&i%#)*Bm45mpD=tF4WV%btj$`-fo^MWDri(Db5+K=?Y;*vU^H<86;B zbbYbDwuuNDBVj?^BVc^0y-lf58IZJO@HCoQU5`|OMEHB8 zD5zPkj|ym(vp5WySZoj@uPej56MJ{wOQ;`wD|U{z{a=PsqnVFGDa&5`@D50XbOs%x zm7qvu#JVl|b{1v`tmd#Qgdg!Ts%Y0&v8&m5k^wf-y!jowYL zA;|WlT6jtdhDIeqh`d-h;^pQ%ql;@0?2yT?__}!xFZF4yW}$R`!Zi;App zYP?to8T36upnJym-fDHF8*KHSyY|iPv#r$`dQa$a3;1;kA*_2^fVM3KwnBNAqHv`2 zkSF*}+m?QyZNHEV^h7}y*}Ujp2hp~hq9aotXxsT|t}eX33(;%lS^O>`a{1_@x9279 zuJr+*N`0HBen|Lx@>sGugYQr^dbPX;qt4D8dI|xhJlEwCLeE(BHLdI)GF+tIA2M8Y zx>?5a-0$5rC+}uQbW>`SL!<0mRVMko=e@R5=-8j4i|N0XJ-JAAyrqG}W zATt013J(ZkB$bu%(H0)Br#jOXZWfKckN1kWFd(OyfqCgb!sZ*bv7sE7 z_)n=#{4c2uck6Gd4cFpPY74;VZWlUJ-{9tA1&e2Cw3T0gX0&{`N$ac-e^MQjR_@`m z6(LALmf<>mvKCgYy$cJfjjYP&?gXc`>Ru( zDt#vt0|AyW9&IEK89Y!$Mf|ioX*NE2}%pu>#z%OGyx}70?rA?S3X16 zk{!}UX8G1DGufAuR7?Wx;qIm2kr4})Jb9(PG;5M-d^8)%8ALF8-)vb9S>$G4!sw3l^j*vFKP0g)&h6yS^@Lqq<)h7DBY6JKtj10rYV zZ(0L~3qDrhaPex*C^^-f=j&<5?kvuHE7anqWTFj>GgUQD?N>t(IU7;h2prY+`(Na2 zoCVQ$G@2lCHd(_Z10qzn>a)N>qlr?whoN=m`Kx&6RMNdS<40gOQ9oO-1M44rO1jsG z=y=}B(L=%3vx-R6 zt{}724v@3SLFDYhM{;&{B+p7kG@&ZNX{%59)iy0<$QmPq^FFn5Yxf3Wk`2u!f|6D6 z{;i;MDgIrKGI*uSYkI zHM<1>>JmkcF9kn9GWq$2LPVz|4P?1jdLs8|xd)421@5WI?S4kUd(waMv9<&$z1ojn zK(B5bAfzJnOfUb+>aSK>Wa^jx{p=w;S^`ZrY3UIZE2fCfyC4HykNkD7;A!GgUjPOg z8g8P|y|7Q`#j? z-!&COPrRD~j>3>_ab0yxV`JW5s#dj0cddYAzZ;L>{gULjPI zVpEwn`tMrfgJtN@21okuW?C3CHD|g(vXequKZ}xweSz>z zl~f!SxLB~&w`py6Wsmxh2@}#HFN#HR0Yv4V5F-TibXrx?iVqPj<<*qV;(}6M4m2HJ zKcwfZg3Xqfjux3`$I`!JWt~V%9wi2l*+j6(Au6u*C2pTDP9(DZ$Z5b;>Z)=cIDN$i zwRz^syCLW%goo};+i!S5=*WfF?#5L(0U3f0qSu6E;qmG)^gaazZIwgTuLoMS-}c zAaoj533~3EKekyR>zHAb1W|-^RG}efdFp;q#0YyFmCCeR?>Q#4qR&o)A`NB!r2fR% zcfHs+wwhB`Sfn*CA_|cer@HsLW2hXww@AI|mSrc%Ru>*-kI;VGq7f}x`61OzQ3)I{ zE@TZz5mJ|)1C1jTm4BTtG7$cGzKB+SB%k%Z0+G*j(Lv-hMi{_>%pDI7kk4!b`-$U$ z@G&a=m82zj?<;9N4@MXU@s2fkfb{(2%-x-a?_2dJ+7xhs_sGT1Y3a|_^M6~0oi)~J z-z$_JFod|Y2w(_n!SOc3&tu31$1wLAy*20)P)i)8*+M}3Y?H0NP+5Z}-q_5M&9)-d zHs+U)Ag38;JW~sY^!4d9_ZedNJms{YK4%%sq4?#ll=mCpQPu3J(@0pXEXvK#1U#xi zf0dD7U?ytC1Rn1-d7edfyF#uC!v>ayykZNP)RO6demH?0GCeHr9h1N>h$ZlSPuuxR zU~cBIVhsFV9-q@c{IW{m1(rU2s0jf61eSB)87TDn{TkydIPe!Xu zr<`;TTg6|b@XQN>a}ua1<%A20c*{tjMHDcpo_oGPY7rTP|19Ju&J(f zV8yWzTlZORNaF$%ohYEQQquP&&{_GjC01Sw>pp};Pqr>%IR7kcgN{1)if0+wam^`k zd2^vR^SoN>4XpUIRBTfB1J8sb|fG5!6^&qjGhyM(V_3OY}pk#Z^p0_?GR{ zHOZ93`Pt`5I{%MXO7JeE&m?cg@F#*X!yrPQW8bRZwuRN=rGI!wY=enB94 z*42sCZX?q?pLsAK)$}0u-B`g~CM8!`R26FpG6ZF8=>Wo~?O4O9kCGX;)TqFY-6%T#pwxXFeUG(m=`p2AQtw=fsFZyPKfFQ z1}qM^AJwP`%Pw!=z2v7eZJ-iM`TVGr17stfsVJB@OjDbq!(20{mlwqtxj%Eso2&bI zo-tm6f#EWX2ID+T$>tS-O`rjwuqR;>bhd5|ti*bEswjj`H!K7?FPT{ibPL1D8`sst^u-bvdm$WhZr7cG7`M zguVf@l3}JvUJ7;z&2m!1?|i#&X-6`Q0Gl&enfRVzSLC9y=E^~j;}r%gl}ub@` z*~v)Z|iqINsHoWbDGF{LM#+omXKo52-B9i?t2-MnDPwbY^>lA!v z>C8(O-BdM~bVOw4Lyyvn8r-*)trgj{fdWh0(c3{+U_EHB-1C>yxLy9}499@}mvBLL zF43pai~uuZ+)@sc1F4#GPlGIP-xZ6UDSf4iTKf^ZXEA#cjLQhpl;?D&k7*0`zn>Yn6s(3pA}hcw6u3KXCxwRx$d=!N6icgtj!KSpNVW z{I<)S{OUm-!@xoWcu1liz5>bzc0<+bgr4WDO^92N6`T!Z4L!|8(Jovg&7t06cg*7h zGCk*IAoh{P_n{(uS2Z%arPA#62o**!h!3j+2jVp+y2%_ePw?K3B7ejhJQ}vM5n}|zADJBZ(VgC_o+=FR8}@If0yyGXJIE&{oBR4vBO~Pd!vt9 z(`96`)qY^(+}sfe6u5Mu4XluOLr7NhK6N)#dsa22-!+Hb#yPGdI+euO0^%@v;=k(1 zH;j}W)|(xdIIZ)BnLBjZZM<6KMwO{-2s#y}CcvVPm<2QScJquZ=$jv_%6j6Iiu-}yFy2<;59dBeenL%|`kD#hVX5CuaRx>qqn);pwOQo1nHL(I>XCGBrI+Jtu zOvCh)al35VMNPGATFFi&THQS<*YCt@!`8V^*EAjM-2XzVq%g=^*vU`v9##N3}zfZ8ZMAW5qE+)Xj`(#nK<$awlo59v@ zncWL9o3tKm=J(I$4kh0W!`V*-FsIXDjda#+W)CddFWK5W=!~< z8OTn7^P_m%%MhraT&#?O(1Tl1!k0Gh`?obUr5r%g-^jZobkqKp%2N7tBk_V*zVlUl+#Ya!!Ff7AwOCHXJt)pWOHiQix&LxQl zImEI}UOv*UtHIIYVY{ee&geEH3~Le0J>BPMcx$s3e$x7*!1MR_FjQjFO#E$6%}V>P z_SE(_-|_@hwwzX%%)dk&iTYwQwbqyRp-1h(dEr@&y$X3wvbSpU{*Df38JOvmH2pc# zLGI_Zm`$(SrIoI*zH)dD&#A&@`$3zTUZ!6t=TC1cL%H@IP=Ope_KUFtkQE}a05Qeg zUw8pTR*-SPg9eV6UcO+vE6*|FaDd(E&NBj?LVaL6)-Ftlq#JQbISyzn+87px-&ENg zuwb4%|B6XRugI*~wte~YN0J(wZ_}F|c_*g_+O?hK7eat`jml^uru8Dcu(m~6->v=k z8TC+2=G-4I{57u|!2`@O3}$h8MV+He2u65G8?4TMva^Tn{jv6QgV1eS9mVO#5V;E# z!SIPRtY+VNd5UG9;da%+2i8L)z7X=v{2ZPI_bwwU!WqJuUH+<7<@H>cH_tJj_)g`a z#TL@qGpHgo#+D#+*1!M)kMyI!!?&j5VJlSkw&%hQi{!`axjfCYEhqAr#}#%+I{#)5Z6 zY{o;2ll;Wb{w5X6B~ysX;A!%)z94zHPL(C7-u{T zKB%cLXrU=JObyad_ac|(CXWi8Q39vl5p-(xw;__Ey;=v0Lrm{}wH*jXsX`Z|S!Q4p zi7sRehp}kTl*ZUkyEr9gv2lR+O9?hAjlkH$S>mVMHXN-U` z^-%|xf_~7!$+JN^cuI7gRSb8;m`$xp8nehGtr(z#N0Mawi7G~tz0d)h){HKT$s>Z? zeCiaGbk5Y*M6@2DT}3&rzRs3p-s}WGuk8%&Zzu=kb@m#De{AGYAlDZDLDb<>3{4@j ztDk{q5@72L4s*(D>DcefthSeJ)Yo|#m}#b694?+=s}d^P9t4~{S-ZjBGpqLc?rLb);5T9n-|90uMNGbi#GQEoW&eWodHW(2o~*D zn7C(G|86V}b=g8ta>>Zo3B(G47EywTlQ7uxciDlW@Sy}#s4v15C4r+d3~ra=QUuV| z6x=Jhv+YA0bWU$l@Qke7zVq)HO80Oi2@uQp@?#bpTBK15F z1@kyzfgAsJ_2~0%P%4)wmfVRL`@aRFzrKO^u!By&QkcguwD&uVpdRtD*BNu<0oWTJ z;3ba3V8X%>{4=mY=)^q<>5A`p(#j*SxA{+CZ}nkd`$7;LV$@VnX+{0?4zp@=*lDn; zq-yRdL_KwTzR5c*2`0EBh<9fLT))U5v6c^Hke^KR|CJ0<1P1=lc+Ce^DK8=NmuQbh z25P+S|AP!-02>(Aqo_uswJ%FTGpX|fsU=O@V#okrTm4HOo@SgOSqt`Q(2!U~eqIK* z40qb-$Ca06i`a;({)j1cl1s*w4D!qAYB2dvzT-h5&`k-j+WQKNmPEIt188gPyJ6en zQP*f}CE;3Psi@J)h=8iNfWDC;_S=l`2$VU#Gp`XS!5cH~q0h==9lVB|F^rQKwR9Wu z+_|3aL|D>e0?#S8!czv8p%s-T`I)&Om?jk8=`(4ZhzWa$gj{!1wB3;CVsk!(EdAml(yEzvlV_4V9(^>#*0%@YIeykp&tU#{-8 zrKJyz@gbJD33id+a8z0)d-0w@ah+n#=g1KiUmove5js`U>}bztzv+0};7!Y@-fh#P z-yom!88T|g=uJy4ir;*93Zawl4pxQ!J}aQ1eei(FtO1{oag>M82V(obKOas1)Zfop-)%w?fxG~bc3Rs!+|qbh zz^7rtI2W?a0Z^s*UrC*bG6TwdFelDq0_H*HnL`&Az zlC-#O8VNcL+WXO$DVeoL_K|TP*db?GwWlop;A;9o2oC|oDeQ|q=l@LQ^v@AEt5~bq z5lRPdHWkKCn4r6ymHg8Hnl!3VqHQJdrdvHX{EH>932Gh_s^Y7f4KN@Fh}OjKo_t(u zFZ}Z4cQFDhe+kn^+(7e{W!Biyty-687)Od zR`(aoOdzShDVHp9FFSC>ErUTTAzwrkBwisD+DoP0yhdtwZM!===~?^6!kN&>;_8s` zJ7Rs)PO!5)hULz6dqv|wc)CplT8sV6add z*=WdJ$=$NhAcx08wdFt6tHdFYdi7K4GExd{kAeQ8{W6T-FR$0?GkE-WU^S4Zqp&Hw zvQpBSE~7Zcgk>Yfqu!2(!Ud1j0WZ$XyVQMe|CZFpB)eOPbZN5Q=qkP^hgvk%L+2Fl%unJj|Cm0tknYEf`x0tCK7=@p$%)m z{ywo=-P}2fW+E(=+SJ$#0@)?nta1qlmnS)Sl&mWlv(GhH_t3${s)E)YG5K2M4(eng zb3_avTF-!$fKMV@7hmZ!VZlK=McnzRv&>VP>-ELUC%I)q^2Ri(wOVylT&~$#sF7ja z^_o31@rXQ!=KdCxG%UoAC&>>YT01){IsZkpcKRpL`cWWCG!Vu3n`k{}W#goIObo$& z#eP&qkYX#8bfk8?1+7RD904mR?`2|h$qF5`L)50FLL}xr5N%74`bBPwdI%X^f>UoD zuzR+OJ=i_#;)*)t_WPtKbJ%6P0d7bt>+7x<=e!(QRdYQN%~{@Ph0g-p>^Ae|SGvz;L7x zovM8rTIs-g>+_G7YH1o@j)XuBCY2v`2j{G+eak)u4QT5*-14kf*!OiOl$ba^Z{Hq$ z4X5*@uCS`c)4*!^Z5KHak~_yUej+kg=L{e#hH;~6Y%$pZinZ*173_fKg7=qn+2p>V z(J1VNF?7aP4uv!a4tXRDVrJ+^`6Nz;a!q8i`emL*1ku92$hMYCjEc7-@%6ZGG%JEP zOcBvlh^+!H#G87us4UX6`3IT44;-vQD?i;Ynih8y)aq!cNZsrXyjoFynm-;P?I~?; zbn#}ta6mFMvHtd?g4S(0*CLx5&|8Ws8~VJjX`mD=Q?}nzZWetO#ExUtf$LyK(Pifx z{+uQ;bDy- zBntS@uO0t22F}n{*CLh91covt{NgqmfLhWGmX?;5v{j_ud;711&$$*(lr$Qaufgswj*H*IQ6g56W}d>bWC(_eNc8_}5N+vMzK z8PjXfSuZ`<{=<>GWn-d%LO@XP*S7Bn=IH?hT|AZm3j-J*tX=GR}Pvi zw#7R6baC7Y(^Vs0vn7)_10*!ty>T`1p1Uocd@q_&k$w^==K5c)O592yF~xLzV=(k% z`41cDX;DVPqYYH5PFm4;-YAA20xfu?xr`{KyXd4%bh89l;#xdW3vdwQ3h=9!kA|&*r_r+Y*Kc-gu z*7hR^e?(Z_p>ZA;LqDM>Ky^6*kwe9#`G14T38fSueSJdj`vR1RxJm!|d%rl|iU9RW zpVs<$%fpf=qYBN(eb*<|pO>BsUJ~;{m~#cw^0#B-Z#3hJhnU1_t6kkcwvm<<@Oows zy$@gVvXz`>Drfs>Lj3ywLiegtIA@@_+r^K2iJMb) zR0ISeT)56I9BXS!cWF%Kr?2Db+-!|OHqcDK1}gI(Hqh=m!{J98C^}#R#i8B@_|yMR zX1Bg*df-nVmH*2Is`+1Rpzh}N&VUVc65^3R4Fj@)F2t6p{m*TnOqVm}Stkboe|l^c zd%8i_LXN1mEu-v_KkfL)pWZqI_|si(on5;>g;PMI+O5N<01)@0_r)X=Fthz={CgM) zeB)sl3H%8RBY|gN82S4(##f({8K~2XCx8XRx53e_E!@x1*#(v~Z_)>IVq-1T(HS`J zaA-JZ;^9y1vHJzR^qKnG6S}?wctZ25b92^{O3-8QOd7vcP&r$LWPS!5Zh#+y3cq6q zc;Eal<$p+3GX3z6ehk#g=Z;;W8mN`qckhlrra5UL6_(k}*v;Qrzr1tUq+gA0a>REf z$-M3KJxt4!Y7rPQfDzR}0{Sf>eO6YwuWZM?%x74mP$#xC_KiG*{MQ1A0A|RCgqkaW zK7Cy<+5rMb&wp*WLY*lp8AvR*{$IqsWmH{zy5&ts2o~G|1a}Ya?(S|OxVu|IaDuzL zyE_DT2=49{f;;qroSZtRy6U~R`n{w33!AYw5HbdPt^YHhIe!z!Y``+KI1blz;#C3N z(KcDSkzS2}_+^1u`X0iA$b(u$fw%xiNq6)ozikl zK5UMFLd34iJU5_eZ~z!Z8?fK^eJnW4rc#xmkQFn~3Nz>Ps2^;Cs+h)EGHEb&DRQE? zSM_*X|Jw;VFw~79UHuOrU-vhE zI6)aYApz`koCAQJhKNl-yM%Itj|V9~k1FUT03sekw%G%eN>Aceng6n*5&Ta&qvF*y z%;Hx2SShPNlO1oXQ-NfMwX601N$~yeWQP=x?8vqIwZK`A;>Hbv!{*cNooG@s-yY#7 z3aDr6H@dwQeu9%=13tp{8p?`qcr>^RvB)M8DG_Xg`y}`HC7k z4ulx!X9J9&zqr$2e=^yCXMuZ(u`}>Iz;y?>)4=-*P_6*GH3!)fy7x)f`nK{)XxUj3 z_)=`JpQO0HlT^(>(Ht-!VNfwo!ACEv6cxo@8}K7rx48L+nB>6Z4Pqev-rZ@Un$rP<>jEzALCVy2B=j z4GfHZXOR5%oplK2(da_&m3nTqQP>hc9+|YJT(S)}{5qUbl>jG6s04j;>W4NrV&&@TDA|Aa4nzrmj`tt^MlGp9?r3vJ7nfB7>pQ?n&O zi>?U*8>@=v1iy6^v@cMmYz93AW!Na)3orY1V*pe_v;(@F5+v!P67%AbjdLCiS^MhO zq~D=WUHqu$}p3<8?&HqJu)9;_tn|+{vlu|7c-_x*OK=#M5UfBN6!}=f8 zjKU{sM%HG+AjcY-!kZtevJWcLC8j3;H(EQQ^|atRnrV~G$9y42L+vcN{mZ=PyzbAH zbA49OP%SIkXf=1k{5X@QhIHrYP5N6|19iNhNRC^%Cu(#`E1QMt`v`nnlaSpkQR3tf z7VW@x&OB9?ZN%fo7f%3bW)hLp{3ss~>sv1=FgtPx(uk)pbyx=M?yQ}-!K@&*qMr1YJ zG1LaBZ0$c~qOqp`mWf^wct^6vnUG|Kzcd}bdne#Vcx|BPBFLw>c#A1nO=URR5U@A8 zNNT)?4Hlt6MOcp^F1uES`Ma&{&1~iHCFhf-HMM@AN^_zCvmuKi@?{c+PQ>3X!QX^L zn$op|FiFWAzj$`txav#MSjomo80UO%N_M5@!`OTOBcMY@Jz%(bQch{DV{({V18avl zd;#pftrP~pFaGuL&NMVzSX1|Ip~kjWFg!MzLqYl#@uvV7KKKE{qir7kznca`OKM7J)3RBZW)?RLsRFlka1I`S+o&V zt`D;Ki~O6-^;`b^55Ul}V*fwD&_o_YG%K4Fvi}7bxuFW`3Vj3FmWq_>J{&3ZHD9-pU9@j-*f^v2SL!6_?w(rOK%ZOM&{)$$ z{W+5S7bY~(uC7Y1oRUrPuWy1boRCT7tvQi_^$AGBD?9Bajn>B+rqQU9*Y@mN*Oh<+ zwx}LxX={0IX>;7r;r*kfZSo&2Z97jbZS17{_zlM`5I&?pOB;98E%dOBnA+^a#&y6) z)sK2zUH9^amJcKL*LoJl6yk1benZVkpiH?0^F=vwsdQ)!yqe5wj>&Q%u7zH>S_|eA zp(Q_e?DD)s%E(y?L?TBFU~Qg)&4b1~9`zFFIj+^E7A!o|0^_ZbP%I}r2@g$H=A2X(htMV=a1CA*^3sjhH!6er`Cl)uV3nNu?)+$Cgy9FNhuXc#1T) zJw=+^ooFhtr_Y#5E(ds%07VPxUn*KG&2`q-LVr^Ehymvk{U2057@(KUBPC_wXHLyi zFPr)2r=zc`O=Xxg(X4plQ{&20FI&0*T~uDspS^6?5oE2kyedYVKrb8pIdNpcH=h_p zR$wX85lW#4)>LUgzl-kkyO%A<5G+ecm=BV#Ie&G$sg#L5*oOM_L^)mq#%v)*hB}() z`y>Pv3!nHKeEQ@l$if78%r}|Y*7$(GrMKkHcsyb#A zcU-~aiwc-gVpzqT^=nL_mXJ{7Rrw2e-XaV$x)=Sg*3IHdC^C17BKbv*8G1rrUkEE& zvHy=Wsl8zRSBd6-YElCd&2^EPO%$)t@fLOoUJy-mx|H(|30<0HLsG$Z*yOsf&y$~d zPAQZfDwbVuiR(T=JA!Hb!_iGKGChbDm542gt4rMUG;Ta*{HjE)=d!wkM+e3{df4>$ zRRtMCl?Hm-qf@NXi>4YT#WbpHb_Mjvs(}GU0MnNu|>NCBaN& zSqGHjOfLw9Upyt4v)uj9TLqc=p;RIMMS?kK;8%i~*9b^3e}H;UFt_*oHNm`+0|wY! z{(XX3VFkF;J(VK-mkDNt;D421j>t##5c$Ar8;^O_9NiPy^mR_|E$E51$44O;PAaQn z0iF|t9)EsO!`FOoFnRw%`rrXdsel;TOFjaD( zoW8h~z|C~|7>$)w5GYNN-IPTskQLQAwwy3jrM^frW-`fBl%Q3-0H#|Cg)=^LnPo>q zJ8s_E5={~3pjP1&;uTO?or`QaXpr3jUxrZTi2vc#OkvE&_YYoMP(SKld2P(9pY?E^ z>qL~=s9}KV3B(re8>J#D{0^84xHr&JaVaKkexn6ZZ%tnRkWuF`UalBb2uf?Eeol z%uNlU6o1Pw@BYp(OKVde^fael!xWVSzv@g&p0LnlD|+S%9?2#;|40@}az({pruqdI z#P|{@(XG3=^caDi;Bfns7@~I(|4e+(s`PS5x`{Vs$BpGPxysF@5dtwT$CL zEvM?=-VMTCR43|}-9+;n34>npG$yfAV;*y)T4o#_udRF$-l@FQS>?{r6j?x=Dy;eM z8sSPwwFiQuQnnaA1~KaOfJ1@Wf5c} z9akHN9}>y|&zF&{J`)$`iMNfmOxo-BG;3#Q5!Fd;xpWq=Tx3Y`L(;UB6FdNG@Vt&D^-T$?lQ+aWMLMhurCj5n zlyU8*Qa08>(qniOu&7a;0qj1~0RUOl!slv}Y=ahJKDVrYQ|&X^FkF)^^D?BzU2ic0 z$!1?3h^y#UkC)o`ZM-Wh{?T`_LvU>>S|CSDbfnFuRF-jZy zMc{{F2`g4xbHws)4^B>O*Qduvv-{(sA24%6Y%AUevp=4lIq^LvviqgN5|Bk^qUDwn zm!_ole%vy#)|ur6)57RHIP!+8$S%v}rqNJ`%c^EkSF4HNL7;lES)Kk_u1>ruy*W|& zni#p<85a`PhuvS|(~OIl;zl^qyP^I1F@01;K`-Ruyv`$b1~$>4%xd2t%c_SdO}4h* zMr2mq1&@YHmH!MUxV1-`d3b~aw5-~@kmkMi3NU*wiw5}zpd`1xW`w@UE^mI|ov;D% z${kt4@*;hN{?7_B9t0`6kA4kokHAtY0M2!iYHJVHS%*SQs!OgNxt>o4X{<1AkgTDf zh&|R|J`HvRBZ0U2fb2)itIfI+jy^29vgtBjdS$?N-eD77TKt-dssg>3-PUPVfYCpv&wzhnj-?997{U19GSu zIGTMOj;g>r4A7&Vj(&ip>V+Mt%AfhX6y#s|JZY$>d>#oHum%CtD!@Yn$XY-??@5h% zPO1!>f&%P7QXtq&TV%1T2t2?1?b7HNCI$JqmoSaW*smtbo(tn=7A|01{7eG&jYo7l z+4s-l!T*oGv`GXkp#VxLo7d+D7<3W{yP&24g14PT;bZxbHo;Zis57=MLM$EJE1c*X z7Z3!UgPry(%T4h>XCyKj#N+!np~5-w{S%tgud%R*gx4JY0v0@Z|1u$jnqoiJkLMWK zb0m%p*{`Bg4NqQhKM5!MJt7YIcaKjOFWW?At=jR)uyE?{VD(4~V&%qcdDaEcVGv75 z^_TO8mgM|e_!%+3u3XfBp@t>n9gs5L!`e9bo4**)l<3Q<^5%tsfY zRZ-+mmsNx)^NhsaJdx3s^X@=r{alFtW3iq11sS0xy2jLpPd%&6qTu#1kP7j%Snd(| z4v0>ECndeBZ_HuFL)RfUtX!fyjLC;Gh_$&pH<+aC!Y%k3Aa{wg5{0mn%2N0n@=q1D zL&qh9wczHWHI!`YNmeV@Z~Ce|NV*fmq8Spb_K)ur;^}#Z%RXBh<2p_xHodh`z5aU- z8hAU0gfU|Go#UF!Dd{K~pBY&@%fJ*QwG z?jAnf;4_hL-Zkp7!!ZwY@n8?Xdr9B-txw9z5&m5-;ykKQjEmY{=MwM3Ez!A=_@WCk zS;6^EG#u}Ne;2-Lp@^bvwO52qXLH7CZw#+^6*jc07ph4(sxu3Dt~#02LY(iqR(1~M zJI?IJzBNy14k7c{GiAD5kGSs_br@O10a4h-?hzttRlHoI?s$9E*9Q$sX51$6rW-l3 z+flo-{hf2;6eP+zCECc0oO;}OXNf1fw<)xhJGRRGZsdwnWZbQ3@-LF1-UVjxk8Z<6 zfR%x z3A4ImXNgNw0`n_%vCVh8&YxqjroMFhFwV8RpDc4sYfG`u7eN<;9eyCM?On#Q3F2f2 zQ+Ffh#~PENW^_jpDKL^j)6fL%@VW3K5J>JuyofAMCPo&{NNLlt|oaE&|j69>rn0=mC_t)5Afd!Qc!&x)svMNf{SKJh>P+hcY&42ZQ)u(Y^ zRqGV;f!u#KCV0+_9g3 zd>z!{qR60(iXLd{lJb^!{iZDt?Pp+gUYXeFN|)P@rGVD~?ux2XWF20mKO?20>VI$<+U$j#vvWL=&n`MXL$*kqY+gd{jZX zdw2j(1weqyP6N4S4Y6c_kDqV7jZ2(}9yfiismKwy+<%&qi`)s_?XQ06E4H^`VD)HE z;Y{U_NC|c+c5?biFV2w^@Z#0@i1yg)cx~>V@0i`gTmcZE@-|~4{Rr%mbCW21AYw?L zd@DP>!J;a4Vo9*$InFZl69_N|^Zh9JZGV&1VePguw&xiN^C<=D2K(JHfAfZXu=ATY zT%r#IlX%pt@AP>M+#s|zCdJ7!_F5KDM$8nBo!=m_=|ao;(r>eg8OSLNgrvguJC2yN zUGK#>fA&dcIv^_W%wK{DSWDyaIYfWC>!d!7G!%kiSA7_2`M$asO?f_@ti&07@LXsO zy-;evR5NUjdFJC`d5Gk6dDAD97~4++*SUSn*YJg`Ns|&wp7QmVG%dRnQ|~XKgNTCYPEml8ud$m$w~Zq$NG7d zCW)ele$k!4A06cSXy!19DhYE-aXUd-x$L8km)`pv4CF%bZBoiaG!&?DC{;SMoR1(~ zcBx{i(Mn}nV@8Qc?o5#6(G#Mi_59+6 zN}rjcf<^ZNEiV>q`Q(gL+X>FTL7xRUw>&oWw3cIFrp+KcI{#=i$Sdj^$X=hjPKvzo z_I7z?-%MA2ff8gZ_6odfHOWHeJsS8aIeJ%+QM}@-?T>jF`l#Kq>S59m=tprrUKF)j zqP)@bmgQh5bxwQkHaH~r%n7i;rU=t@??Fw&$ZEr1Wel9y75Iv4Ep*h0*B;&5H zif&C#Oc}amlla=nRo56&eM#qf7@`{ahQ|BxkQPrF zl%I3)Ls5>%viuZ}M8O4l($FM&!LJcT!!RUYCgw1iJ7Q|KT842>q2SyRW(Oiy5vOM_ ze>2&`U3zhXlu_YlCXzSL5!Ef~{xbbg!a3P*FOC2t)`LW`LL+;;xZ*;w{VWMj%vHvr ztzNo|vBgB0LMVbvCx!@q;~^K_SNDa>6g7!`6>g-)Y(u`tVulRa_KVX*33>FhPXoST zHhz-KEXT^p=_#5TO^yEjzAYY?czuiL*q+uiI+vdJ%7a)`M^o~WQ*rgX0 z!XT!5ob|Bd-a0_sx=6Rn+dT(C&)dX68tp`CBHHv}`d2=a>!k z2L$K*R36p01XT&3UlgHC%_UT~lpQ*GeW-aqzZz#gzE<&m9y{?+3Lazh;FrUTm&&bZ zb@C5~*FoPO4lnCJxMvhcy2yXnontP$89Ty0KRdjhZgY-+Zt%1-RVZBs{k1bS3||-h zMZD!?r{c6P5CQ=*VEFGNT(kQ8Kn*WY*e@v^jVtpJ+(lNLl*ylq(juC^-j3~n24ZrT zt%qR8#3_;E-uvrzmSj~zdck3ShRf8D)qYQ}M}z+O`Ni^+afaGW_}|_D$ud%1bu0yl z;B{G<-*TG-A?o?;m(h4b&ymP(wiOoYRm)k0$N2lBC^*4b8PKoKz3Hz~vk%KNn4-88 zdao)NioVt%@t{RZzsTd>J#K$!5koQf!BtGwy@vG5Yd;vjW+d3elZ=SsLTI3O!KP42 zO`#@BXLuoDx8Q>=u$NuX1B>{)v5Kz>-pKHrG~fiOlvCyTwQ5R)Ux*#5>4#!WtUkU; zQ+ZRMA3Rw2b^3#>1H=^DJ5#}G%D6+2A=JqVA+s8%m)d>&CQmjApu`Mt-u?Q>kdzAo z{SCCNx1Yb75|}WbAA$1)P^AV~?SPNJJ_q_Co}PL64HszUo-78wchXKq5}d1O7sXy- z02J6pkFH@(R#-VKvud1GNQ_Y{_3kN4R)B)Z>jFQQ#f;^E3r9~YT@4jr&*5A=vFCnM z41w?dL#V0vCDc?jT=L=@#GlhIAjAiKKQ7r&yI-9bS6;BIV{tuREuwVlhZ(FvqlCy2 z8cJ>ETu*(}J-DykT>i=U@#68|xO+EgvRVqmFNkQK9@`3q3iU1aZ4J~nu0est(!i2@kNK>Ur zb@;kRLt2Yiw~L`LYs>eN0MG;!s+!n-Ig)3D*|V*!k;HH_zmom}>d@6PL2%vqL>+JQ zcE|=}!Fj3;XCcnA_4E;9)~>?x)j@ZXlm!X*MKGF9ofY-9C-^v~?E~uyx3jW#1&LRd z?ePaf+tOa|%N#{X0m(1T&F=%uAIUK7{5Eyq_qskFd>?iMlgg-}J3l@!rLHXN0m( zRzck&e(_(KYhy`>6EKT5S3M7xk*>ps1fws>b7X2;Z0r=t;LL$`6-i3-E*Ne5a0^Sr zdTVFcQa~JjaE=%#Ec_|ut=(2HEy$&kGe*>R&s&v^Ig;swu}3jW)_6XRlPjzvmx^Ug1zcKMXHpXRiNLw+>lW$I3-o@5idSrlFCrm=dQ+ z;<{o)A@m@4IRDwUF~!E3@Qzz&Bt;sVctogKiG!m&o(OwbugztH#>A^~JPhw~8#YX;eGJ53?$_KS3rlm*8UG2y;W{gJnY<(um*%{TR6Yl=9W2k$6I#{ncjAH@SKV*iq zUexqP`OPcmov->`D=9*oKsyFs2SppjxoOu*w9sa>otp(1iJ+rgu`9-kEz8y2+1lD+ z>@AIO&Rz7fT`(yb9=XRA^tPCS)GV`?>G(x!z;gLKt2ztwU?DZ_DcAJ_9>T?t-D3g( zAR1SgJu?+>`*?@nV#?YpHAqwLJyuE%aTrhf_;Jc3?$|kd7j0dl3Y7rCJlLu&P9Qru zi5gQVi!KcxFF~jpuR)F?U2?(xN6Q2HW0z|eYR9vnhAOQ1%{-L zLU8U!uB9o(#Xd6YeQ(Z5UTivjkMev9l}N?fDQ9aUoq6~5emI!Kp=pA%ISrWlg^8G{ z@S@O#PLv#cwJ>cedUn4FvWa;+^UbB9WflvSbp-XZ{#M!q`4YQ>yY2d;g!Bqjn%EH2 z?W3SPX^}i(nQ00nQ|X3S*)JCL1UyiUV>F(&-{%M48@E>_tT@S&MwL`FMnR6*eGQ|@ zoY}jU!0x`e`*0?7+g*RptbTRGQR7l;{^8=JQe6FZzJ7PMskEar>CJ6OD#Wi7XOQfd zEEAW3Bxzr$#fv@qyGjvJ1Ll^dLb2v7;It`H3!FBI3x)+!Nkm#Ab=B- z!3Am(NFugS#iID$6g~Na7$Fu;DdEuKX7sv#*cmA&CAV0%bTIylR=Eegy+RHbjeOxQ zJ2y2JJc@Xk1aBjZ3T>|j|tG=2rz4&?vJG(Y5O48 zN5G(G)=ce6>g>IGG1f=mG<$TnQ0OnHQ|DtZ`o?=gQzTO6LZxQDahP-8W z|2Vg`DcTk^SHnJfY7iE$Qy;swR;w-tZtAFTE?3-l9a@>*ut`4&p@wy2o{s@6*_iT$LaO0wQjmJW97D4vt(_p@DHjX^Zs z>dQ{|Klp99+kUzwf@N%dHpIC@?(*rUAhCE|cdG0}ZgEz$I2&FUUFmUXu&y*Dz5l3L z^q`hm(ZkdehpDrBRWRQ9@n&SX#W}WqK70WRPwTk1=$3?D=q0$JmdFlfhEnzbpOVVZ zC%5kJHZr5zypYb(hfZyKg(yUHq}FfmC0%Pg6?|dyBDE33GXik4x$}|a5+B}m_fKSV zV2v&#@s3d=DU=eil) zn^KHEOz$Iw<24P1-t z?1P5rmnptOmv%S*-g7Ga+GcM*4JlsnGn~)2=H3#!yllJQ3R;}XEt2~z*~@pwZslM2 zM%hWGg+mOH<7`9Gq(5xk#OA2yyPEIkBbeT(z$eB*?>BTdV@X`RLcdvH9LQHlP_Y;n zmGJ7C{b}(_U2=@O&~AKtN`(}_q{)2U4Dkw5P!$&`TuLANA!r}x-4a{QW9t59`cMmY z$=B@%Z9!)1ljfjRKeLXJWGRx7@!9(@F{C&o@Y#G?Rwsv_SH16v4>o3MLQ;lPH0Z{w zQgD*#_Rl(9xYoJL*L5#8>`o4Ljz_ykX(=8ikG6g5>!9cuxi5_}*K_K7sK)2#NprCV zq;n1!QK}(CpwqjjGq1R zR?8c&F<#i9uXrSto<_|-EADO(;f;O$sNv^J@wXkO%XJ#Qh1$+v z;Rw&*))03yh#z9Qm*}o)mqqQf^QzfuH%xCgn?BK`o{0d)MqpF6}qSb*= z3k3#oqluT2>i$`mW(rSz*GPXGnCu$UST#H0WIsfCZ2jt3y+)@ulaOY;g`wfoS~uK5 zU8WyVPBfIr8G0qfu9??LhtNs~sqzHc;G0?TZqE;;6jZkwWA6;LVD-(!jfF&z=@=TS zvYIBhfj2RHq=nr8Se_3M#65Q1}!QI9VZ^)fti7EGf~CX}4pQ!E?uRxGEQ>ZBg_ zj=&yt{|9&Fc5dYNYAMd6J|L>(H9j5*`DnX|<-zhbn}gNm=HP5&r%zyVre7?|av)Ku z%=9~!jfzk$9}4fvWrk^oH5n&i0r*Z1+0aq%;ddMFp>qYrz!YK0GQ`xVCR|BjlIvU8 zV(iq7(YbG((=Ie*FP1wGHVtEIE^2MZE1k9d0$9;6u((rMVh|{4GMS~$K~xc){E7Lq zwjQ=3U>$sG~yp>#drGE4fAK&5?DH?Fxkyh)BDU zu>>NG=5{y$r#(qjgw9$9B$*?JWHfmO14S(xpq5m3oAKfd`R6dM71tYhmHLW9LaRi6 zi>&yA&>RVBiWOb$MFM8P&Ql2{-S0p2S3)xh*b|}o*_e+p{EN`6w+XBr0c&W*$)6j{ zCqi@9Goe}Tnb1rIxL|nyYRUiR$M|M0uVrFQ=y3Jx&z z>ne+}Jx!S~$iE2Zzz+xHW|42eb~DZKUCoB;~Zm0GhFMMK$xmaR^#kU~VgU61>gmf9(5fQyaj9iv+ z&y#FmR8}?%df86l=!ir}F8y_P`r_E+4jEKv;e2{duLZj~{Gpxr#`yBub-~qLelv?yl3X$K z3I|VO>i~HJ#uZ9?^dE8gey*ymk7~?PI7(bDKxb?jk!~+#iLGNLj9vd?R<=N zwd-|#(Xh7X>+pfGWcocxN-f(9Blg|aA2xDBr{R)|)^PHnoJ%@+0TnIqlSfi)5{5UT zrdn_6i`F!hn9h+Yse-`TMigNpe5~B0qwLfg7G9GyW%~56LtR|A))U*ch00cdJe!wQmN2K^@cZs{0DDerPlpDu zXG=ffiNc?j{=dsp?}MKUnKsBx+AD!UtK4?PCYC7?s8CaUu22&@LDXBrMe>8g4Ec)) zl@9j1LXEp=|IZ4wzF8+%!i#+@>IigcwkU2uPST)NutDHSPR=dN&*d-ZKw7@*m~Wdu z^sbHkh%O8P_w6lf>F!@DFhj!jy@!<@+77ab#AJZKfBJ760`Dm%qh9^GoGa+8JzvgK z#T8UEqESqkOkbY!ipH6BO(F(U^N-oI89<{(0akvhO5u!i6@ShfdA?S3KUn90UL8A4 zJB|jZ%}C*DPU2M8=sDbzzJ_$FaXlIE9Gyv#eBUYMz^tycI%+jS-<=kvvjiAB%{MhT zo862jr1_^f=VtmJ4=GT$qkfQ(`X!Dt?M5;AOlPV|mXY6n&Pwa2%D24^=mIf{4~_61VQ8>tW2VqPk3eB#t~E`<0Gl8?L#;hKx;+A+MrOwj*?ZJn200 z>!Z4pIs#EIY>-C3q1Jw{I){;fJ`xE)i%h%D8xxP`6FM&|uw1Zbcuxd6A!32-Z9Ffn z$3kh;(}dv#Q_yFOsYCTrD3Y0K%q-vbCNH=$j!}<7-DFzbhVfj2TOkc2QK};wQ}|^! z3K<@e0^KWgIR!c*(jxuOteWNDvQ#T@#liVSV-_9n`@W)i&x>eD>|n-t(mSg5!otGt z>_wjPR$a`w$w)$^c&Ba|l2@RbsKT@-MIh;&RP0R^ID5n}aW|Y9bzPX8sA0|Esb)Hy zP)lyc_D&>tF3Y{CqI;w^wL;mg=W4FLIEc!@vl^qkP(T8oj!GjU;};J>h|T6=WrDcn zu_>Zzv%mK2>|Qe;`DxTE}Ui&MIjJu0l{#i?V4H}uf3EG7b=SWfM5CGk|zeDd{G<_F9% zZyDGsNC*_xUje4e;8SD(`VWGt>Wq^q!VUidhxE_{7{uFxir$RvM4&^h`rU(y zws*R*<}zCRQrURyLH{v_MPBwBZiOe&4Z(rJ#ryH@G6F2UKRQis<&7wvdo%W;hKtR0QK=EKWN$Z-u$*@pq>Q*{L^*PE4Tg|MsOtLYB#NcH^Gi(s^iWu=0x|wk4u>2Da|W z#L@6qRmYft9Mg5p`<_cLSJ3hJ-PaOlZ7p;(8n=hn(LFfsT|;+)VArsocQQ2B3cA=Y zzeqywHtyRj0LD7mh*u7VoL9!Fi<@k|?~*Yq`=zAjeiJ6A=Dgp8ouP}7=2QKW z$YxQY?TH#%*2Q_XiffVXxIJa5me1z96`Kd+6G^U46IesW$k=5-m{YgB@i@Ql5u5j= zd700pwp{)JU}$K*G9G_RFjawLs*3$ps$(xgcBkE-YjrzVI68EprP&T$0y>AtP;Ig; zq)3j9k0EMCdRJhBn4bNIra!8pW2H_K8!82RSHlL1oOh(2BRsXdou}e72osO!&|@qE zAs-I5GYfWoaS(Ocv1a)(v7qqa#*PWfhI!20=$XSoY>^aI4UuS75CN%b#?;vK0kL>_x-)dY5>g-2CTz^OG0LUP z=zE z^4L4!d5s0e_b%6c*~NE<3uk6a(>fm|eBFB-se9(XJeYqX5x4ksD+}0828yV$$x*%*J$)x<*I|hmsZLlgc zltKZl$-#hr6H8EGyWcZ2lYDQy9a1&(vjAe2P^FJ!q(klKxrW*AwX~eg<1q#sxP(peMxqhIL`cpsccx=qR98Hh=OeN zsnoqlf^Ey?rU5;MNR2$ZkEfBlMdWfDdfqKM*UYxDK1EefpJE2HN{~e`$pV#@1E9T{*BcEWME$|_NjtCWh$?gjEY#ZtK zD-H%_+b}iT+-#IRa<^5g0ZPzjvOD*eBN!1ezNzUH7pv|=&xepe7(iEIh0HQhDMD8s zmd|=&8WX`~WdbfwUaHK$?ME4Efj3|Izu%84J`phfTl>*9;eWLsrSE#~K>HtEj6y1< zF=xd1mnUI<#VHjm_HD0UMgs-2j@tHvey`U)IIW~mCt|!G+Py1s#`uQe}Fp9}BRyl-3 z%zBYtFs>x=PD>&`DyuP%N=Fd+Fhy;jP3L0>Fx^t zIEpIz6?Zh6VWX}Sg71L=wkk0*kLPfc5miy|x8V5o3K)9!UvgXPYB9|GliWrsK?!jg zJfL596Ww#g!}s`&rS-(Ed<$G1!_|i4%C_yQQaf|ZldsVqf|y+HkPVx@G_Kt7dZDFC zI)%&QOz&jE!>--nU^L&}`DQ2HzH??5X`G2h`l4ZP0|MwlyWF8m(v+S00Q8_eFP=rY zo)^zR51LsG0qkkt9P7z*dLZICJ>W^#9{ijh_|x|02fP5J2cm&TV9@#uRGtCpf#074 z9=CKi2R_kX>4Ef(#k7rdLTwyPNHK>tzv|ZdOg*%V3(a=H@sDzX?MpYlhl2*Uf|I4> z?6KrS+11-#I=PXJ)v(NBnT&hz=OrtLD~HHj3jEC!fJ(qMvL&V!pjvwbrT(}byfggtTI(UEXy-AevX58^!G#|xm<|-|?T%)b#g$^I^Nc)y zu&(x_k*j8qkI^3nOeMy+L(An(SJ9=~T{lW1_$dBl5^7eiqdz*tRCa@|o^>-h(3fn^ zx{He##2+uFwj_Re#`USfr>{WkQwDw{nr920#JraOO~{?0eIntLR%RmeiV%eiDLA)# z%+?~9p9{>7e!1FbP1(^eH?sA|1HG#U8>Hs`#A(W)`93iVCujUmZsny_zYdlDjpO6v z-QCE{?Sk%~77yLl9T<<}8E|*OqZ%KwF*mtK+^63}S3A|)X|PWcaDf&8!WjW4Avs^8 zvDhiJ&8^AaJF#V48`zKQWSSOgl2%QTsA9^q5d*I#4~IG>`5!{~3;nR2tC-#&S1)N_ zi_^HRFFYht-8o{rlFhUZc4;D2#-?h{%`Fd0j%>MO5pU_uINrIfsR)%deO+Rie5oh8 zf|u|nI)SBd`ub_vRLX}w|G*{IN-Q?YFUq(gp^zNFC2f_G=ka6cj_a`6eQ7mXb5V=> zUHtr~pjxAb`fb92RUfB|@5SlFxLchyE_cfsojZc{tM96vy05CN_+mN%;;$&9Zv!6h ztr&Aam;HTCZWU=ZgraIHxlTe%K^64oOH*o{jMKv+HTnb@FSrO40hocK0MUW#v^(r^ zh1Z;@b$D5B>gn?ZSB1mfx_sfGO=$C9ovBLVS1MQJ+9}JSoRGs1aqI&uk+b}&VSCho zRalhLHf+UKz`<5 zKt0(fEf z;$qc=?43(e^e`8LPi38OxJ&hhTV@yRu(6Ue$cyJmBCvj3s`Gaf%FWq;#SjsXVlWL| z;I%jwLHD~r`zh-d2}*_- zL) zYsSjXH-dTL)U!(|>StyYv`bR#OuJDLXsbFqA^&Ufq|7LS+J4L_yJWm;BOd=V1Qq*; zbY}Z;>tc-tbTc-Xr5}v$INbeNXag$Bp4DE7ch72X{1u?~GAbyY$Rxo7b2?Cw1{g3; zNC=`j{yv)nU1tFFyNKg=Z`uo>=2CABxYWO(vj)6HGFQtFxYx&*sqv9ScJw+8as3GRtU{JWR0xAO^CLHI$b zfEE0iEm@j3_VJTMQ>xw?npw>UUQxkO^Oy}w8EI~G{#3D*<-!Tnwn{=>XG{f%d}jv% zg;8i?_=>~xjwK9Ok&}ioRV^ZfRCAP_Qjx6WA5UIOPMf9eC-~fx3KxYN z^Cu%^7y|3^(zOzYgt;ca$x#Ys#A^ljkvPn=Yd-xJA96sn(*92M$>Ig9;Ar~J>1(kG zP|o=d#-tMJIOA40%%oz{@`U=vCYz?gJx(#^W#N2QNnjxjU9w0JzzwGMj(Kuz=2@m9 zc>F`AS{LKo6LX8D5-xv*h|gwP;EKtq#36`^11^eX^uc!qe$IO$U5wv;ECdl{R{=rb zMf7c0J4b$RBrX&g7%9W5^6w zF~%f=hqE-Xz)fs2-Cb%AXL$?Qdij&?3hLNJUs3r8C4~W4C|so+CZDMdO}4(c&ZJH`HSVVs%56NXsWFHklWac%>1h0 zDjYy2l)7zGXFO55Y2uZfvxMJaPRP z%S<-FjJFZ`#C}0~`Rp-72mVPwBt`^8;@FqJMB*qF+HZMF`EY^cd-_-~mjbkwU-T-u z-CTDo@{d)E_R#ve^HHJ|OA|02d+WM4Rz(Ic;oR18>J40PyG@x4Q-(MObe_Z1qTzYfk&pjebzeOMp`p^&(I09L>YIExBB2h}_8sA;)qk98Zm>(Sb zE1~+m7OmiG@qi;AVkVe1c|pqKPG$*($snYPN+8Wvl16kEA`utpU_rn-L%+aE86smbMEa8Vf zlq4F)^pnRwU?7W~0B)UiZ&$VMhtN^a;{;Lb&VxN|&k7`|#tYAhA2D}KBAQRh;@ z2K^pG!DmWNO|3l3?jl<%C&SKQgKZ}%!DA0p>UPda zrc5=sYY!96;JfSz?DtXUUj>R_K~HSo;M2kfw3RbhQQkCpA;zmP&tE2NX-4KSY~i>9 zlR*?NZ?rTT4$;QT;$W5!-eSqvxlxx9sQIl~Gu3uwA4FO484)R}(Uwsm7WBS;;hLZ- zlY{YwFG1?3yvF??-%zP+Q)Pvi7Xd|Dy^2Uyz~G`AoqoPgz!`(OM8npDN?Pe~ez9Ig zGo^@5S?BiwP`8z24BE`VjcU>cG0JDx#M7fW+`Qm`i$L1o|03_Lqq186u3teK1nCBm z?gnY;?rxw-Jl!{T`*$P8ut{8~+lUTHp;EdL$VhN!tzvl*wxJNC%E<79L7*75D?Yz5(; z#^0|?&ff3r9828f5re*Yh24z@TE;;#YH=fFa}SB^F>72hLB|z{dkz1Dwp4pGPiqjDb$J zAy}Za%{4Z70JjCA1Y{Qk1iNEO#zMj6`fxxTi~==nS`UZ0h8ED?_M`Jhr+s@+>ZRJnpo(5V8GMY3o(&@VLX<}SuUq2v z?yXON!@cy{Lce$a__x4DzV|L;yWfW*7GcO2Eu+VN43@*+sd(?6bKHl7N|N3PwOI6T zt=b>!E&}^_*p2Xe2f9Z3jg(sG6M~OD76PIh%K#(f^F|HA#n{{elLpoFb~Zwmilhq` zafdroj#+5Rr?SRkusEG1WG|&33qWO?B*jq=OX1Gx$ajAmlA~}`Z=OJhgGZ+ zdT{8&KEB? zq$AQ9ajw9KHsXPy|7VhUcEF9x*6B^Uppc?8c;^;3R43wsMSyj9*q3f;j3djitMt9# z%|g{tj&)6D(A$7z3WatJz<;}Gtk_Wb;J;mcBQQhT{k~KYt^WFqFkgYRnDN4Xtb}KK zN4pJv2UH7d{X^SJJE(#9Fd8SpNr?!_>EnQF+`yAC2?aEAik)UATQ)g#ku(c6=OytY zw(#0lZWyyo9rGo_oF!khLTHdX0wG6+I=46CZ0Zxyb3(C$;rntVEYa1|8*V8ijZ@3GZ2HTABs-+U6GW@U$%K z4Sqp8kdI3mexrytLLba$?pTjF>^Tf!H@uxDCh!+Xr(PnzUzE5gHa>N5UaZJFn#%dw zcob0c&N(zGURwtV8nOwVW^V!(tG&zKC8pZQQ-Zu zx#pHPGqXjKjAFoCqFJoXnBTxmfq|+WtP%-LhgTkZ+&KjWilh}~m2i3a$V-g8oYm227Wc+u(geD{~Rwb|3+&ycE^N5v4ZC! zQBf_K%G2bXb3&2v-NV#_AG(lvXwFi?eVK(M<(~6GlTl%Ed2Q5c@2B~*Gic~DsBLFJ z`4&}%GjU77k=Yv5W6Vsac@tqaIxM)gA%OLz%g{5PHz!eLThN5Vb=-C$i7@7Z_&U)F z1={iV#6!IU4C{Syk3^qZ*P{k;|6a!#&>)6Lg7v}r`ZjTkcxMel0Vfz#<_UKlRAR@4njfh^x9(ldp6ua6^XP zIE}tO&pE}OwXs}xaNI;M$9F5qqWN)ao^>co<16oL8sEl6UkHflUMsA5>V4fh~Y7hFw6M-y|JW;@}K2-kX2rx!bm#qu0` zaFszP27U2lQEcG~&*25&w7QvxZ8`GCiQ16zfB*+{?ZE1$qw9H}RcCDlWg6bKf~)JadR!W$m!Q&u_GBRHf(+(QJ^%eraCB`*YxI(tFq zZY;#(%MFQe0XDY?=OXmO;#zYO_MgQE_Y+PK^^jA$Ftq;0l9gM1(SFl;@8E~82WRQN zQ@C`!W$-SvEq-gLpv6Ul6)$QnQNiod(=o|?wO?cvym2(783kdqQi>@a>d)d&$MQ*2hM0Et1(aGDN?}f%RhgAXRJyE740T-!=FoJ7>kmX?@p z;LNq>+_Mian4QzqVoNTWylJV}s_Qur#1ZU=fYk#X&+TvfXm#t^rrvdWZ_~(GF+J55 zr9TJE>9IteBJtX?OdEw+@l{&g4_8I&zTnFlgUWfNzcZ15PsZisW(b7lV5SwJ_Nx$WHE?$OD$76|$0 zmnr?Y@YAgJ7U|m74@#9bVP`cjSs_lE{3Uv?U6o*MLHjo3TLrW7OT}pKt!H>!LjIL_hkB& z;%i(R9vrjcLo@*K^b{I-ZPgP?>%{}jad@*kIZ$Ut|6iWjI$6+1K0DPxkf}Gp7$yQZ z(KA0@eh-ABXaOAPx6fYIKZBaMwgUz{^l~EYs;iE2K#Ln_jmVJH-z~GJ)Pr$u9fMqb znlokq|I;A)a@bEp%NpufMB7f-&Da*iL;zz441>`AuTAcn|FuUPflJRN0^B17)oO`@ z{conJH(ZdgjK0Y%oQo+_N0EkL4QDTA;|9*1>WAslCoAC5djd&ud+~RoT#7JEm{IM; z|8i5UEj>sB_TOe@rLLyaP5}oZLA6OH_>DN$dVgVD5e&Ts%?5QVFnshG4UO0om zrWuY70kK~UF7c_ld{UuDi%>T05AjK&Doxj4C}s&zWnp$+8pc!5lOY1^QG)@;;UP-N zfWETubT3pdBjxSh69u*|%u) zcnF+9zGo}Pkp%No4bFSvQ+FDgBbAwu#iUG9%{!d*+>1$SdM87eIf=oqA5I@K>YJ3p z;5&T}rX=UpVxkIXvQP)m7op{oF3{@y$V@#$H0FD#81*^y1w0c}Zsnbx=qVc$HT$L~#FqC=@=Y$m4MlAF=!fjAtMIH> z8KRlF)J`nBM*%X#xHxQCQBvX27&=0+Bspw88r{RaqS|b>A$~(?Rfc;JkII!P&!d3%DRe%of z{gW9H8fTsqn0Wd*N+Z_ij zmWLTXcAIP-zRyt(aNAl|V=usS0`6MC9*g!rv&VufQIF$D_E5@I+R1QiuEH6sc}g*E z__9p;ZC@$DpgQz4k42UVS(u1?HhXbO7i7$}nNLNWPo63c+h+P!W3&m2;e6B{`eUIU zsksbhh5UKljz@ZWI-+P$(^k&HqI6&e--|$gj$Rib>QOQC?Yro`;)V59#(l1IkdfWq z(P@7px+*C$IcUPGOweqoH-bMBnb=5i!prW$^c>!)jkrb1Ej(vmyOKHhiv9&*Ldrz= z4Zgi+y-(e^9SXvV>K2~-got-Xb&FE^otQ=;jw($nAF5thn@y0Yy3n`KGO@SdQ(1c- z<@@;EdP9jeR{wG%MA~M0nMu#W9jW*GnsH3?4^%Kg*BuC)m&xlmxBtLy(yB_JHw~9J zBj}ip=*L9Dt4pYg4j5088HwB7S5|T++nTAeQT5$?J^#fPc~nw_HE57+@7v9@QOR^k z*2*LdlQT4Fj-Ik)!BLlRBU9qyaj|oF)jV$fTzf`DCJ(Q%BnB)q3p29X0<&#$6*V+i zkCc2YBIJ$BWU_+_+uBSCM`73y2bA5Dk2Wqx=%0@hBycds`=I;|_&DxqY`>=9gzv0* zHepS&gZ+8OpQR!$T9`4Xn;rafW3Sf4dg~q$*N@)1vWJ{7;H}fT zfB+r=VSKOu?yY-=_2jKn9RFhYhqo?FH_z1$sBZ(jb=~cNrtnX1-S7IgzkBO|`Zo74 ziMM<@v>$s(%BPDjelm|RZn1H2s2_xCBuf}>Y?*9tg>D}T@PasUIMyc}+RIU`KrMk_ z5dOWs4beJj_^OvCwy{D08s>KmXQoHg)6LtTLgHtfhwzs(8$XzU{4%})1M{!kWY7_X zHEL-i`XXvH-_2Sd;5}UH$~|cG;+f zh!^gKrF}1)Rj))_1$p7yCDg-v+D3jF+nU<%P)X3NW^mz^^C&1}+~(*Pg;@wEkz_vz z3fiS_vTb>Ik6Nv_ednTl2;;d91>UHjeeE z5lLvfyU(teuw@E5RP{{o$s1#9P`CFOC**^}XNEnHeoP!iMHnc~4+}5Hw#QIK+rb(4 zE>Z*e7hcMs<@Yd!q>$4m=Pq60_-goEep5KMO`4uk{1$_)_`Gd~Z*09ZT*X6CmQa_O8uc#ed7~0q*@9j+k}+*NG7>HcEt)(;z6T5MJX@_KiX{q z!9G#p>OIBP!YU)n2{ogEWOMR2DsfypW2Uo8(zJEf-ncd2;8(CHQVlW-5Usp+%7wV- zDhc$0X2f=lx;68AGuDd|dWAh;p=pR}LVxgi6)r^C6 zHi_(*uiWN|#Ldk2lJw;s?AO#kSqo1j?iEgOz!v*SP-&ry>{px4(`t?Xk;GlY!!hqM z01IH`keu=^Ivz4l8KJ)A-A5TueEQ_1XuO=^M?SrE2onJ)cX{#mTEklL1WD!HBnte1 zQzblNC+I4hlB71D7hOKGMLD#wm}N16_rsuDY>%}$N%#wA&s{1$fzb$k3RpEuO!f>C zWB_?U3Ps7Et1p%$O;g>~ECjX>JKx1`#+@c6B^5e|&|v4312XfOt%#jt8r-u6+=h86 zM-Fl<+b@UD0>hAt_x}^>ScR&U+H}=N@7Gcz@OfB)J?{rv809*|pV$b3(8PBVt%TpM zYX!VI6jf6V%2~|tQF!9dm@dp{f1-EJ!4+qBZqQzDD~@QW>X8*~Iq84}?Lbm$wNNUh zeWMWce3W2vqvDTZ!ny~#WZGz$-t81bSXndbdI~}#+G$$i&?3ok347tyL zt#k@?e|10%%NIpSTiULX{0fbo`3~`3SMl)X2H#aDD*_1Tr<84JW#QdH?jvW_dSpKi z*m49ePg{#~Umal|75gBNR8RJdtX`J7F~vRcgq**#yHVf`wma4#GIRL)^`?vDopKyf=dyIQ`nW}Z1+sO$5kwSDI zw*3+)iz<7?$PGq&yiKfVYp1>MZ^)8&$>|N9RO&^x6K8`<%t)T6-z5#XR&QAO!?m)% zq2-$t_BqnTn8Ja#fR)VGZ|8xZZ5s-A{0xL&)XkiCvBu6!2ITJzqf9kCUw0-1vL*lL zc|yz~6RS*QxPs?A_5^kE^8?=WTfQIl(fI(f(4g8tnej0@R1A4tb6&zT?5`PGAY{fO zU${AsKX6*BJ7~UJsn=PNEb>Fyq2*YX{VLIQCIi&-Jl~WXc5}8mI`6whk-TahvPDkj zA*K82WwYj(Rc{GK|NPZGZhca@mrYy4_G7Ial|(39r-G-B-4>3Q{z=-mcUlDEu#E-& zQi6O6kv&)*+jDv|eW7&$;M7?n#2>fRT8YHci==G76WtQ!%b1W$QcKR7S;UGduNG}2 z%eTQxvm$I2m)OOL1##oXQ;K>;VpP4si`4fY04HF|71E++V}{kmrtU6|j?>iKUfwpZ zGmzgya-OQb51MF6T4uN$vRP#2*QeH^!JQi1SQsZI8Yj%ziSnzAIqv`iwL;n!Yi7^M z%9{i?W^O%#*Cf-_&!2!1CubKU9LUf~`ASlUwuxb{Ob^F*>8WJoh z(qFr(k!H9iq|X)sz8u|uF@vh1e=~!w6{>X6S6im6CsE0FD~as5UuZ^OYl;)Za98C{ zrM>d35BoYmtSHV{z%!BUJNS@`?lB^%mDy@vCmmBo&Ft=ha%(%bT&ICmH759EP9&T_ z^pc~`yDE&+G#+l3>B^f4VicoIEiQNr!(1Qhl*kY=QB*Lt;^rOxD9M^}1O%(l5M@`= zW@ND;>J+CMF5!+C_Xw=Mxw(pNQ6InBFu>0yDDvF5=AOOJ$Udsr0gB+5HolM6E&eKk zTa%D;A;-*3>|hy{zU^jZ8e%%TbgN)N3*P0^E{A0g2gTJR>pr+YHG7LxG1wXRhX*#C zbv3|njk#JjCURLy3E)KMRx9S;du^!>t|(@7dy&_1|7BTKpy-a3_@ za%TNrh+G!rVLX&g+GQM9f{@E3YbepsEfLhyTJG5;>`nikw5!VQ)b@NeJTB|(=Vqj8 zHtFOE=Di;-emQA&grRUq2+KCNK}YlT?c9$(6vgBhe6q{?H^7JuunHuzrPMpP@c476 zXktUhbwCn>u#Y5a>aS42H_f{Xb@GpRL^2u5>zB&6+Yx zNqszd&;xmHnDb^xD*}Ag)0`)YR_IUsFiw^oFuNu>9LNzFR(lGI|dLy}d6 z1CzMv%}}6KN5sO%pAb>2oE||b^v}8m%~|hpy)mE}^QE^$;)4PQf*6rJx6w%2PMfh*(z_Wq@ zFKx%1Ndd*fXb7cZ1glW&s!aB}i`@g_{JvX-2TkTfW%Wx5=i(1T z3$v_6m+L0wSL5F)6H3Bge=%hi^|bXdQp-@NL)y&pjSv4Sn5p_ZF|H^JB*wRaIl2)K zGZ|<&dy!wKyzUlTbti##Yg^pG4Bt_HecM=%w!?iK7f;fcBD-xT>Gdi*RBKmTJtJe;5gWpJRhLgLSPnBp*k*bx6pE$xKl3Pg!mbr^Q{Eh}wg_bB4Ou0P zrPK$TvziJ|^^#94v85F`Sn5{HBtJ|N?nK5VGm0{-w8^Bti+ZjV2Smy#u3*s$Ek=*E zGC@$LoIkG7HXi;(`^)@s&WP?H`GcINzM}Fs?XMr8{nfa!|F;qIe3{Ow=8xmWesZTW zaxdN>D;lN^At#dNrMNIQOB;G{rwBy;vctLrI2Z;Ads&xUYk>r^f6aXPkY@=m9KM1P zQrkTmg6$B|BSuVZBo&G|$uS*Nk`%d09#tSOJsNTI1RlEb9k#sqwOLh|CzBeCt;%hg zU%)H=)&^G@+^Yuku06V8fi}3mxncikgZsk```88txMBbA+Tb4Duq42@Jh@@xqqP8L z_XD9GZSjX_>$Ka(hY>b)QCdIMhmjhjB`dcj`ihtsQNJ(tuG|}$47Czg$~8}pv8RxC(8RX29Vf_r z&x|{9bK@7Lkn`EfNH`oTNrdamI0lci%Fm`5)?U&M7on`w>Di2O)G$%Ar0Dd~(X|+u zew=sfpKL}~6F&s^TnmTKe=ZidFcx+~F!HI_-AN=qM)XmNTS4)o-Ncjes7m8vk0MbZ zD}1@Z1f|WQU+1VeB$5_qCmb{?)X~13L*d-OCWiQ_yHR={g;i{A{3VqjFB+Q(ly_t- zfqv8{m?Fv9`>bf6>qr8j!5-+}D<1MK7;9DaUi}wuUeD@dNwxKj+cP?0Y4umd!3iX~ zyt3NI+YsaEAtJ1t!CZMWT3I(%IScjSmF#4n3KNpPNF;%*w}2{blKd11Em5y?^4_*X zTESkV5=UGO+9KZsopb)&B4ik*wLQ9Z{z-F!Yk9 z;^cr2^16qhR*QQx|6dGXx#jJ`d5A{_Fh9@*-vRy51s`PD_HPC-n*^}Y`2JTI;(}fb z?b#nl5*as)U*VrueZLsML1us}@ZSvJC+>EbhM=UQ{T~cqwya(sf+q&B1diVFKN-L+ z2wl@u^+V zSgEeE#e`acs3-h|ZGQ(73WI+YCRTSYucru_=%P3^ z4bSadL9NqG-l?b?&0Je#csXRY6N}1Z=HMY!=I0D(PKJvpP{m-s_2a;W_B<#mPE3^JihTB(h!6EEACpix3?$J$^C zD_;^f&=Ggaif>t$rP+$gYw6$)bp)6P2X!cZj zauNb_!+yTNyQpyTcIaH`EI1u~c9El+gZTs-^K3xMD5aaH=SCnM~OghSR($+F39p$)9 zu&>0)=p4_94qSpN|2wCfm*X!^x6h>{(ZD-iSOint;J*VR%9Smx*d728^~i!+M)twm zen2*_R0wJrEX@ss=}&S{sun2_jy3xWmwAIBc>Ng)AZGfmHCheO>5+xN0SmSZ!))~u zC!o1k;I;Hw)0^MSAhu+M2eF1`w0vyzIV#qtQuP**{V)=NmcQ_&TmFsmYr0_zy#Okx z)gHde=CWkzlCBr^44jewJp2e>?XP~jwtDw>A!wm39Z(;_5`)HUO6WW{joVIcYXz># ztDD|)l&k)fZ2Sils8vQ39Da5)RD;-Wj%{MekKx(FnvY{?FT-dU@!NAcA*qJP7>CR& zOc6Yq@c&xeM5TQ~<)acuUNVA*K<#WGp`s~2?k!5(WPTb=j?I-f9%^IQFg7G(r8IBm zD+}7_ho#nJZTKNF72y{+f)uOx!Q`X7(CXjVPcjPqt{f$Z#MvCO8gH%-DM%at-b$GLEU+S{{VGQ{tMLoE%G<0yXSuf>dsrxNQ$1SdsS&!(lMip zn^2_d1VTx*@hs*PdpK*{D!eq6(Smi2+Q>zH!E0guuQiz}wc*~oP~_{t{K_G_sG zZ_Lu@Ta~_JM_Vk_7&rlR7RunamYjL6tLJ-}eecxhsM*Hy;O+IIMiJ9e+uf~gqOrB6 zQTF&Ma_SOZtc;hcOxI2k7l&%Z5Y>Brh)cW4uY)cD5!*)n6z(ms*+z)aMObY&141I;hE@{LFMn&Ci) z8B?!@7pU4WBRafMSq66Ku7CLDQ^5Q)T%Pfkd0iHpUqpRJ+)YrA^jzoaNyDj?T_)2}52m z;tSiTmk*SvB+Z$J^*fxB+>06{LFKd|gs1iUM@R%+EGapxL7Tr*y19$~L+LJ{ypIe1 zlhQqawjen(FXXq;we0yS*Yjr-C>NjjYsuVgwQow8?kCbtP2We;)GzIkewVQ83SNGvUnrFe5>s5#T7o+C# z#{Z0(`~NL!{_<_HwE@&^$df-bjFq*ZXyYh z*^*du7+l3T4r%gD$%+~$Ms|S(lve_@k(HJ2gKGc7-LC7qGQ0mLb^Ff5@hU%>QB=K0 zt-BeiI`&gId?>3F%2q(h#cEI$eot<)gts?}41O6b`|_3Bpv-VNi0Cjowjq2p8qXa# z4<&vTi`YUaZF1COQ{8~-v~}3|`>Fe)kFt~9e9uh1Dys|%Y0Ws2U&T`Q+rKfGt*D{u zuj(>@H#%;soFv{VHjF;v7Lxd;r#!57N-WL;L(Q#6I~( zcUa)LM(8DwC>TtN3OvI;1)T0yoQ~&W0ex<|zK_o_e3-;(<=wgRxy*qqp;Lm-E1J1; zEeD01V#f^o;_0wt7C&2pIh<8=)N((a5(XpIhYylKahrj02U-idwf2WL>ivX-cxa)x zs5!*N{kLeO_PG^!yp56rHl9FJMly@KkBgs?1IfKtL>bqKp$8MA32Vf{&ISEN#>NXShMGmY1d}55HH}d z;ax~^Sk?rI)p0Rep3$u;6;|Y!iMA!yGLI>?cG`n<*7Q7W>B`{kjUVM=muCipS^?oX&aKX&g%&c!*C;{$u`M&PNEcNN0+rJdv&~KQeCozXRZ&GWZ98yN~;S55SGj z#qG}R6w8TyrgnDNuD84-fkqYUeizQt{gb^_@OmG=b?>kC6ju}Jr3`kle$;*MVg zpuQ$@$2*OZ;(pjEcNh1yBVlbT$_5?YrZ)WT;XFo!`ST==71c&5!2|vg<|NF6wv~^x zz1pRx?tF*{w~aFIV0{N&apd6i2y2ZJ=gh=X=mi{cKivB?i+`1}?qNwoi0fz_E4sFj zqBDAXd=oN3Gx}i#Sb5bMp6lb~d*c)HGpyX|Zg~%Whol~rGgi|7WU85*D`sO6PM!x4 ze`<39;?H8MZIc`Mpu3q7F-(L|y#7vTjHmu{ z*fg^XK`gUHL`BVx8K7bXUg@D?h1iX7OGox7v8QeVLNjQbFv934TI*8IRl$&El}8M( zzI5>+EYOa^RVT6D?fCN6tiU~)t-9~3O_H}Z&Ri!kDrU+5^DBg;(gKXRv7EVxQB5O9 zV#iGPLX3vdoQCk=p~4>-n^Jjp($8JtAyZPcG#YNL@7KD=Rp`c;(=xu~Tt4)*$PVf+ zLOl$PNZh+DHq4gh=!)xJ_uZ`C_y~Mw9lJHfy2X{0*B>iJJn}mVxGY+G#qmvB2Rc(* z@#PGoY(!_lE#1nA*cfeRoC+E}(#RwfdZ1{e3L<-Z<2p@c%8_v0;LdA*4Nx3S8xYAx(^dCi=Q*Y7ozLG#mYYc~q4 zZ)T`U5R`;fm%=yio2=G+ya(2I=#7IIlL&F z+;y3azP|GRTR6ZC@ju~!HO+~vn9!3(TPTb5gUx!0RNJV%Jc-aOH}R&QI{7X!oTI0; zd7GIhzem1iXV=p$4F5ukF1kFiw(G?`8w{NhA6VOj=iB0s_yg7^?fl;02iEqxiocwb zB0P)S53KDF^%b^Upa%?X(diG?cCK98v=YGD1_}H(Ya3teFV?n*@t>^iZQ_4(wv|pN zW*>6R&Yb3XUGlnY)d@RC1-W=b0=qN&S^?VR9~r!i@wke{)4z%EP$z~)#j%O<=_O7N zF)M#^`zF(X>lbE$TVORa^9F6}%k2MFVf;iH&?TMIjtS|NL2 z@{%UTuiH1RS*i_!P564NJ+s98G@~qKmxwztd6d;L5K|P(X6XoTTM9pR>C*zHJVe&^A>#|Gti4Lr8_IJ-~&) zefzi|o~sev-=c<8JKdv78sx=pNIkN*eBiiAw=n~cfNA`%(2{%3vn{}Fp5>> zpGO2O`iwwZF#L(eZP#Q%_dw&ej4$RB(ka07&;X*KeZvqOsb>RR_l(3^CB_LBdvh*3SxPnRi+{D_DarCzO*+-ZJF{o7bIN){KY%Mp8*tYmQ>gP_ z{A&AQuluv@N8|ZZ+Yj4+*_9{21^WwY5!Us{d%Tx6?I*36R(f=|$>2wR=~4PZHL}Sd zLA9a9+d7~j4;~I}eRHf6%!Y97lxb{5FKk^=lBS_6dv#__%wkFqZYQIkNscrcx%gJ9 zv~EIfBa5KgG7mIJPf#>kNIe?9zS?Zc)(r(>+UGMfmIQT>*z?tJ6@eT^C8e~unI`{f z(Kd|yuBgdvxY48nV`{}MkIaPECSoyYilL2{CsFTb&iPO**Ld>trMGL-ZXtASQ$J(GQUXN?Er~8DIUlfSkwSHgN?=Lp`z?23LbS7 zF=e)lho%a?u;%dL?PMZ^90;YFNf*(mbCT$PX{48wpLH=?j~SfF6++$VM5F3v(!%2V$7EEph7PL9t!?c|W7j@>_3F5q*+;Z7uRv;wC85 zQbzcc0U6{bOfWuXed>_c>*r3Xfuo0|BsYZi!>eCZXFUB=m7&M#Cu zhz2S~B4y`7OnkLGvLr@4nW4u*2EAlYz!kXzab_1$Cf=M zFH5pAl>ycDY0K#-_gMP`=TQa5N&ZCPeh$PEMr_`}luN#2C&pQ#kpA#Hd=V!@rocV)R2#Zs3$Q7|x*;zwO7Iu64@ zYxuom17Y^4YT#8fWIovTh|G`^%-b3Fjf|*gK#c!^;l`ymi9|O?Ben>V zPJf|RF$tA$tzOma%o8qsbP)(4P<|t6NSW69ojp~A_!N*s6JP&8`TQt{mh?T!p~iw2 zN78^C>SdPc#uB7uLuEv~97W`Rr@+NGkqb3J_F{gsOW5@R!s6{Qk6%`hVB93JjD{bYC99jp{$2ir2jov@Iq?|v-v7v-)#$(EPxAjC$e)%W z?e2|$jK3o4bKK>;mJI7HMnNzo9Xac+P{GhkOT1xU0@plA%$#7PUNcpHY8?Leh){-q3aU5w-p;|v#B-pcrQis#Qod1s_R z#uaX(?aiIYc22zhfOPvlDy1*eyVSqR8`GZ>=R)B4*F9zsa`+)ieN5kI{%RXmm(^jm zad295jGJ(Yv}g*Evx&zN1Vz)R7e@LxGqSAQn<5_k(=r%WOFBq6M3Iz{Vu`jIXTHH; z)7?~lLCB77GzU*|f+JKvg8)VbT06INrh6B|4(z4phsudnEJcId!6f6|IF3nO>5pIwcN=8S%T~m{27zu!!gkt4k&m% zKXX_@ajx*0QisrccM;7QxoR3BK~0x6{+Aqbr6Q9Q;fe}{Oyof-z54-p15nIO1+gs? za2M_t5ubMV_h0W-hZ#3}BdI5mq&k;tA54P=RJ_$T%sQ!fTYP*g@_MQnd^o~3m`~cUL_NEz~)RDLh1w!{Pgo4(+5WyvKPL1q4InmjXtU1 zKITFPSr4{9bE&-rMJH?#Y+#i-Z;eMs__SWLuVE15wq=sVoM0{w@;S>#=Tgnx_bgCd zDnx4Mh4s{DxoJm+1QV=PJI7sdw}%u_(4jq_!p>ugh}2;o3}Xe0?1>ghU|rba{A#LQ z0|UhCulRw&t;kdSKoy$!T4Io}|2kK|uO})l)j9nke!vxf&NS3cb&5WtVffQVyE!*s zxnrZ3kH6He>pfSFW{~nRs8$mau~LuLo4BDp(sMpDZGE)33)jYcZ@=iagB{VAo1ODE zVOz``T*TGUD>D4ldY4O4C|rj)qpaVAH;3-2eDS+AocxFkj={o)bI*%6@%xUadG6Al zGbWjt#@KcPl|)DH#d&M;Q8?55yO1^1T-s_>x*y@h3 z3)D>sH%?X;b^KY9H(K^>a_L2U8&0xW zMoEdR9WiUW^y|($Q59)!na7=`vd{@b6Fh>}B0Tk^Zr zb5?jmv339bi;=7oqiJWZPT44Os+bnd1#QeO{5SpZ{o-A$7A!KSlY4Ml>t9Un+y^GN zWa$yRgTV0_`rZSRd-)fW8+x-TQA69^1uuFF9)IM1WHGFZ z?Wn+IIdUUD=A!uZ`@r{f%b0rQebO>2Of(FENyKFWH5{st$9^1{P2|+6!>j3v!Dp(5 z^^tPfAKLYVIq9Xr5AqW-1#+(AmUI}3OdEq#%n%AH7DC0~M4Q;xCR1U2Qf~Pe!&Ch7w&xc00-HMt9sx(L$25xAy z-J_dguir?q_@%Z&vi7+q=$wyQ*AlL}YWHI+3LC%2GM)GpMMkGy z1tc6YDG(+nYaC?h67YvrbAqOLi6NDjizjR|hxxa{z!N>f3GkVy@MAzVK?R1ri`T(}l)x@0~cjG0nJ$t*}kR>y$B zjS@L4E5VCeK9dsrLiJ7s@Hib4lyNLQ ziOF}Pi0oKiXhvRZiV~o6R~5y?M6_y(4;zH%d0|d!=Mmna$80F)1{x&7qDq;$j}+<) z`UeL7e*B;)ff&8!$|j=CBtvG*Lh-jfwqcY{4PJ=ji)w=QrC# zSTjS4Q1fTeV$P>b%B?BtzWhJ7aw?nYHqVFd6`ji~mEQW?V5wVOS&X}A<3~mBG$PbT zI-ZfM5~Myh6~|Wo@ffSJ$W?n7PN2<+h&#Q)1qb#-3k z*mY4Zi%9IX+H6`D=n9g}gxy1WzXN!4y=T;?x zf*pfB{7NgcQHqBQ1P6YUES0c+dw+tXFv{!>{A>8lCSkPMntJq@dXgTduD^sLZyiIaueJc@{5)dxIcHqNw*#RH0YvHU%yPFEKnEb&u?tw zVr9x!fHK7>|HWeV=wN!d#9zG~{e7T_-u`WfH0Q^Ik@y?j*+Y$Mj7KYB;(G%)MQ=2 z!$DrT9CqYL=K*w*?vbO_f!4cLlliW|ONWnIMz-XxZFYnAxR#mMn)-G1N zulH#$U_txx-sH^J+A2x*rSsG-M6jI9xdgpI=w~c~H>!Qx$Ev}Orl7}wE5385>1;5w z*CzTh^v203di;8h&FY&U9pBERLA2zgnVCawgJEPZcebTzmbmLi^{43=d^1zlfOl7v zKEuIa`$hIvJ2htClqH*To4Q;)5%5xnxWCxl$J!IXN~omHRN3zdJhJ{K{LA-7&@Q;;Q%#_fQrY z<^>6532F=LEM&*0q3$AtOYiFiI<#36Fz`35mtPqwxagW&)4~ZJm1@w$N2MC(L-2Pq zVf^upYMZ%e5a3q?3GjT5d(r;tMGzoS@6>A*G%2=1VFeGk8fAjd7+45=XPyjVnEi()US? zT*+WnR=aP6ZN_RYLP8KoPlpQ2FN|r!%kI|)mPYbfZ@S8=?3sD0cfWRT?s(K*-|~on z?>m-=e@w2g!V@vIsw**L93h;M>M`YiPd=B4wNUA$d^HEZ;a^#ng|^?xxU)SY4<3+z z=rJGrqXJopoY8V(B^=-UlQnkObSx^gTtyUm^ShC+5#dL4+vnm3b6&`a>LK?8$u4W5 z@||bdF4A^iDY)xgZSLPp;YL(dq9;TcaLe9Q!wqQ9eIb>)dZvow2nETXMKXB&<|7(~ zXum#L`rbkMxan(DE)<35gZ~$4Zvm85ySIGclq}|Cyq^~`OShku@p^Umg*+*a(v-At8(B{NT##IL z=|B>6*Lq(aY4S)z3s&E^CYh_eJ)&duVHmNrGC5Zq6oy2aeBEn)v)zUtAZ~#4@$v7K24HUkq>mad;C*CoZ~(6TMEZaMPDTKr`^h{S2?mfp zZ2yVrR-%5wbU)omJ86TT61(75AE#8jk0{0r{N1@jB;>MIKRl;!Ej z8Tr6f9VJRoZb>93#IRVFP=tzX+-#yKwwo0qN5H*Um6PJ8D{j|V?uwygCm7eOVDUi< z33wP)Xu+}bu}FS>15s`sf7MK>0^4G3R+mrpZkjOST@9h-bo`NsY8x-ZUQ_GM6&L+S zm`Qs0%|rh3TrZYa*kw(+9qr>DB#(HnztLV!t%)CJpv|l9qs5NGgbvp0)U~5VuPOTX z-4`w(Z0Gi}>$k^WL~6Z<pxlm=4}>+{x{in3Sd?!9MDAhIA=C2LSrLQWH6 zEIKtpj^WmRiGLIGMkUEnA<07Ip&0*~YBf%7d}C;Eb77)~3^7zB`&BR_abFZUcB8|b zUn?cDG57LAkysqXfLX1~0}UR%Ik8|NG0NM33Aic;G4R?9s2D{Vrxe3pm15@1JVu}C z@r5K?TB>xn6Z8OnkH_gM^fkL`t-yF^jTuK1vKoTnEV%3Wvt>t5?cqcoOCcn>sNl%` z<`ExHdniUbwY|bpG^4fWr+IG|Kp!s5s)`F-^8>ui0B;+ZxgaiMX5o+m2Bgx10uWe$ z6ED{PBAE>UeB^Ed9B$wd4uHIYvpGQR28i(hvm5=3|Jf2W0Nn1$2M**RbDAeMlP7$n zOQNb{vmhOT;a>uQU$fw}m2Y@|kviXe{t`fX{LaNN+JU@i3HGYo^XEVU$ zwLp&pBt}bEZf{~5dJNcUSQ7{NGIVeg2!VevSCp)HOEhy2s)dl-%p@!?g&F5x;f~@Vb z1Shu`6&9U-@2IGuHcC?SxRtItU&7d-+n8c@x_5^-O&_(kHHJ$mv`)2L7^U$9-dnjW zdJI*AYCa?cRU>mKi+esQ9@2;nvYlFFh~y)ke-HbcNs1c&0V!jcvLO-G)!j_0@mg9f zc^GLCTV)t&6_eS)z$Q19MGtbdhVGoRk00X>z`EAW!pq6lXr&iZzGPll5b4bF%^ekb znep<(&xzgI!OM)B@v_BfJpD%$irvB)(B<7^;+xwtp?GsGu!lI`tIu$@qdK#SQDdB&fwH6}4fb0!4nF1JZ;0cZ~2}oH2Zb#rEP9TQk z-bl+t%AIOFAg+2!M>#<8ulIBD@0Y@eTHFS5Elv{G!Ayvs3?H_c$;DU8KW)a8+b632 z)}p|3H~w9+#=sLvCr^+yEMipBR1a*9Ir=aPXu^Z5*{TTV4r| z&axK8Wvdc4MzcuOzuUVEEqdI*X!fE3JGtsK9hM@SPmw)5-`%h~IW0IVu4J{>>KB0G z&~#plgR6!vzbPQ&|Ag&4Y^;|oZ=8P+x1bRr7+4qO>#M48_QfG=9tuB;_p$nuL_cvJ zQE>7XSK_!ig|o;!g8NT(*h3>E{u7Q=%CH>M4dv%Y`r=yVLcwn?urDgQ+;nhb>a)(N z9}DhQpVTW3f2I;?9kKfxf5TZi)l!-&r^h~ZEVsLf%&9O8*_RNT5?+aq$Lr~$h5YHr z9B|?SHXP$tnU$=)FYG?XFY7FDoQ*m=H#wvVsLbfWRhKhHij;9VVG}Ml)Ur90Bi#hO zCLZ3|_f|H(68?I6i}JQ>E{=i`x&^G_{1|I{*nW#h>pRA|!YbupkM1ou(`{b3)|0Ab z)gNYu7E}<@C41|bDqq=6j-FOVo`jY(|87v_Yl`p5pb}ej-a_Gp9@390WkOiiYbw#y zuiuQ$n3EG&cQ(lALBTXDGXK>;Krk$8yj zNJYxBk;K#Lv9GMSH*Sj@S=JI2_z6j2K#v(-Mq>nZt*Du^y>`;!50NTO-E*N#@kxwcl+}v4CP}1Q7vxUIm^5@_k)8j5c2$=SAt4y1=%-Q%tS&;@ zQx>_N!0FILTbSE$DG{~IOCguJ()uPFMdA&Dgx5s6PzKgU3(Zq%~k$pcPsLq6{WXNpP{GtwQI+WYT;b^A-Y+ucD++!RF6vMrGOfF8Ij6rL)@{0O9Gqsz_GyUB?d)eD^f`4MUSvxW* z{)y#g`6rf};y>q_wKLIIPQk9|(L(=% zma~Rb03Y%ElE6d#0lV((7nD0OqQ?Y)azpTQ+%vUV*8 z{n(<5hyC?}y^=>F2OAS<69fG@mGa6mv!)~TW1HkS{6C*pz7L{&`LR}mWS2LNCv4Y^ zW$EBPri&ww7X_C$cT_C;bDK81{^Zta+`UF^w$!(q?_+qAUoL&5_PWWfo@Fl3C)_s; z?|V55O0IDW6ZM^xN97&#VIA!Wr9v=#o%I4qq=-ryL$IQ~1v`9t@#5m5TTC`&hN6YY z71aH8CWNwDk05P}9$3^0Zh1?&c*E#nFp$l5Y)7@@wM08o+qJ_k> zt|RkAu7oi%f1hjU>sFG)POjP-+wv@Tz0@Ke6Y{}jSnw21aAfH@faGp+NRG6ju_hZT zARFGH9+3CHCtOwL4tea*pi_HBa$~*M8N(CkqU(Bo5JG);YpA7}tbO@f`Hg2^rn#;| z>VrxJT3xhNY4Y9YzB-jR4PVo#h2O1qe1dXH9;N26D#MY3#g82w*He)^MqIjH#fU0C zCSUxzwKuXVqmr$Z>#c$&s&moz-NTwUk1C;70Z#4{LOkT=dKjyJLr442we-ln+?5x0l9TC4>j!e(%04(nYk9*a>x1Y93l#AJ^52 z=5^9YgKH3*W)W|txDrHUBy?+@W-Kp=M+oHJf5aixG^y&B#WCCUfDAWxbxl{I(e&q9 z%>hoJ^^QTMvwF(5G94J@J|a6c8ciG9ENmFtL`j7Mib@k+6z}YFX`@1$%vQk*_M6EU z*Kg?STtl15eA+DNTi;t7bl0G-4$qxzC!aSYZ5KxEM!@uwOWSN~<}}<(z$wmd#8aPz`XA^R)!0ys@oJ<8-?+M6nH0v0ymEx%e69DTZ5)f`7Oo5n=z!!l`eT{6WR=1 zY9ZbzH;k{RxP)cxaA%IZ@8Ty>f0@c80;TKY*Ut&N@2oLDiYcD`2-#9Sb?Ao&uTkN$ z_d-1O(Zu$+IbEnd=hFxs_cu$i&1`8ZP_xh&bG8-+R)l0ZZs4 zqA|gNx7}noc5!JiK0ZL!1X=YgUx2~u;LQRaEuj4?hOez1vlPAYquV`ns-TJ zjqh_OD=r&e$g_ZcjI2{G!K6zZhb{MfGoNIUow{!*FndXKuqW2A;-$EV74HG&wmPr? zf4OCO)_GL40dZGd$)z9}@CCfI1v7v^Qg><=1!6^?iiY9!s)R00kx)2Y2HFJn$I5Gj zaynE+bEDX=jhp3jxBd8Un%{>L zEtA%U!+q#X5sPO>eu8l?WjVG`9FR)!WI(wuzKFUzyf&yS8`T|ooaDX@zELgVs_$oj z>H{aO9*YkMuW+Ku#8wkt+hAX=o@7DXUAvR>)Cb3015XVo+K@XCwP9sore8y-cy2#Daqc z*Tin>P`RYE!MFZGkrs4yJGyehGqg=Yhy4;69)p}d`Nw#Jvo0cq~3 zRj=m5<@Dv6oermw1=w&2ri33c5tcO)P#AFm@IU;2KpYi_`IRaAB_d&}V16xQ--g

*4pdjbB*5bCd~f5}oH00|%mcrl|Kjt* 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%0AC-AUC7%IxRTLupd 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 literal 0 HcmV?d00001 diff --git a/examples/alicloud-slb/main.tf b/examples/alicloud-slb/main.tf new file mode 100644 index 0000000000..67f5e0cf60 --- /dev/null +++ b/examples/alicloud-slb/main.tf @@ -0,0 +1,25 @@ +resource "alicloud_slb" "instance" { + name = "${var.slb_name}" + internet_charge_type = "${var.internet_charge_type}" + internet = "${var.internet}" + + listener = [ + { + "instance_port" = "2111" + "lb_port" = "21" + "lb_protocol" = "tcp" + "bandwidth" = "5" + }, + { + "instance_port" = "8000" + "lb_port" = "80" + "lb_protocol" = "http" + "bandwidth" = "5" + }, + { + "instance_port" = "1611" + "lb_port" = "161" + "lb_protocol" = "udp" + "bandwidth" = "5" + }] +} diff --git a/examples/alicloud-slb/outputs.tf b/examples/alicloud-slb/outputs.tf new file mode 100644 index 0000000000..d8e1cc93e8 --- /dev/null +++ b/examples/alicloud-slb/outputs.tf @@ -0,0 +1,7 @@ +output "slb_id" { + value = "${alicloud_slb.instance.id}" +} + +output "slbname" { + value = "${alicloud_slb.instance.name}" +} \ No newline at end of file diff --git a/examples/alicloud-slb/variables.tf b/examples/alicloud-slb/variables.tf new file mode 100644 index 0000000000..7a741b295c --- /dev/null +++ b/examples/alicloud-slb/variables.tf @@ -0,0 +1,11 @@ +variable "slb_name" { + default = "slb_worder" +} + +variable "internet_charge_type" { + default = "paybytraffic" +} + +variable "internet" { + default = true +} diff --git a/examples/alicloud-vpc-cluster-sg/README.md b/examples/alicloud-vpc-cluster-sg/README.md new file mode 100644 index 0000000000..5ef00e5f63 --- /dev/null +++ b/examples/alicloud-vpc-cluster-sg/README.md @@ -0,0 +1,19 @@ +### SecurityGroups With Vpc Example + +The example create SecurityGroups for specify VPC Clusters. + +### Get up and running + +* Planning phase + + terraform plan + + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-vpc-cluster-sg/main.tf b/examples/alicloud-vpc-cluster-sg/main.tf new file mode 100644 index 0000000000..1e6575aeb9 --- /dev/null +++ b/examples/alicloud-vpc-cluster-sg/main.tf @@ -0,0 +1,23 @@ +resource "alicloud_security_group" "default" { + name = "${var.short_name}-default" + description = "Default security group for VPC" + vpc_id = "${var.vpc_id}" +} + +resource "alicloud_security_group" "control" { + name = "${var.short_name}-control" + description = "Allow inboud traffic for control nodes" + vpc_id = "${var.vpc_id}" +} + +resource "alicloud_security_group" "edge" { + name = "${var.short_name}-edge" + description = "Allow inboud traffic for edge routing" + vpc_id = "${var.vpc_id}" +} + +resource "alicloud_security_group" "worker" { + name = "${var.short_name}-worker" + description = "Allow inboud traffic for worker nodes" + vpc_id = "${var.vpc_id}" +} diff --git a/examples/alicloud-vpc-cluster-sg/outputs.tf b/examples/alicloud-vpc-cluster-sg/outputs.tf new file mode 100644 index 0000000000..f2e6408069 --- /dev/null +++ b/examples/alicloud-vpc-cluster-sg/outputs.tf @@ -0,0 +1,15 @@ +output "default_security_group" { + value = "${alicloud_security_group.default.id}" +} + +output "edge_security_group" { + value = "${alicloud_security_group.edge.id}" +} + +output "control_security_group" { + value = "${alicloud_security_group.control.id}" +} + +output "worker_security_group" { + value = "${alicloud_security_group.worker.id}" +} diff --git a/examples/alicloud-vpc-cluster-sg/variables.tf b/examples/alicloud-vpc-cluster-sg/variables.tf new file mode 100644 index 0000000000..e37d4e0d23 --- /dev/null +++ b/examples/alicloud-vpc-cluster-sg/variables.tf @@ -0,0 +1,4 @@ +variable "short_name" { +} +variable "vpc_id" { +} diff --git a/examples/alicloud-vpc-multi-region/README.md b/examples/alicloud-vpc-multi-region/README.md new file mode 100644 index 0000000000..e45fe46661 --- /dev/null +++ b/examples/alicloud-vpc-multi-region/README.md @@ -0,0 +1,18 @@ +### VPC Example + +The example will create VPC in multi region use "alias" characters. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-vpc-multi-region/main.tf b/examples/alicloud-vpc-multi-region/main.tf new file mode 100644 index 0000000000..a3f946cc45 --- /dev/null +++ b/examples/alicloud-vpc-multi-region/main.tf @@ -0,0 +1,21 @@ +provider "alicloud" { + alias = "bj" + region = "${var.region1}" +} + +provider "alicloud" { + alias = "hz" + region = "${var.region2}" +} + +resource "alicloud_vpc" "work" { + provider = "alicloud.hz" + name = "${var.long_name}" + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vpc" "control" { + provider = "alicloud.bj" + name = "${var.long_name}" + cidr_block = "${var.vpc_cidr}" +} diff --git a/examples/alicloud-vpc-multi-region/outputs.tf b/examples/alicloud-vpc-multi-region/outputs.tf new file mode 100644 index 0000000000..d46e24927a --- /dev/null +++ b/examples/alicloud-vpc-multi-region/outputs.tf @@ -0,0 +1,7 @@ +output "vpc_work_id" { + value = "${alicloud_vpc.work.id}" +} + +output "vpc_control_id" { + value = "${alicloud_vpc.control.id}" +} \ No newline at end of file diff --git a/examples/alicloud-vpc-multi-region/variables.tf b/examples/alicloud-vpc-multi-region/variables.tf new file mode 100644 index 0000000000..c896ae47a0 --- /dev/null +++ b/examples/alicloud-vpc-multi-region/variables.tf @@ -0,0 +1,12 @@ +variable "long_name" { + default = "alicloud" +} +variable "vpc_cidr" { + default = "10.1.0.0/21" +} +variable "region1" { + default = "cn-beijing" +} +variable "region2" { + default = "cn-hangzhou" +} \ No newline at end of file diff --git a/examples/alicloud-vpc-route-entry/main.tf b/examples/alicloud-vpc-route-entry/main.tf new file mode 100644 index 0000000000..9f6876b29b --- /dev/null +++ b/examples/alicloud-vpc-route-entry/main.tf @@ -0,0 +1,55 @@ +resource "alicloud_vpc" "default" { + name = "tf_vpc" + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vswitch" "default" { + vpc_id = "${alicloud_vpc.default.id}" + cidr_block = "${var.vswitch_cidr}" + availability_zone = "${var.zone_id}" +} + +resource "alicloud_route_entry" "default" { + router_id = "${alicloud_vpc.default.router_id}" + route_table_id = "${alicloud_vpc.default.router_table_id}" + destination_cidrblock = "${var.entry_cidr}" + nexthop_type = "Instance" + nexthop_id = "${alicloud_instance.snat.id}" +} + +resource "alicloud_security_group" "sg" { + name = "tf_sg" + description = "tf_sg" + vpc_id = "${alicloud_vpc.default.id}" +} + +resource "alicloud_security_group_rule" "ssh" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "${var.rule_policy}" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.sg.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_instance" "snat" { + # cn-beijing + availability_zone = "${var.zone_id}" + security_groups = ["${alicloud_security_group.sg.id}"] + + vswitch_id = "${alicloud_vswitch.default.id}" + allocate_public_ip = true + + # series II + instance_charge_type = "PostPaid" + instance_type = "${var.instance_type}" + internet_charge_type = "${var.internet_charge_type}" + internet_max_bandwidth_out = 5 + io_optimized = "${var.io_optimized}" + + system_disk_category = "cloud_efficiency" + image_id = "${var.image_id}" + instance_name = "tf_snat" +} \ No newline at end of file diff --git a/examples/alicloud-vpc-route-entry/ouputs.tf b/examples/alicloud-vpc-route-entry/ouputs.tf new file mode 100644 index 0000000000..37e60e250c --- /dev/null +++ b/examples/alicloud-vpc-route-entry/ouputs.tf @@ -0,0 +1,15 @@ +output "route_table_id" { + value = "${alicloud_route_entry.default.route_table_id}" +} + +output "router_id" { + value = "${alicloud_route_entry.default.router_id}" +} + +output "nexthop_type" { + value = "${alicloud_route_entry.default.nexthop_type}" +} + +output "nexthop_id" { + value = "${alicloud_route_entry.default.nexthop_id}" +} \ No newline at end of file diff --git a/examples/alicloud-vpc-route-entry/variables.tf b/examples/alicloud-vpc-route-entry/variables.tf new file mode 100644 index 0000000000..4708e266f5 --- /dev/null +++ b/examples/alicloud-vpc-route-entry/variables.tf @@ -0,0 +1,28 @@ + +variable "vpc_cidr" { + default = "10.1.0.0/21" +} +variable "vswitch_cidr" { + default = "10.1.1.0/24" +} +variable "zone_id" { + default = "cn-beijing-c" +} +variable "entry_cidr" { + default = "172.11.1.1/32" +} +variable "rule_policy" { + default = "accept" +} +variable "instance_type" { + default = "ecs.n1.small" +} +variable "image_id" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} +variable "internet_charge_type" { + default = "PayByTraffic" +} +variable "io_optimized" { + default = "optimized" +} \ No newline at end of file diff --git a/examples/alicloud-vpc/README.md b/examples/alicloud-vpc/README.md new file mode 100644 index 0000000000..392e0abc8d --- /dev/null +++ b/examples/alicloud-vpc/README.md @@ -0,0 +1,18 @@ +### VPC Example + +The example create VPC, VSwitch, Natgateway. The variables.tf can let you create specify parameter instances, such as availability_zone, cidr_block etc. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-vpc/main.tf b/examples/alicloud-vpc/main.tf new file mode 100644 index 0000000000..78d58b17cc --- /dev/null +++ b/examples/alicloud-vpc/main.tf @@ -0,0 +1,28 @@ +resource "alicloud_vpc" "main" { + name = "${var.long_name}" + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vswitch" "main" { + vpc_id = "${alicloud_vpc.main.id}" + count = "${length(split(",", var.availability_zones))}" + cidr_block = "${lookup(var.cidr_blocks, "az${count.index}")}" + availability_zone = "${var.availability_zones}" + depends_on = [ + "alicloud_vpc.main"] +} + +resource "alicloud_nat_gateway" "main" { + vpc_id = "${alicloud_vpc.main.id}" + spec = "Small" + bandwidth_packages = [ + { + ip_count = 1 + bandwidth = 5 + zone = "${var.availability_zones}" + } + ] + depends_on = [ + "alicloud_vswitch.main"] +} + diff --git a/examples/alicloud-vpc/outputs.tf b/examples/alicloud-vpc/outputs.tf new file mode 100644 index 0000000000..06809a16a8 --- /dev/null +++ b/examples/alicloud-vpc/outputs.tf @@ -0,0 +1,11 @@ +output "vpc_id" { + value = "${alicloud_vpc.main.id}" +} + +output "vswitch_ids" { + value = "${join(",", alicloud_vswitch.main.*.id)}" +} + +output "availability_zones" { + value = "${join(",",alicloud_vswitch.main.*.availability_zone)}" +} diff --git a/examples/alicloud-vpc/variables.tf b/examples/alicloud-vpc/variables.tf new file mode 100644 index 0000000000..2b30cde057 --- /dev/null +++ b/examples/alicloud-vpc/variables.tf @@ -0,0 +1,25 @@ +variable "availability_zones" { + default = "cn-beijing-c" +} + +variable "cidr_blocks" { + type = "map" + default = { + az0 = "10.1.1.0/24" + az1 = "10.1.2.0/24" + az2 = "10.1.3.0/24" + } +} + +variable "long_name" { + default = "alicloud" +} +variable "short_name" { + default = "ali" +} +variable "vpc_cidr" { + default = "10.1.0.0/21" +} +variable "region" { + default = "cn-beijing" +} \ No newline at end of file diff --git a/vendor/github.com/denverdino/aliyungo/LICENSE.txt b/vendor/github.com/denverdino/aliyungo/LICENSE.txt new file mode 100644 index 0000000000..9182971332 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/LICENSE.txt @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015-2015 Li Yi (denverdino@gmail.com). + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/denverdino/aliyungo/common/client.go b/vendor/github.com/denverdino/aliyungo/common/client.go new file mode 100755 index 0000000000..1fa43afaeb --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/client.go @@ -0,0 +1,225 @@ +package common + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "log" + "net/http" + "net/url" + "time" + "strings" + + "github.com/denverdino/aliyungo/util" +) + +// A Client represents a client of ECS services +type Client struct { + AccessKeyId string //Access Key Id + AccessKeySecret string //Access Key Secret + debug bool + httpClient *http.Client + endpoint string + version string +} + +// NewClient creates a new instance of ECS client +func (client *Client) Init(endpoint, version, accessKeyId, accessKeySecret string) { + client.AccessKeyId = accessKeyId + client.AccessKeySecret = accessKeySecret + "&" + client.debug = false + client.httpClient = &http.Client{} + client.endpoint = endpoint + client.version = version +} + +// SetEndpoint sets custom endpoint +func (client *Client) SetEndpoint(endpoint string) { + client.endpoint = endpoint +} + +// SetEndpoint sets custom version +func (client *Client) SetVersion(version string) { + client.version = version +} + +// SetAccessKeyId sets new AccessKeyId +func (client *Client) SetAccessKeyId(id string) { + client.AccessKeyId = id +} + +// SetAccessKeySecret sets new AccessKeySecret +func (client *Client) SetAccessKeySecret(secret string) { + client.AccessKeySecret = secret + "&" +} + +// SetDebug sets debug mode to log the request/response message +func (client *Client) SetDebug(debug bool) { + client.debug = debug +} + +// Invoke sends the raw HTTP request for ECS services +func (client *Client) Invoke(action string, args interface{}, response interface{}) error { + + request := Request{} + request.init(client.version, action, client.AccessKeyId) + + query := util.ConvertToQueryValues(request) + util.SetQueryValues(args, &query) + + // Sign request + signature := util.CreateSignatureForRequest(ECSRequestMethod, &query, client.AccessKeySecret) + + // Generate the request URL + requestURL := client.endpoint + "?" + query.Encode() + "&Signature=" + url.QueryEscape(signature) + + httpReq, err := http.NewRequest(ECSRequestMethod, requestURL, nil) + + if err != nil { + return GetClientError(err) + } + + // TODO move to util and add build val flag + httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version) + + t0 := time.Now() + httpResp, err := client.httpClient.Do(httpReq) + t1 := time.Now() + if err != nil { + return GetClientError(err) + } + statusCode := httpResp.StatusCode + + if client.debug { + log.Printf("Invoke %s %s %d (%v)", ECSRequestMethod, requestURL, statusCode, t1.Sub(t0)) + } + + defer httpResp.Body.Close() + body, err := ioutil.ReadAll(httpResp.Body) + + if err != nil { + return GetClientError(err) + } + + if client.debug { + var prettyJSON bytes.Buffer + err = json.Indent(&prettyJSON, body, "", " ") + log.Println(string(prettyJSON.Bytes())) + } + + if statusCode >= 400 && statusCode <= 599 { + errorResponse := ErrorResponse{} + err = json.Unmarshal(body, &errorResponse) + ecsError := &Error{ + ErrorResponse: errorResponse, + StatusCode: statusCode, + } + return ecsError + } + + err = json.Unmarshal(body, response) + //log.Printf("%++v", response) + if err != nil { + return GetClientError(err) + } + + return nil +} + +// Invoke sends the raw HTTP request for ECS services +//改进了一下上面那个方法,可以使用各种Http方法 +func (client *Client) InvokeByAnyMethod(method, action string, args interface{}, response interface{}) error { + + request := Request{} + request.init(client.version, action, client.AccessKeyId) + + data := util.ConvertToQueryValues(request) + util.SetQueryValues(args, &data) + + // Sign request + signature := util.CreateSignatureForRequest(method, &data, client.AccessKeySecret) + + data.Add("Signature", signature) + + // Generate the request URL + var ( + httpReq *http.Request + err error + ) + if method == http.MethodGet { + requestURL := client.endpoint + "?" + data.Encode() + httpReq, err = http.NewRequest(method, requestURL, nil) + } else { + httpReq, err = http.NewRequest(method, client.endpoint, strings.NewReader(data.Encode())) + httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") + } + + if err != nil { + return GetClientError(err) + } + + // TODO move to util and add build val flag + httpReq.Header.Set("X-SDK-Client", `AliyunGO/` + Version) + + t0 := time.Now() + httpResp, err := client.httpClient.Do(httpReq) + t1 := time.Now() + if err != nil { + return GetClientError(err) + } + statusCode := httpResp.StatusCode + + if client.debug { + log.Printf("Invoke %s %s %d (%v) %v", ECSRequestMethod, client.endpoint, statusCode, t1.Sub(t0), data.Encode()) + } + + defer httpResp.Body.Close() + body, err := ioutil.ReadAll(httpResp.Body) + + if err != nil { + return GetClientError(err) + } + + if client.debug { + var prettyJSON bytes.Buffer + err = json.Indent(&prettyJSON, body, "", " ") + log.Println(string(prettyJSON.Bytes())) + } + + if statusCode >= 400 && statusCode <= 599 { + errorResponse := ErrorResponse{} + err = json.Unmarshal(body, &errorResponse) + ecsError := &Error{ + ErrorResponse: errorResponse, + StatusCode: statusCode, + } + return ecsError + } + + err = json.Unmarshal(body, response) + //log.Printf("%++v", response) + if err != nil { + return GetClientError(err) + } + + return nil +} + +// GenerateClientToken generates the Client Token with random string +func (client *Client) GenerateClientToken() string { + return util.CreateRandomString() +} + +func GetClientErrorFromString(str string) error { + return &Error{ + ErrorResponse: ErrorResponse{ + Code: "AliyunGoClientFailure", + Message: str, + }, + StatusCode: -1, + } +} + +func GetClientError(err error) error { + return GetClientErrorFromString(err.Error()) +} diff --git a/vendor/github.com/denverdino/aliyungo/common/regions.go b/vendor/github.com/denverdino/aliyungo/common/regions.go new file mode 100644 index 0000000000..781a727bc1 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/regions.go @@ -0,0 +1,29 @@ +package common + +// Region represents ECS region +type Region string + +// Constants of region definition +const ( + Hangzhou = Region("cn-hangzhou") + Qingdao = Region("cn-qingdao") + Beijing = Region("cn-beijing") + Hongkong = Region("cn-hongkong") + Shenzhen = Region("cn-shenzhen") + USWest1 = Region("us-west-1") + USEast1 = Region("us-east-1") + APSouthEast1 = Region("ap-southeast-1") + Shanghai = Region("cn-shanghai") + MEEast1 = Region("me-east-1") + APNorthEast1 = Region("ap-northeast-1") + APSouthEast2 = Region("ap-southeast-2") + EUCentral1 = Region("eu-central-1") +) + +var ValidRegions = []Region{ + Hangzhou, Qingdao, Beijing, Shenzhen, Hongkong, Shanghai, + USWest1, USEast1, + APNorthEast1, APSouthEast1, APSouthEast2, + MEEast1, + EUCentral1, +} diff --git a/vendor/github.com/denverdino/aliyungo/common/request.go b/vendor/github.com/denverdino/aliyungo/common/request.go new file mode 100644 index 0000000000..2a883f19b2 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/request.go @@ -0,0 +1,101 @@ +package common + +import ( + "fmt" + "log" + "time" + + "github.com/denverdino/aliyungo/util" +) + +// Constants for Aliyun API requests +const ( + SignatureVersion = "1.0" + SignatureMethod = "HMAC-SHA1" + JSONResponseFormat = "JSON" + XMLResponseFormat = "XML" + ECSRequestMethod = "GET" +) + +type Request struct { + Format string + Version string + AccessKeyId string + Signature string + SignatureMethod string + Timestamp util.ISO6801Time + SignatureVersion string + SignatureNonce string + ResourceOwnerAccount string + Action string +} + +func (request *Request) init(version string, action string, AccessKeyId string) { + request.Format = JSONResponseFormat + request.Timestamp = util.NewISO6801Time(time.Now().UTC()) + request.Version = version + request.SignatureVersion = SignatureVersion + request.SignatureMethod = SignatureMethod + request.SignatureNonce = util.CreateRandomString() + request.Action = action + request.AccessKeyId = AccessKeyId +} + +type Response struct { + RequestId string +} + +type ErrorResponse struct { + Response + HostId string + Code string + Message string +} + +// An Error represents a custom error for Aliyun API failure response +type Error struct { + ErrorResponse + StatusCode int //Status Code of HTTP Response +} + +func (e *Error) Error() string { + return fmt.Sprintf("Aliyun API Error: RequestId: %s Status Code: %d Code: %s Message: %s", e.RequestId, e.StatusCode, e.Code, e.Message) +} + +type Pagination struct { + PageNumber int + PageSize int +} + +func (p *Pagination) SetPageSize(size int) { + p.PageSize = size +} + +func (p *Pagination) Validate() { + if p.PageNumber < 0 { + log.Printf("Invalid PageNumber: %d", p.PageNumber) + p.PageNumber = 1 + } + if p.PageSize < 0 { + log.Printf("Invalid PageSize: %d", p.PageSize) + p.PageSize = 10 + } else if p.PageSize > 50 { + log.Printf("Invalid PageSize: %d", p.PageSize) + p.PageSize = 50 + } +} + +// A PaginationResponse represents a response with pagination information +type PaginationResult struct { + TotalCount int + PageNumber int + PageSize int +} + +// NextPage gets the next page of the result set +func (r *PaginationResult) NextPage() *Pagination { + if r.PageNumber*r.PageSize >= r.TotalCount { + return nil + } + return &Pagination{PageNumber: r.PageNumber + 1, PageSize: r.PageSize} +} diff --git a/vendor/github.com/denverdino/aliyungo/common/types.go b/vendor/github.com/denverdino/aliyungo/common/types.go new file mode 100644 index 0000000000..c562aedfc2 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/types.go @@ -0,0 +1,15 @@ +package common + +type InternetChargeType string + +const ( + PayByBandwidth = InternetChargeType("PayByBandwidth") + PayByTraffic = InternetChargeType("PayByTraffic") +) + +type InstanceChargeType string + +const ( + PrePaid = InstanceChargeType("PrePaid") + PostPaid = InstanceChargeType("PostPaid") +) diff --git a/vendor/github.com/denverdino/aliyungo/common/version.go b/vendor/github.com/denverdino/aliyungo/common/version.go new file mode 100644 index 0000000000..7cb3d3aff0 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/version.go @@ -0,0 +1,3 @@ +package common + +const Version = "0.1" diff --git a/vendor/github.com/denverdino/aliyungo/ecs/client.go b/vendor/github.com/denverdino/aliyungo/ecs/client.go new file mode 100644 index 0000000000..063c0738c0 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/client.go @@ -0,0 +1,37 @@ +package ecs + +import ( + "github.com/denverdino/aliyungo/common" + "os" +) + +// Interval for checking status in WaitForXXX method +const DefaultWaitForInterval = 5 + +// Default timeout value for WaitForXXX method +const DefaultTimeout = 60 + +type Client struct { + common.Client +} + +const ( + // ECSDefaultEndpoint is the default API endpoint of ECS services + ECSDefaultEndpoint = "https://ecs-cn-hangzhou.aliyuncs.com" + ECSAPIVersion = "2014-05-26" +) + +// NewClient creates a new instance of ECS client +func NewClient(accessKeyId, accessKeySecret string) *Client { + endpoint := os.Getenv("ECS_ENDPOINT") + if endpoint == "" { + endpoint = ECSDefaultEndpoint + } + return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret) +} + +func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client { + client := &Client{} + client.Init(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret) + return client +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/disks.go b/vendor/github.com/denverdino/aliyungo/ecs/disks.go new file mode 100644 index 0000000000..f1d1e93412 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/disks.go @@ -0,0 +1,330 @@ +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +// Types of disks +type DiskType string + +const ( + DiskTypeAll = DiskType("all") //Default + DiskTypeAllSystem = DiskType("system") + DiskTypeAllData = DiskType("data") +) + +// Categories of disks +type DiskCategory string + +const ( + DiskCategoryAll = DiskCategory("all") //Default + DiskCategoryCloud = DiskCategory("cloud") + DiskCategoryEphemeral = DiskCategory("ephemeral") + DiskCategoryEphemeralSSD = DiskCategory("ephemeral_ssd") + DiskCategoryCloudEfficiency = DiskCategory("cloud_efficiency") + DiskCategoryCloudSSD = DiskCategory("cloud_ssd") +) + +// Status of disks +type DiskStatus string + +const ( + DiskStatusInUse = DiskStatus("In_use") + DiskStatusAvailable = DiskStatus("Available") + DiskStatusAttaching = DiskStatus("Attaching") + DiskStatusDetaching = DiskStatus("Detaching") + DiskStatusCreating = DiskStatus("Creating") + DiskStatusReIniting = DiskStatus("ReIniting") + DiskStatusAll = DiskStatus("All") //Default +) + +// Charge type of disks +type DiskChargeType string + +const ( + PrePaid = DiskChargeType("PrePaid") + PostPaid = DiskChargeType("PostPaid") +) + +// A DescribeDisksArgs defines the arguments to describe disks +type DescribeDisksArgs struct { + RegionId common.Region + ZoneId string + DiskIds []string + InstanceId string + DiskType DiskType //enum for all(default) | system | data + Category DiskCategory //enum for all(default) | cloud | ephemeral + Status DiskStatus //enum for In_use | Available | Attaching | Detaching | Creating | ReIniting | All(default) + SnapshotId string + Name string + Portable *bool //optional + DeleteWithInstance *bool //optional + DeleteAutoSnapshot *bool //optional + EnableAutoSnapshot *bool //optional + DiskChargeType DiskChargeType + Tag map[string]string + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&diskitemtype +type DiskItemType struct { + DiskId string + RegionId common.Region + ZoneId string + DiskName string + Description string + Type DiskType + Category DiskCategory + Size int + ImageId string + SourceSnapshotId string + ProductCode string + Portable bool + Status DiskStatus + OperationLocks OperationLocksType + InstanceId string + Device string + DeleteWithInstance bool + DeleteAutoSnapshot bool + EnableAutoSnapshot bool + CreationTime util.ISO6801Time + AttachedTime util.ISO6801Time + DetachedTime util.ISO6801Time + DiskChargeType DiskChargeType +} + +type DescribeDisksResponse struct { + common.Response + common.PaginationResult + RegionId common.Region + Disks struct { + Disk []DiskItemType + } +} + +// DescribeDisks describes Disks +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&describedisks +func (client *Client) DescribeDisks(args *DescribeDisksArgs) (disks []DiskItemType, pagination *common.PaginationResult, err error) { + response := DescribeDisksResponse{} + + err = client.Invoke("DescribeDisks", args, &response) + + if err != nil { + return nil, nil, err + } + + return response.Disks.Disk, &response.PaginationResult, err +} + +type CreateDiskArgs struct { + RegionId common.Region + ZoneId string + DiskName string + Description string + DiskCategory DiskCategory + Size int + SnapshotId string + ClientToken string +} + +type CreateDisksResponse struct { + common.Response + DiskId string +} + +// CreateDisk creates a new disk +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&createdisk +func (client *Client) CreateDisk(args *CreateDiskArgs) (diskId string, err error) { + response := CreateDisksResponse{} + err = client.Invoke("CreateDisk", args, &response) + if err != nil { + return "", err + } + return response.DiskId, err +} + +type DeleteDiskArgs struct { + DiskId string +} + +type DeleteDiskResponse struct { + common.Response +} + +// DeleteDisk deletes disk +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&deletedisk +func (client *Client) DeleteDisk(diskId string) error { + args := DeleteDiskArgs{ + DiskId: diskId, + } + response := DeleteDiskResponse{} + err := client.Invoke("DeleteDisk", &args, &response) + return err +} + +type ReInitDiskArgs struct { + DiskId string +} + +type ReInitDiskResponse struct { + common.Response +} + +// ReInitDisk reinitizes disk +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&reinitdisk +func (client *Client) ReInitDisk(diskId string) error { + args := ReInitDiskArgs{ + DiskId: diskId, + } + response := ReInitDiskResponse{} + err := client.Invoke("ReInitDisk", &args, &response) + return err +} + +type AttachDiskArgs struct { + InstanceId string + DiskId string + Device string + DeleteWithInstance bool +} + +type AttachDiskResponse struct { + common.Response +} + +// AttachDisk attaches disk to instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&attachdisk +func (client *Client) AttachDisk(args *AttachDiskArgs) error { + response := AttachDiskResponse{} + err := client.Invoke("AttachDisk", args, &response) + return err +} + +type DetachDiskArgs struct { + InstanceId string + DiskId string +} + +type DetachDiskResponse struct { + common.Response +} + +// DetachDisk detaches disk from instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&detachdisk +func (client *Client) DetachDisk(instanceId string, diskId string) error { + args := DetachDiskArgs{ + InstanceId: instanceId, + DiskId: diskId, + } + response := DetachDiskResponse{} + err := client.Invoke("DetachDisk", &args, &response) + return err +} + +type ResetDiskArgs struct { + DiskId string + SnapshotId string +} + +type ResetDiskResponse struct { + common.Response +} + +// ResetDisk resets disk to original status +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&resetdisk +func (client *Client) ResetDisk(diskId string, snapshotId string) error { + args := ResetDiskArgs{ + SnapshotId: snapshotId, + DiskId: diskId, + } + response := ResetDiskResponse{} + err := client.Invoke("ResetDisk", &args, &response) + return err +} + +type ModifyDiskAttributeArgs struct { + DiskId string + DiskName string + Description string + DeleteWithInstance *bool + DeleteAutoSnapshot *bool + EnableAutoSnapshot *bool +} + +type ModifyDiskAttributeResponse struct { + common.Response +} + +// ModifyDiskAttribute modifies disk attribute +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&modifydiskattribute +func (client *Client) ModifyDiskAttribute(args *ModifyDiskAttributeArgs) error { + response := ModifyDiskAttributeResponse{} + err := client.Invoke("ModifyDiskAttribute", args, &response) + return err +} + +type ReplaceSystemDiskArgs struct { + InstanceId string + ImageId string + SystemDisk SystemDiskType + ClientToken string +} + +type ReplaceSystemDiskResponse struct { + common.Response + DiskId string +} + +// ReplaceSystemDisk replace system disk +// +// You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/disk/replacesystemdisk.html +func (client *Client) ReplaceSystemDisk(args *ReplaceSystemDiskArgs) (diskId string, err error) { + response := ReplaceSystemDiskResponse{} + err = client.Invoke("ReplaceSystemDisk", args, &response) + if err != nil { + return "", err + } + return response.DiskId, nil +} + +// WaitForDisk waits for disk to given status +func (client *Client) WaitForDisk(regionId common.Region, diskId string, status DiskStatus, timeout int) error { + if timeout <= 0 { + timeout = DefaultTimeout + } + args := DescribeDisksArgs{ + RegionId: regionId, + DiskIds: []string{diskId}, + } + + for { + disks, _, err := client.DescribeDisks(&args) + if err != nil { + return err + } + if disks == nil || len(disks) == 0 { + return common.GetClientErrorFromString("Not found") + } + if disks[0].Status == status { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/images.go b/vendor/github.com/denverdino/aliyungo/ecs/images.go new file mode 100644 index 0000000000..c623caf9c3 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/images.go @@ -0,0 +1,280 @@ +package ecs + +import ( + "net/url" + "strconv" + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +// ImageOwnerAlias represents image owner +type ImageOwnerAlias string + +// Constants of image owner +const ( + ImageOwnerSystem = ImageOwnerAlias("system") + ImageOwnerSelf = ImageOwnerAlias("self") + ImageOwnerOthers = ImageOwnerAlias("others") + ImageOwnerMarketplace = ImageOwnerAlias("marketplace") + ImageOwnerDefault = ImageOwnerAlias("") //Return the values for system, self, and others +) + +type ImageStatus string + +const ( + ImageStatusAvailable = ImageStatus("Available") + ImageStatusUnAvailable = ImageStatus("UnAvailable") + ImageStatusCreating = ImageStatus("Creating") + ImageStatusCreateFailed = ImageStatus("CreateFailed") +) + +type ImageUsage string + +const ( + ImageUsageInstance = ImageUsage("instance") + ImageUsageNone = ImageUsage("none") +) + +// DescribeImagesArgs repsents arguements to describe images +type DescribeImagesArgs struct { + RegionId common.Region + ImageId string + SnapshotId string + ImageName string + Status ImageStatus + ImageOwnerAlias ImageOwnerAlias + common.Pagination +} + +type DescribeImagesResponse struct { + common.Response + common.PaginationResult + + RegionId common.Region + Images struct { + Image []ImageType + } +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&diskdevicemapping +type DiskDeviceMapping struct { + SnapshotId string + //Why Size Field is string-type. + Size string + Device string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&imagetype +type ImageType struct { + ImageId string + ImageVersion string + Architecture string + ImageName string + Description string + Size int + ImageOwnerAlias string + OSName string + OSType string + Platform string + DiskDeviceMappings struct { + DiskDeviceMapping []DiskDeviceMapping + } + ProductCode string + IsSubscribed bool + IsSelfShared string + IsCopied bool + IsSupportIoOptimized bool + Progress string + Usage ImageUsage + Status ImageStatus + CreationTime util.ISO6801Time +} + +// DescribeImages describes images +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/image&describeimages +func (client *Client) DescribeImages(args *DescribeImagesArgs) (images []ImageType, pagination *common.PaginationResult, err error) { + + args.Validate() + response := DescribeImagesResponse{} + err = client.Invoke("DescribeImages", args, &response) + if err != nil { + return nil, nil, err + } + return response.Images.Image, &response.PaginationResult, nil +} + +// CreateImageArgs repsents arguements to create image +type CreateImageArgs struct { + RegionId common.Region + SnapshotId string + ImageName string + ImageVersion string + Description string + ClientToken string +} + +type CreateImageResponse struct { + common.Response + + ImageId string +} + +// CreateImage creates a new image +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/image&createimage +func (client *Client) CreateImage(args *CreateImageArgs) (imageId string, err error) { + response := &CreateImageResponse{} + err = client.Invoke("CreateImage", args, &response) + if err != nil { + return "", err + } + return response.ImageId, nil +} + +type DeleteImageArgs struct { + RegionId common.Region + ImageId string +} + +type DeleteImageResponse struct { + common.Response +} + +// DeleteImage deletes Image +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/image&deleteimage +func (client *Client) DeleteImage(regionId common.Region, imageId string) error { + args := DeleteImageArgs{ + RegionId: regionId, + ImageId: imageId, + } + + response := &DeleteImageResponse{} + return client.Invoke("DeleteImage", &args, &response) +} + +// ModifyImageSharePermission repsents arguements to share image +type ModifyImageSharePermissionArgs struct { + RegionId common.Region + ImageId string + AddAccount []string + RemoveAccount []string +} + +// You can read doc at http://help.aliyun.com/document_detail/ecs/open-api/image/modifyimagesharepermission.html +func (client *Client) ModifyImageSharePermission(args *ModifyImageSharePermissionArgs) error { + req := url.Values{} + req.Add("RegionId", string(args.RegionId)) + req.Add("ImageId", args.ImageId) + + for i, item := range args.AddAccount { + req.Add("AddAccount."+strconv.Itoa(i+1), item) + } + for i, item := range args.RemoveAccount { + req.Add("RemoveAccount."+strconv.Itoa(i+1), item) + } + + return client.Invoke("ModifyImageSharePermission", req, &common.Response{}) +} + +type AccountType struct { + AliyunId string +} +type ImageSharePermissionResponse struct { + common.Response + ImageId string + RegionId string + Accounts struct { + Account []AccountType + } + TotalCount int + PageNumber int + PageSize int +} + +func (client *Client) DescribeImageSharePermission(args *ModifyImageSharePermissionArgs) (*ImageSharePermissionResponse, error) { + response := ImageSharePermissionResponse{} + err := client.Invoke("DescribeImageSharePermission", args, &response) + return &response, err +} + +type CopyImageArgs struct { + RegionId common.Region + ImageId string + DestinationRegionId common.Region + DestinationImageName string + DestinationDescription string + ClientToken string +} + +type CopyImageResponse struct { + common.Response + ImageId string +} + +// You can read doc at https://help.aliyun.com/document_detail/25538.html +func (client *Client) CopyImage(args *CopyImageArgs) (string, error) { + response := &CopyImageResponse{} + err := client.Invoke("CopyImage", args, &response) + if err != nil { + return "", err + } + return response.ImageId, nil +} + +// Default timeout value for WaitForImageReady method +const ImageDefaultTimeout = 120 + +//Wait Image ready +func (client *Client) WaitForImageReady(regionId common.Region, imageId string, timeout int) error { + if timeout <= 0 { + timeout = ImageDefaultTimeout + } + for { + args := DescribeImagesArgs{ + RegionId: regionId, + ImageId: imageId, + Status: ImageStatusCreating, + } + + images, _, err := client.DescribeImages(&args) + if err != nil { + return err + } + if images == nil || len(images) == 0 { + args.Status = ImageStatusAvailable + images, _, er := client.DescribeImages(&args) + if er == nil && len(images) == 1 { + break + } else { + return common.GetClientErrorFromString("Not found") + } + } + if images[0].Progress == "100%" { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} + +type CancelCopyImageRequest struct { + regionId common.Region + ImageId string +} + +// You can read doc at https://help.aliyun.com/document_detail/25539.html +func (client *Client) CancelCopyImage(regionId common.Region, imageId string) error { + response := &common.Response{} + err := client.Invoke("CancelCopyImage", &CancelCopyImageRequest{regionId, imageId}, &response) + return err +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/instance_types.go b/vendor/github.com/denverdino/aliyungo/ecs/instance_types.go new file mode 100644 index 0000000000..1e3e94bb94 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/instance_types.go @@ -0,0 +1,51 @@ +package ecs + +import "github.com/denverdino/aliyungo/common" + +type DescribeInstanceTypesArgs struct { + InstanceTypeFamily string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancetypeitemtype +type InstanceTypeItemType struct { + InstanceTypeId string + CpuCoreCount int + MemorySize float64 + InstanceTypeFamily string +} + +type DescribeInstanceTypesResponse struct { + common.Response + InstanceTypes struct { + InstanceType []InstanceTypeItemType + } +} + +// DescribeInstanceTypes describes all instance types +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/other&describeinstancetypes +func (client *Client) DescribeInstanceTypes() (instanceTypes []InstanceTypeItemType, err error) { + response := DescribeInstanceTypesResponse{} + + err = client.Invoke("DescribeInstanceTypes", &DescribeInstanceTypesArgs{}, &response) + + if err != nil { + return []InstanceTypeItemType{}, err + } + return response.InstanceTypes.InstanceType, nil + +} + +// support user args +func (client *Client) DescribeInstanceTypesNew(args *DescribeInstanceTypesArgs) (instanceTypes []InstanceTypeItemType, err error) { + response := DescribeInstanceTypesResponse{} + + err = client.Invoke("DescribeInstanceTypes", args, &response) + + if err != nil { + return []InstanceTypeItemType{}, err + } + return response.InstanceTypes.InstanceType, nil + +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/instances.go b/vendor/github.com/denverdino/aliyungo/ecs/instances.go new file mode 100644 index 0000000000..cce16dff48 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/instances.go @@ -0,0 +1,540 @@ +package ecs + +import ( + "encoding/base64" + "encoding/json" + "strconv" + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +// InstanceStatus represents instance status +type InstanceStatus string + +// Constants of InstanceStatus +const ( + Creating = InstanceStatus("Creating") + Running = InstanceStatus("Running") + Starting = InstanceStatus("Starting") + + Stopped = InstanceStatus("Stopped") + Stopping = InstanceStatus("Stopping") +) + +type LockReason string + +const ( + LockReasonFinancial = LockReason("financial") + LockReasonSecurity = LockReason("security") +) + +type LockReasonType struct { + LockReason LockReason +} + +type DescribeUserdataArgs struct { + RegionId common.Region + InstanceId string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype +type DescribeUserdataItemType struct { + UserData string + InstanceId string + RegionId string +} + +type DescribeUserdataResponse struct { + common.Response + DescribeUserdataItemType +} + +// DescribeInstanceStatus describes instance status +// +// You can read doc at https://intl.aliyun.com/help/doc-detail/49227.htm +func (client *Client) DescribeUserdata(args *DescribeUserdataArgs) (userData *DescribeUserdataItemType, err error) { + response := DescribeUserdataResponse{} + + err = client.Invoke("DescribeUserdata", args, &response) + + if err == nil { + return &response.DescribeUserdataItemType, nil + } + + return nil, err +} + +type DescribeInstanceStatusArgs struct { + RegionId common.Region + ZoneId string + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype +type InstanceStatusItemType struct { + InstanceId string + Status InstanceStatus +} + +type DescribeInstanceStatusResponse struct { + common.Response + common.PaginationResult + InstanceStatuses struct { + InstanceStatus []InstanceStatusItemType + } +} + +// DescribeInstanceStatus describes instance status +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancestatus +func (client *Client) DescribeInstanceStatus(args *DescribeInstanceStatusArgs) (instanceStatuses []InstanceStatusItemType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeInstanceStatusResponse{} + + err = client.Invoke("DescribeInstanceStatus", args, &response) + + if err == nil { + return response.InstanceStatuses.InstanceStatus, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type StopInstanceArgs struct { + InstanceId string + ForceStop bool +} + +type StopInstanceResponse struct { + common.Response +} + +// StopInstance stops instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&stopinstance +func (client *Client) StopInstance(instanceId string, forceStop bool) error { + args := StopInstanceArgs{ + InstanceId: instanceId, + ForceStop: forceStop, + } + response := StopInstanceResponse{} + err := client.Invoke("StopInstance", &args, &response) + return err +} + +type StartInstanceArgs struct { + InstanceId string +} + +type StartInstanceResponse struct { + common.Response +} + +// StartInstance starts instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&startinstance +func (client *Client) StartInstance(instanceId string) error { + args := StartInstanceArgs{InstanceId: instanceId} + response := StartInstanceResponse{} + err := client.Invoke("StartInstance", &args, &response) + return err +} + +type RebootInstanceArgs struct { + InstanceId string + ForceStop bool +} + +type RebootInstanceResponse struct { + common.Response +} + +// RebootInstance reboot instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&rebootinstance +func (client *Client) RebootInstance(instanceId string, forceStop bool) error { + request := RebootInstanceArgs{ + InstanceId: instanceId, + ForceStop: forceStop, + } + response := RebootInstanceResponse{} + err := client.Invoke("RebootInstance", &request, &response) + return err +} + +type DescribeInstanceAttributeArgs struct { + InstanceId string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&operationlockstype +type OperationLocksType struct { + LockReason []LockReasonType //enum for financial, security +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&securitygroupidsettype +type SecurityGroupIdSetType struct { + SecurityGroupId string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&ipaddresssettype +type IpAddressSetType struct { + IpAddress []string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vpcattributestype +type VpcAttributesType struct { + VpcId string + VSwitchId string + PrivateIpAddress IpAddressSetType + NatIpAddress string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddressassociatetype +type EipAddressAssociateType struct { + AllocationId string + IpAddress string + Bandwidth int + InternetChargeType common.InternetChargeType +} + +// Experimental feature +type SpotStrategyType string + +// Constants of SpotStrategyType +const ( + NoSpot = SpotStrategyType("NoSpot") + SpotWithPriceLimit = SpotStrategyType("SpotWithPriceLimit") +) + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instanceattributestype +type InstanceAttributesType struct { + InstanceId string + InstanceName string + Description string + ImageId string + RegionId common.Region + ZoneId string + CPU int + Memory int + ClusterId string + InstanceType string + InstanceTypeFamily string + HostName string + SerialNumber string + Status InstanceStatus + OperationLocks OperationLocksType + SecurityGroupIds struct { + SecurityGroupId []string + } + PublicIpAddress IpAddressSetType + InnerIpAddress IpAddressSetType + InstanceNetworkType string //enum Classic | Vpc + InternetMaxBandwidthIn int + InternetMaxBandwidthOut int + InternetChargeType common.InternetChargeType + CreationTime util.ISO6801Time //time.Time + VpcAttributes VpcAttributesType + EipAddress EipAddressAssociateType + IoOptimized StringOrBool + InstanceChargeType common.InstanceChargeType + ExpiredTime util.ISO6801Time + Tags struct { + Tag []TagItemType + } + SpotStrategy SpotStrategyType +} + +type DescribeInstanceAttributeResponse struct { + common.Response + InstanceAttributesType +} + +// DescribeInstanceAttribute describes instance attribute +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstanceattribute +func (client *Client) DescribeInstanceAttribute(instanceId string) (instance *InstanceAttributesType, err error) { + args := DescribeInstanceAttributeArgs{InstanceId: instanceId} + + response := DescribeInstanceAttributeResponse{} + err = client.Invoke("DescribeInstanceAttribute", &args, &response) + if err != nil { + return nil, err + } + return &response.InstanceAttributesType, err +} + +type ModifyInstanceAttributeArgs struct { + InstanceId string + InstanceName string + Description string + Password string + HostName string +} + +type ModifyInstanceAttributeResponse struct { + common.Response +} + +//ModifyInstanceAttribute modify instance attrbute +// +// You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/modifyinstanceattribute.html +func (client *Client) ModifyInstanceAttribute(args *ModifyInstanceAttributeArgs) error { + response := ModifyInstanceAttributeResponse{} + err := client.Invoke("ModifyInstanceAttribute", args, &response) + return err +} + +// Default timeout value for WaitForInstance method +const InstanceDefaultTimeout = 120 + +// WaitForInstance waits for instance to given status +func (client *Client) WaitForInstance(instanceId string, status InstanceStatus, timeout int) error { + if timeout <= 0 { + timeout = InstanceDefaultTimeout + } + for { + instance, err := client.DescribeInstanceAttribute(instanceId) + if err != nil { + return err + } + if instance.Status == status { + //TODO + //Sleep one more time for timing issues + time.Sleep(DefaultWaitForInterval * time.Second) + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + + } + return nil +} + +type DescribeInstanceVncUrlArgs struct { + RegionId common.Region + InstanceId string +} + +type DescribeInstanceVncUrlResponse struct { + common.Response + VncUrl string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancevncurl +func (client *Client) DescribeInstanceVncUrl(args *DescribeInstanceVncUrlArgs) (string, error) { + response := DescribeInstanceVncUrlResponse{} + + err := client.Invoke("DescribeInstanceVncUrl", args, &response) + + if err == nil { + return response.VncUrl, nil + } + + return "", err +} + +type DescribeInstancesArgs struct { + RegionId common.Region + VpcId string + VSwitchId string + ZoneId string + InstanceIds string + InstanceNetworkType string + InstanceName string + Status InstanceStatus + PrivateIpAddresses string + InnerIpAddresses string + PublicIpAddresses string + SecurityGroupId string + Tag map[string]string + InstanceType string + SpotStrategy SpotStrategyType + common.Pagination +} + +type DescribeInstancesResponse struct { + common.Response + common.PaginationResult + Instances struct { + Instance []InstanceAttributesType + } +} + +// DescribeInstances describes instances +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstances +func (client *Client) DescribeInstances(args *DescribeInstancesArgs) (instances []InstanceAttributesType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeInstancesResponse{} + + err = client.Invoke("DescribeInstances", args, &response) + + if err == nil { + return response.Instances.Instance, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type DeleteInstanceArgs struct { + InstanceId string +} + +type DeleteInstanceResponse struct { + common.Response +} + +// DeleteInstance deletes instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&deleteinstance +func (client *Client) DeleteInstance(instanceId string) error { + args := DeleteInstanceArgs{InstanceId: instanceId} + response := DeleteInstanceResponse{} + err := client.Invoke("DeleteInstance", &args, &response) + return err +} + +type DataDiskType struct { + Size int + Category DiskCategory //Enum cloud, ephemeral, ephemeral_ssd + SnapshotId string + DiskName string + Description string + Device string + DeleteWithInstance bool +} + +type SystemDiskType struct { + Size int + Category DiskCategory //Enum cloud, ephemeral, ephemeral_ssd + DiskName string + Description string +} + +type IoOptimized string + +type StringOrBool struct { + Value bool +} + +// UnmarshalJSON implements the json.Unmarshaller interface. +func (io *StringOrBool) UnmarshalJSON(value []byte) error { + if value[0] == '"' { + var str string + err := json.Unmarshal(value, &str) + if err == nil { + io.Value = (str == "true" || str == "optimized") + } + return err + } + var boolVal bool + err := json.Unmarshal(value, &boolVal) + if err == nil { + io.Value = boolVal + } + return err +} + +func (io StringOrBool) Bool() bool { + return io.Value +} + +func (io StringOrBool) String() string { + return strconv.FormatBool(io.Value) +} + +var ( + IoOptimizedNone = IoOptimized("none") + IoOptimizedOptimized = IoOptimized("optimized") +) + +type CreateInstanceArgs struct { + RegionId common.Region + ZoneId string + ImageId string + InstanceType string + SecurityGroupId string + InstanceName string + Description string + InternetChargeType common.InternetChargeType + InternetMaxBandwidthIn int + InternetMaxBandwidthOut int + HostName string + Password string + IoOptimized IoOptimized + SystemDisk SystemDiskType + DataDisk []DataDiskType + VSwitchId string + PrivateIpAddress string + ClientToken string + InstanceChargeType common.InstanceChargeType + Period int + UserData string + AutoRenew bool + AutoRenewPeriod int + SpotStrategy SpotStrategyType +} + +type CreateInstanceResponse struct { + common.Response + InstanceId string +} + +// CreateInstance creates instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&createinstance +func (client *Client) CreateInstance(args *CreateInstanceArgs) (instanceId string, err error) { + if args.UserData != "" { + // Encode to base64 string + args.UserData = base64.StdEncoding.EncodeToString([]byte(args.UserData)) + } + response := CreateInstanceResponse{} + err = client.Invoke("CreateInstance", args, &response) + if err != nil { + return "", err + } + return response.InstanceId, err +} + +type SecurityGroupArgs struct { + InstanceId string + SecurityGroupId string +} + +type SecurityGroupResponse struct { + common.Response +} + +//JoinSecurityGroup +// +//You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/joinsecuritygroup.html +func (client *Client) JoinSecurityGroup(instanceId string, securityGroupId string) error { + args := SecurityGroupArgs{InstanceId: instanceId, SecurityGroupId: securityGroupId} + response := SecurityGroupResponse{} + err := client.Invoke("JoinSecurityGroup", &args, &response) + return err +} + +//LeaveSecurityGroup +// +//You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/leavesecuritygroup.html +func (client *Client) LeaveSecurityGroup(instanceId string, securityGroupId string) error { + args := SecurityGroupArgs{InstanceId: instanceId, SecurityGroupId: securityGroupId} + response := SecurityGroupResponse{} + err := client.Invoke("LeaveSecurityGroup", &args, &response) + return err +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/monitoring.go b/vendor/github.com/denverdino/aliyungo/ecs/monitoring.go new file mode 100644 index 0000000000..6123d28b71 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/monitoring.go @@ -0,0 +1,136 @@ +package ecs + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type DescribeInstanceMonitorDataArgs struct { + InstanceId string + StartTime util.ISO6801Time + EndTime util.ISO6801Time + Period int //Default 60s +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancemonitordatatype +type InstanceMonitorDataType struct { + InstanceId string + CPU int + IntranetRX int + IntranetTX int + IntranetBandwidth int + InternetRX int + InternetTX int + InternetBandwidth int + IOPSRead int + IOPSWrite int + BPSRead int + BPSWrite int + TimeStamp util.ISO6801Time +} + +type DescribeInstanceMonitorDataResponse struct { + common.Response + MonitorData struct { + InstanceMonitorData []InstanceMonitorDataType + } +} + +// DescribeInstanceMonitorData describes instance monitoring data +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/monitor&describeinstancemonitordata +func (client *Client) DescribeInstanceMonitorData(args *DescribeInstanceMonitorDataArgs) (monitorData []InstanceMonitorDataType, err error) { + if args.Period == 0 { + args.Period = 60 + } + response := DescribeInstanceMonitorDataResponse{} + err = client.Invoke("DescribeInstanceMonitorData", args, &response) + if err != nil { + return nil, err + } + return response.MonitorData.InstanceMonitorData, err +} + +type DescribeEipMonitorDataArgs struct { + AllocationId string + StartTime util.ISO6801Time + EndTime util.ISO6801Time + Period int //Default 60s +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipmonitordatatype +type EipMonitorDataType struct { + EipRX int + EipTX int + EipFlow int + EipBandwidth int + EipPackets int + TimeStamp util.ISO6801Time +} + +type DescribeEipMonitorDataResponse struct { + common.Response + EipMonitorDatas struct { + EipMonitorData []EipMonitorDataType + } +} + +// DescribeEipMonitorData describes EIP monitoring data +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/monitor&describeeipmonitordata +func (client *Client) DescribeEipMonitorData(args *DescribeEipMonitorDataArgs) (monitorData []EipMonitorDataType, err error) { + if args.Period == 0 { + args.Period = 60 + } + response := DescribeEipMonitorDataResponse{} + err = client.Invoke("DescribeEipMonitorData", args, &response) + if err != nil { + return nil, err + } + return response.EipMonitorDatas.EipMonitorData, err +} + +type DescribeDiskMonitorDataArgs struct { + DiskId string + StartTime util.ISO6801Time + EndTime util.ISO6801Time + Period int //Default 60s +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&diskmonitordatatype +type DiskMonitorDataType struct { + DiskId string + IOPSRead int + IOPSWrite int + IOPSTotal int + BPSRead int + BPSWrite int + BPSTotal int + TimeStamp util.ISO6801Time +} + +type DescribeDiskMonitorDataResponse struct { + common.Response + TotalCount int + MonitorData struct { + DiskMonitorData []DiskMonitorDataType + } +} + +// DescribeDiskMonitorData describes disk monitoring data +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/monitor&describediskmonitordata +func (client *Client) DescribeDiskMonitorData(args *DescribeDiskMonitorDataArgs) (monitorData []DiskMonitorDataType, totalCount int, err error) { + if args.Period == 0 { + args.Period = 60 + } + response := DescribeDiskMonitorDataResponse{} + err = client.Invoke("DescribeDiskMonitorData", args, &response) + if err != nil { + return nil, 0, err + } + return response.MonitorData.DiskMonitorData, response.TotalCount, err +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/networks.go b/vendor/github.com/denverdino/aliyungo/ecs/networks.go new file mode 100644 index 0000000000..fecc7af1a0 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/networks.go @@ -0,0 +1,249 @@ +// API on Network + +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type AllocatePublicIpAddressArgs struct { + InstanceId string +} + +type AllocatePublicIpAddressResponse struct { + common.Response + + IpAddress string +} + +// AllocatePublicIpAddress allocates Public Ip Address +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&allocatepublicipaddress +func (client *Client) AllocatePublicIpAddress(instanceId string) (ipAddress string, err error) { + args := AllocatePublicIpAddressArgs{ + InstanceId: instanceId, + } + response := AllocatePublicIpAddressResponse{} + err = client.Invoke("AllocatePublicIpAddress", &args, &response) + if err != nil { + return "", err + } + return response.IpAddress, nil +} + +type ModifyInstanceNetworkSpec struct { + InstanceId string + InternetMaxBandwidthOut *int + InternetMaxBandwidthIn *int +} + +type ModifyInstanceNetworkSpecResponse struct { + common.Response +} + +// ModifyInstanceNetworkSpec modifies instance network spec +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&modifyinstancenetworkspec +func (client *Client) ModifyInstanceNetworkSpec(args *ModifyInstanceNetworkSpec) error { + + response := ModifyInstanceNetworkSpecResponse{} + return client.Invoke("ModifyInstanceNetworkSpec", args, &response) +} + +type AllocateEipAddressArgs struct { + RegionId common.Region + Bandwidth int + InternetChargeType common.InternetChargeType + ClientToken string +} + +type AllocateEipAddressResponse struct { + common.Response + EipAddress string + AllocationId string +} + +// AllocateEipAddress allocates Eip Address +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&allocateeipaddress +func (client *Client) AllocateEipAddress(args *AllocateEipAddressArgs) (EipAddress string, AllocationId string, err error) { + if args.Bandwidth == 0 { + args.Bandwidth = 5 + } + response := AllocateEipAddressResponse{} + err = client.Invoke("AllocateEipAddress", args, &response) + if err != nil { + return "", "", err + } + return response.EipAddress, response.AllocationId, nil +} + +type AssociateEipAddressArgs struct { + AllocationId string + InstanceId string +} + +type AssociateEipAddressResponse struct { + common.Response +} + +// AssociateEipAddress associates EIP address to VM instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&associateeipaddress +func (client *Client) AssociateEipAddress(allocationId string, instanceId string) error { + args := AssociateEipAddressArgs{ + AllocationId: allocationId, + InstanceId: instanceId, + } + response := ModifyInstanceNetworkSpecResponse{} + return client.Invoke("AssociateEipAddress", &args, &response) +} + +// Status of disks +type EipStatus string + +const ( + EipStatusAssociating = EipStatus("Associating") + EipStatusUnassociating = EipStatus("Unassociating") + EipStatusInUse = EipStatus("InUse") + EipStatusAvailable = EipStatus("Available") +) + +type DescribeEipAddressesArgs struct { + RegionId common.Region + Status EipStatus //enum Associating | Unassociating | InUse | Available + EipAddress string + AllocationId string + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddresssettype +type EipAddressSetType struct { + RegionId common.Region + IpAddress string + AllocationId string + Status EipStatus + InstanceId string + Bandwidth string // Why string + InternetChargeType common.InternetChargeType + OperationLocks OperationLocksType + AllocationTime util.ISO6801Time +} + +type DescribeEipAddressesResponse struct { + common.Response + common.PaginationResult + EipAddresses struct { + EipAddress []EipAddressSetType + } +} + +// DescribeInstanceStatus describes instance status +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&describeeipaddresses +func (client *Client) DescribeEipAddresses(args *DescribeEipAddressesArgs) (eipAddresses []EipAddressSetType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeEipAddressesResponse{} + + err = client.Invoke("DescribeEipAddresses", args, &response) + + if err == nil { + return response.EipAddresses.EipAddress, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type ModifyEipAddressAttributeArgs struct { + AllocationId string + Bandwidth int +} + +type ModifyEipAddressAttributeResponse struct { + common.Response +} + +// ModifyEipAddressAttribute Modifies EIP attribute +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&modifyeipaddressattribute +func (client *Client) ModifyEipAddressAttribute(allocationId string, bandwidth int) error { + args := ModifyEipAddressAttributeArgs{ + AllocationId: allocationId, + Bandwidth: bandwidth, + } + response := ModifyEipAddressAttributeResponse{} + return client.Invoke("ModifyEipAddressAttribute", &args, &response) +} + +type UnallocateEipAddressArgs struct { + AllocationId string + InstanceId string +} + +type UnallocateEipAddressResponse struct { + common.Response +} + +// UnassociateEipAddress unallocates Eip Address from instance +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&unassociateeipaddress +func (client *Client) UnassociateEipAddress(allocationId string, instanceId string) error { + args := UnallocateEipAddressArgs{ + AllocationId: allocationId, + InstanceId: instanceId, + } + response := UnallocateEipAddressResponse{} + return client.Invoke("UnassociateEipAddress", &args, &response) +} + +type ReleaseEipAddressArgs struct { + AllocationId string +} + +type ReleaseEipAddressResponse struct { + common.Response +} + +// ReleaseEipAddress releases Eip address +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&releaseeipaddress +func (client *Client) ReleaseEipAddress(allocationId string) error { + args := ReleaseEipAddressArgs{ + AllocationId: allocationId, + } + response := ReleaseEipAddressResponse{} + return client.Invoke("ReleaseEipAddress", &args, &response) +} + +// WaitForVSwitchAvailable waits for VSwitch to given status +func (client *Client) WaitForEip(regionId common.Region, allocationId string, status EipStatus, timeout int) error { + if timeout <= 0 { + timeout = DefaultTimeout + } + args := DescribeEipAddressesArgs{ + RegionId: regionId, + AllocationId: allocationId, + } + for { + eips, _, err := client.DescribeEipAddresses(&args) + if err != nil { + return err + } + if len(eips) == 0 { + return common.GetClientErrorFromString("Not found") + } + if eips[0].Status == status { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/regions.go b/vendor/github.com/denverdino/aliyungo/ecs/regions.go new file mode 100644 index 0000000000..d8ed72e8d3 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/regions.go @@ -0,0 +1,34 @@ +package ecs + +import "github.com/denverdino/aliyungo/common" + +type DescribeRegionsArgs struct { +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype®iontype +type RegionType struct { + RegionId common.Region + LocalName string +} + +type DescribeRegionsResponse struct { + common.Response + Regions struct { + Region []RegionType + } +} + +// DescribeRegions describes regions +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/region&describeregions +func (client *Client) DescribeRegions() (regions []RegionType, err error) { + response := DescribeRegionsResponse{} + + err = client.Invoke("DescribeRegions", &DescribeRegionsArgs{}, &response) + + if err != nil { + return []RegionType{}, err + } + return response.Regions.Region, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go b/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go new file mode 100644 index 0000000000..01f43127c7 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go @@ -0,0 +1,176 @@ +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type DescribeRouteTablesArgs struct { + VRouterId string + RouteTableId string + common.Pagination +} + +type RouteTableType string + +const ( + RouteTableSystem = RouteTableType("System") + RouteTableCustom = RouteTableType("Custom") +) + +type RouteEntryStatus string + +const ( + RouteEntryStatusPending = RouteEntryStatus("Pending") + RouteEntryStatusAvailable = RouteEntryStatus("Available") + RouteEntryStatusModifying = RouteEntryStatus("Modifying") +) + +type NextHopListType struct { + NextHopList struct { + NextHopItem []NextHopItemType + } +} + +type NextHopItemType struct { + NextHopType string + NextHopId string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&routeentrysettype +type RouteEntrySetType struct { + RouteTableId string + DestinationCidrBlock string + Type RouteTableType + NextHopType string + NextHopId string + NextHopList NextHopListType + InstanceId string + Status RouteEntryStatus // enum Pending | Available | Modifying +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&routetablesettype +type RouteTableSetType struct { + VRouterId string + RouteTableId string + RouteEntrys struct { + RouteEntry []RouteEntrySetType + } + RouteTableType RouteTableType + CreationTime util.ISO6801Time +} + +type DescribeRouteTablesResponse struct { + common.Response + common.PaginationResult + RouteTables struct { + RouteTable []RouteTableSetType + } +} + +// DescribeRouteTables describes Virtual Routers +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/routertable&describeroutetables +func (client *Client) DescribeRouteTables(args *DescribeRouteTablesArgs) (routeTables []RouteTableSetType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeRouteTablesResponse{} + + err = client.Invoke("DescribeRouteTables", args, &response) + + if err == nil { + return response.RouteTables.RouteTable, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type NextHopType string + +const ( + NextHopIntance = NextHopType("Instance") //Default + NextHopTunnel = NextHopType("Tunnel") +) + +type CreateRouteEntryArgs struct { + RouteTableId string + DestinationCidrBlock string + NextHopType NextHopType + NextHopId string + ClientToken string +} + +type CreateRouteEntryResponse struct { + common.Response +} + +// CreateRouteEntry creates route entry +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/routertable&createrouteentry +func (client *Client) CreateRouteEntry(args *CreateRouteEntryArgs) error { + response := CreateRouteEntryResponse{} + return client.Invoke("CreateRouteEntry", args, &response) +} + +type DeleteRouteEntryArgs struct { + RouteTableId string + DestinationCidrBlock string + NextHopId string +} + +type DeleteRouteEntryResponse struct { + common.Response +} + +// DeleteRouteEntry deletes route entry +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/routertable&deleterouteentry +func (client *Client) DeleteRouteEntry(args *DeleteRouteEntryArgs) error { + response := DeleteRouteEntryResponse{} + return client.Invoke("DeleteRouteEntry", args, &response) +} + +// WaitForAllRouteEntriesAvailable waits for all route entries to Available status +func (client *Client) WaitForAllRouteEntriesAvailable(vrouterId string, routeTableId string, timeout int) error { + if timeout <= 0 { + timeout = DefaultTimeout + } + args := DescribeRouteTablesArgs{ + VRouterId: vrouterId, + RouteTableId: routeTableId, + } + for { + + routeTables, _, err := client.DescribeRouteTables(&args) + + if err != nil { + return err + } + if len(routeTables) == 0 { + return common.GetClientErrorFromString("Not found") + } + success := true + + loop: + for _, routeTable := range routeTables { + for _, routeEntry := range routeTable.RouteEntrys.RouteEntry { + if routeEntry.Status != RouteEntryStatusAvailable { + success = false + break loop + } + } + } + if success { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go new file mode 100644 index 0000000000..ef057393f6 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go @@ -0,0 +1,272 @@ +package ecs + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type NicType string + +const ( + NicTypeInternet = NicType("internet") + NicTypeIntranet = NicType("intranet") +) + +type IpProtocol string + +const ( + IpProtocolAll = IpProtocol("all") + IpProtocolTCP = IpProtocol("tcp") + IpProtocolUDP = IpProtocol("udp") + IpProtocolICMP = IpProtocol("icmp") + IpProtocolGRE = IpProtocol("gre") +) + +type PermissionPolicy string + +const ( + PermissionPolicyAccept = PermissionPolicy("accept") + PermissionPolicyDrop = PermissionPolicy("drop") +) + +type DescribeSecurityGroupAttributeArgs struct { + SecurityGroupId string + RegionId common.Region + NicType NicType //enum for internet (default) |intranet +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&permissiontype +type PermissionType struct { + IpProtocol IpProtocol + PortRange string + SourceCidrIp string + SourceGroupId string + SourceGroupOwnerAccount string + DestCidrIp string + DestGroupId string + DestGroupOwnerAccount string + Policy PermissionPolicy + NicType NicType + Priority int + Direction string + Description string +} + +type DescribeSecurityGroupAttributeResponse struct { + common.Response + + SecurityGroupId string + SecurityGroupName string + RegionId common.Region + Description string + Permissions struct { + Permission []PermissionType + } + VpcId string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&describesecuritygroupattribute +func (client *Client) DescribeSecurityGroupAttribute(args *DescribeSecurityGroupAttributeArgs) (response *DescribeSecurityGroupAttributeResponse, err error) { + response = &DescribeSecurityGroupAttributeResponse{} + err = client.Invoke("DescribeSecurityGroupAttribute", args, response) + if err != nil { + return nil, err + } + return response, nil +} + +type DescribeSecurityGroupsArgs struct { + RegionId common.Region + VpcId string + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&securitygroupitemtype +type SecurityGroupItemType struct { + SecurityGroupId string + SecurityGroupName string + Description string + VpcId string + CreationTime util.ISO6801Time +} + +type DescribeSecurityGroupsResponse struct { + common.Response + common.PaginationResult + + RegionId common.Region + SecurityGroups struct { + SecurityGroup []SecurityGroupItemType + } +} + +// DescribeSecurityGroups describes security groups +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&describesecuritygroups +func (client *Client) DescribeSecurityGroups(args *DescribeSecurityGroupsArgs) (securityGroupItems []SecurityGroupItemType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeSecurityGroupsResponse{} + + err = client.Invoke("DescribeSecurityGroups", args, &response) + + if err != nil { + return nil, nil, err + } + + return response.SecurityGroups.SecurityGroup, &response.PaginationResult, nil +} + +type CreateSecurityGroupArgs struct { + RegionId common.Region + SecurityGroupName string + Description string + VpcId string + ClientToken string +} + +type CreateSecurityGroupResponse struct { + common.Response + + SecurityGroupId string +} + +// CreateSecurityGroup creates security group +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&createsecuritygroup +func (client *Client) CreateSecurityGroup(args *CreateSecurityGroupArgs) (securityGroupId string, err error) { + response := CreateSecurityGroupResponse{} + err = client.Invoke("CreateSecurityGroup", args, &response) + if err != nil { + return "", err + } + return response.SecurityGroupId, err +} + +type DeleteSecurityGroupArgs struct { + RegionId common.Region + SecurityGroupId string +} + +type DeleteSecurityGroupResponse struct { + common.Response +} + +// DeleteSecurityGroup deletes security group +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&deletesecuritygroup +func (client *Client) DeleteSecurityGroup(regionId common.Region, securityGroupId string) error { + args := DeleteSecurityGroupArgs{ + RegionId: regionId, + SecurityGroupId: securityGroupId, + } + response := DeleteSecurityGroupResponse{} + err := client.Invoke("DeleteSecurityGroup", &args, &response) + return err +} + +type ModifySecurityGroupAttributeArgs struct { + RegionId common.Region + SecurityGroupId string + SecurityGroupName string + Description string +} + +type ModifySecurityGroupAttributeResponse struct { + common.Response +} + +// ModifySecurityGroupAttribute modifies attribute of security group +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&modifysecuritygroupattribute +func (client *Client) ModifySecurityGroupAttribute(args *ModifySecurityGroupAttributeArgs) error { + response := ModifySecurityGroupAttributeResponse{} + err := client.Invoke("ModifySecurityGroupAttribute", args, &response) + return err +} + +type AuthorizeSecurityGroupArgs struct { + SecurityGroupId string + RegionId common.Region + IpProtocol IpProtocol + PortRange string + SourceGroupId string + SourceGroupOwnerAccount string + SourceGroupOwnerID string + SourceCidrIp string // IPv4 only, default 0.0.0.0/0 + Policy PermissionPolicy // enum of accept (default) | drop + Priority int // 1 - 100, default 1 + NicType NicType // enum of internet | intranet (default) +} + +type AuthorizeSecurityGroupResponse struct { + common.Response +} + +// AuthorizeSecurityGroup authorize permissions to security group +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&authorizesecuritygroup +func (client *Client) AuthorizeSecurityGroup(args *AuthorizeSecurityGroupArgs) error { + response := AuthorizeSecurityGroupResponse{} + err := client.Invoke("AuthorizeSecurityGroup", args, &response) + return err +} + +type RevokeSecurityGroupArgs struct { + AuthorizeSecurityGroupArgs +} + +type RevokeSecurityGroupResponse struct { + common.Response +} + +// You can read doc at https://help.aliyun.com/document_detail/25557.html?spm=5176.doc25554.6.755.O6Tjz0 +func (client *Client) RevokeSecurityGroup(args *RevokeSecurityGroupArgs) error { + response := RevokeSecurityGroupResponse{} + err := client.Invoke("RevokeSecurityGroup", args, &response) + return err +} + +type AuthorizeSecurityGroupEgressArgs struct { + SecurityGroupId string + RegionId common.Region + IpProtocol IpProtocol + PortRange string + DestGroupId string + DestGroupOwnerAccount string + DestGroupOwnerId string + DestCidrIp string // IPv4 only, default 0.0.0.0/0 + Policy PermissionPolicy // enum of accept (default) | drop + Priority int // 1 - 100, default 1 + NicType NicType // enum of internet | intranet (default) +} + +type AuthorizeSecurityGroupEgressResponse struct { + common.Response +} + +// AuthorizeSecurityGroup authorize permissions to security group +// +// You can read doc at https://help.aliyun.com/document_detail/25560.html +func (client *Client) AuthorizeSecurityGroupEgress(args *AuthorizeSecurityGroupEgressArgs) error { + response := AuthorizeSecurityGroupEgressResponse{} + err := client.Invoke("AuthorizeSecurityGroupEgress", args, &response) + return err +} + +type RevokeSecurityGroupEgressArgs struct { + AuthorizeSecurityGroupEgressArgs +} + +type RevokeSecurityGroupEgressResponse struct { + common.Response +} + +// You can read doc at https://help.aliyun.com/document_detail/25561.html?spm=5176.doc25557.6.759.qcR4Az +func (client *Client) RevokeSecurityGroupEgress(args *RevokeSecurityGroupEgressArgs) error { + response := RevokeSecurityGroupEgressResponse{} + err := client.Invoke("RevokeSecurityGroupEgress", args, &response) + return err +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go b/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go new file mode 100644 index 0000000000..fb6f9c8e10 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go @@ -0,0 +1,131 @@ +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type DescribeSnapshotsArgs struct { + RegionId common.Region + InstanceId string + DiskId string + SnapshotIds []string //["s-xxxxxxxxx", "s-yyyyyyyyy", ..."s-zzzzzzzzz"] + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&snapshottype +type SnapshotType struct { + SnapshotId string + SnapshotName string + Description string + Progress string + SourceDiskId string + SourceDiskSize int + SourceDiskType string //enum for System | Data + ProductCode string + CreationTime util.ISO6801Time +} + +type DescribeSnapshotsResponse struct { + common.Response + common.PaginationResult + Snapshots struct { + Snapshot []SnapshotType + } +} + +// DescribeSnapshots describe snapshots +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/snapshot&describesnapshots +func (client *Client) DescribeSnapshots(args *DescribeSnapshotsArgs) (snapshots []SnapshotType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeSnapshotsResponse{} + + err = client.Invoke("DescribeSnapshots", args, &response) + + if err != nil { + return nil, nil, err + } + return response.Snapshots.Snapshot, &response.PaginationResult, nil + +} + +type DeleteSnapshotArgs struct { + SnapshotId string +} + +type DeleteSnapshotResponse struct { + common.Response +} + +// DeleteSnapshot deletes snapshot +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/snapshot&deletesnapshot +func (client *Client) DeleteSnapshot(snapshotId string) error { + args := DeleteSnapshotArgs{SnapshotId: snapshotId} + response := DeleteSnapshotResponse{} + + return client.Invoke("DeleteSnapshot", &args, &response) +} + +type CreateSnapshotArgs struct { + DiskId string + SnapshotName string + Description string + ClientToken string +} + +type CreateSnapshotResponse struct { + common.Response + SnapshotId string +} + +// CreateSnapshot creates a new snapshot +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/snapshot&createsnapshot +func (client *Client) CreateSnapshot(args *CreateSnapshotArgs) (snapshotId string, err error) { + + response := CreateSnapshotResponse{} + + err = client.Invoke("CreateSnapshot", args, &response) + if err == nil { + snapshotId = response.SnapshotId + } + return snapshotId, err +} + +// Default timeout value for WaitForSnapShotReady method +const SnapshotDefaultTimeout = 120 + +// WaitForSnapShotReady waits for snapshot ready +func (client *Client) WaitForSnapShotReady(regionId common.Region, snapshotId string, timeout int) error { + if timeout <= 0 { + timeout = SnapshotDefaultTimeout + } + for { + args := DescribeSnapshotsArgs{ + RegionId: regionId, + SnapshotIds: []string{snapshotId}, + } + + snapshots, _, err := client.DescribeSnapshots(&args) + if err != nil { + return err + } + if snapshots == nil || len(snapshots) == 0 { + return common.GetClientErrorFromString("Not found") + } + if snapshots[0].Progress == "100%" { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/tags.go b/vendor/github.com/denverdino/aliyungo/ecs/tags.go new file mode 100644 index 0000000000..5ffd4931aa --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/tags.go @@ -0,0 +1,120 @@ +package ecs + +import "github.com/denverdino/aliyungo/common" + +type TagResourceType string + +const ( + TagResourceImage = TagResourceType("image") + TagResourceInstance = TagResourceType("instance") + TagResourceSnapshot = TagResourceType("snapshot") + TagResourceDisk = TagResourceType("disk") +) + +type AddTagsArgs struct { + ResourceId string + ResourceType TagResourceType //image, instance, snapshot or disk + RegionId common.Region + Tag map[string]string +} + +type AddTagsResponse struct { + common.Response +} + +// AddTags Add tags to resource +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&addtags +func (client *Client) AddTags(args *AddTagsArgs) error { + response := AddTagsResponse{} + err := client.Invoke("AddTags", args, &response) + return err +} + +type RemoveTagsArgs struct { + ResourceId string + ResourceType TagResourceType //image, instance, snapshot or disk + RegionId common.Region + Tag map[string]string +} + +type RemoveTagsResponse struct { + common.Response +} + +// RemoveTags remove tags to resource +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&removetags +func (client *Client) RemoveTags(args *RemoveTagsArgs) error { + response := RemoveTagsResponse{} + err := client.Invoke("RemoveTags", args, &response) + return err +} + +type ResourceItemType struct { + ResourceId string + ResourceType TagResourceType + RegionId common.Region +} + +type DescribeResourceByTagsArgs struct { + ResourceType TagResourceType //image, instance, snapshot or disk + RegionId common.Region + Tag map[string]string + common.Pagination +} + +type DescribeResourceByTagsResponse struct { + common.Response + common.PaginationResult + Resources struct { + Resource []ResourceItemType + } +} + +// DescribeResourceByTags describe resource by tags +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&describeresourcebytags +func (client *Client) DescribeResourceByTags(args *DescribeResourceByTagsArgs) (resources []ResourceItemType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeResourceByTagsResponse{} + err = client.Invoke("DescribeResourceByTags", args, &response) + if err != nil { + return nil, nil, err + } + return response.Resources.Resource, &response.PaginationResult, nil +} + +type TagItemType struct { + TagKey string + TagValue string +} + +type DescribeTagsArgs struct { + RegionId common.Region + ResourceType TagResourceType //image, instance, snapshot or disk + ResourceId string + Tag map[string]string + common.Pagination +} + +type DescribeTagsResponse struct { + common.Response + common.PaginationResult + Tags struct { + Tag []TagItemType + } +} + +// DescribeResourceByTags describe resource by tags +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&describeresourcebytags +func (client *Client) DescribeTags(args *DescribeTagsArgs) (tags []TagItemType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeTagsResponse{} + err = client.Invoke("DescribeTags", args, &response) + if err != nil { + return nil, nil, err + } + return response.Tags.Tag, &response.PaginationResult, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/vpcs.go b/vendor/github.com/denverdino/aliyungo/ecs/vpcs.go new file mode 100644 index 0000000000..7a62857cd1 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/vpcs.go @@ -0,0 +1,151 @@ +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type CreateVpcArgs struct { + RegionId common.Region + CidrBlock string //192.168.0.0/16 or 172.16.0.0/16 (default) + VpcName string + Description string + ClientToken string +} + +type CreateVpcResponse struct { + common.Response + VpcId string + VRouterId string + RouteTableId string +} + +// CreateVpc creates Virtual Private Cloud +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&createvpc +func (client *Client) CreateVpc(args *CreateVpcArgs) (resp *CreateVpcResponse, err error) { + response := CreateVpcResponse{} + err = client.Invoke("CreateVpc", args, &response) + if err != nil { + return nil, err + } + return &response, err +} + +type DeleteVpcArgs struct { + VpcId string +} + +type DeleteVpcResponse struct { + common.Response +} + +// DeleteVpc deletes Virtual Private Cloud +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&deletevpc +func (client *Client) DeleteVpc(vpcId string) error { + args := DeleteVpcArgs{ + VpcId: vpcId, + } + response := DeleteVpcResponse{} + return client.Invoke("DeleteVpc", &args, &response) +} + +type VpcStatus string + +const ( + VpcStatusPending = VpcStatus("Pending") + VpcStatusAvailable = VpcStatus("Available") +) + +type DescribeVpcsArgs struct { + VpcId string + RegionId common.Region + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vpcsettype +type VpcSetType struct { + VpcId string + RegionId common.Region + Status VpcStatus // enum Pending | Available + VpcName string + VSwitchIds struct { + VSwitchId []string + } + CidrBlock string + VRouterId string + Description string + CreationTime util.ISO6801Time +} + +type DescribeVpcsResponse struct { + common.Response + common.PaginationResult + Vpcs struct { + Vpc []VpcSetType + } +} + +// DescribeInstanceStatus describes instance status +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&describevpcs +func (client *Client) DescribeVpcs(args *DescribeVpcsArgs) (vpcs []VpcSetType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeVpcsResponse{} + + err = client.Invoke("DescribeVpcs", args, &response) + + if err == nil { + return response.Vpcs.Vpc, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type ModifyVpcAttributeArgs struct { + VpcId string + VpcName string + Description string +} + +type ModifyVpcAttributeResponse struct { + common.Response +} + +// ModifyVpcAttribute modifies attribute of Virtual Private Cloud +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&modifyvpcattribute +func (client *Client) ModifyVpcAttribute(args *ModifyVpcAttributeArgs) error { + response := ModifyVpcAttributeResponse{} + return client.Invoke("ModifyVpcAttribute", args, &response) +} + +// WaitForInstance waits for instance to given status +func (client *Client) WaitForVpcAvailable(regionId common.Region, vpcId string, timeout int) error { + if timeout <= 0 { + timeout = DefaultTimeout + } + args := DescribeVpcsArgs{ + RegionId: regionId, + VpcId: vpcId, + } + for { + vpcs, _, err := client.DescribeVpcs(&args) + if err != nil { + return err + } + if len(vpcs) > 0 && vpcs[0].Status == VpcStatusAvailable { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/vrouters.go b/vendor/github.com/denverdino/aliyungo/ecs/vrouters.go new file mode 100644 index 0000000000..059a324bd9 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/vrouters.go @@ -0,0 +1,68 @@ +package ecs + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type DescribeVRoutersArgs struct { + VRouterId string + RegionId common.Region + common.Pagination +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vroutersettype +type VRouterSetType struct { + VRouterId string + RegionId common.Region + VpcId string + RouteTableIds struct { + RouteTableId []string + } + VRouterName string + Description string + CreationTime util.ISO6801Time +} + +type DescribeVRoutersResponse struct { + common.Response + common.PaginationResult + VRouters struct { + VRouter []VRouterSetType + } +} + +// DescribeVRouters describes Virtual Routers +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vrouter&describevrouters +func (client *Client) DescribeVRouters(args *DescribeVRoutersArgs) (vrouters []VRouterSetType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeVRoutersResponse{} + + err = client.Invoke("DescribeVRouters", args, &response) + + if err == nil { + return response.VRouters.VRouter, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type ModifyVRouterAttributeArgs struct { + VRouterId string + VRouterName string + Description string +} + +type ModifyVRouterAttributeResponse struct { + common.Response +} + +// ModifyVRouterAttribute modifies attribute of Virtual Router +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vrouter&modifyvrouterattribute +func (client *Client) ModifyVRouterAttribute(args *ModifyVRouterAttributeArgs) error { + response := ModifyVRouterAttributeResponse{} + return client.Invoke("ModifyVRouterAttribute", args, &response) +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/vswitches.go b/vendor/github.com/denverdino/aliyungo/ecs/vswitches.go new file mode 100644 index 0000000000..8bf10394a7 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/vswitches.go @@ -0,0 +1,152 @@ +package ecs + +import ( + "time" + + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type CreateVSwitchArgs struct { + ZoneId string + CidrBlock string + VpcId string + VSwitchName string + Description string + ClientToken string +} + +type CreateVSwitchResponse struct { + common.Response + VSwitchId string +} + +// CreateVSwitch creates Virtual Switch +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&createvswitch +func (client *Client) CreateVSwitch(args *CreateVSwitchArgs) (vswitchId string, err error) { + response := CreateVSwitchResponse{} + err = client.Invoke("CreateVSwitch", args, &response) + if err != nil { + return "", err + } + return response.VSwitchId, err +} + +type DeleteVSwitchArgs struct { + VSwitchId string +} + +type DeleteVSwitchResponse struct { + common.Response +} + +// DeleteVSwitch deletes Virtual Switch +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&deletevswitch +func (client *Client) DeleteVSwitch(VSwitchId string) error { + args := DeleteVSwitchArgs{ + VSwitchId: VSwitchId, + } + response := DeleteVSwitchResponse{} + return client.Invoke("DeleteVSwitch", &args, &response) +} + +type DescribeVSwitchesArgs struct { + VpcId string + VSwitchId string + ZoneId string + common.Pagination +} + +type VSwitchStatus string + +const ( + VSwitchStatusPending = VSwitchStatus("Pending") + VSwitchStatusAvailable = VSwitchStatus("Available") +) + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vswitchsettype +type VSwitchSetType struct { + VSwitchId string + VpcId string + Status VSwitchStatus // enum Pending | Available + CidrBlock string + ZoneId string + AvailableIpAddressCount int + Description string + VSwitchName string + CreationTime util.ISO6801Time +} + +type DescribeVSwitchesResponse struct { + common.Response + common.PaginationResult + VSwitches struct { + VSwitch []VSwitchSetType + } +} + +// DescribeVSwitches describes Virtual Switches +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&describevswitches +func (client *Client) DescribeVSwitches(args *DescribeVSwitchesArgs) (vswitches []VSwitchSetType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeVSwitchesResponse{} + + err = client.Invoke("DescribeVSwitches", args, &response) + + if err == nil { + return response.VSwitches.VSwitch, &response.PaginationResult, nil + } + + return nil, nil, err +} + +type ModifyVSwitchAttributeArgs struct { + VSwitchId string + VSwitchName string + Description string +} + +type ModifyVSwitchAttributeResponse struct { + common.Response +} + +// ModifyVSwitchAttribute modifies attribute of Virtual Private Cloud +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&modifyvswitchattribute +func (client *Client) ModifyVSwitchAttribute(args *ModifyVSwitchAttributeArgs) error { + response := ModifyVSwitchAttributeResponse{} + return client.Invoke("ModifyVSwitchAttribute", args, &response) +} + +// WaitForVSwitchAvailable waits for VSwitch to given status +func (client *Client) WaitForVSwitchAvailable(vpcId string, vswitchId string, timeout int) error { + if timeout <= 0 { + timeout = DefaultTimeout + } + args := DescribeVSwitchesArgs{ + VpcId: vpcId, + VSwitchId: vswitchId, + } + for { + vswitches, _, err := client.DescribeVSwitches(&args) + if err != nil { + return err + } + if len(vswitches) == 0 { + return common.GetClientErrorFromString("Not found") + } + if vswitches[0].Status == VSwitchStatusAvailable { + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + } + return nil +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/zones.go b/vendor/github.com/denverdino/aliyungo/ecs/zones.go new file mode 100644 index 0000000000..4818c760f8 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/zones.go @@ -0,0 +1,65 @@ +package ecs + +import "github.com/denverdino/aliyungo/common" + +type ResourceType string + +const ( + ResourceTypeInstance = ResourceType("Instance") + ResourceTypeDisk = ResourceType("Disk") + ResourceTypeVSwitch = ResourceType("VSwitch") + ResourceTypeIOOptimizedInstance = ResourceType("IoOptimized") +) + +type DescribeZonesArgs struct { + RegionId common.Region +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&availableresourcecreationtype +type AvailableResourceCreationType struct { + ResourceTypes []ResourceType //enum for Instance, Disk, VSwitch +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&availablediskcategoriestype +type AvailableDiskCategoriesType struct { + DiskCategories []DiskCategory //enum for cloud, ephemeral, ephemeral_ssd +} + +type AvailableInstanceTypesType struct { + InstanceTypes []string +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&zonetype +type ZoneType struct { + ZoneId string + LocalName string + AvailableInstanceTypes AvailableInstanceTypesType + AvailableResourceCreation AvailableResourceCreationType + AvailableDiskCategories AvailableDiskCategoriesType +} + +type DescribeZonesResponse struct { + common.Response + Zones struct { + Zone []ZoneType + } +} + +// DescribeZones describes zones +func (client *Client) DescribeZones(regionId common.Region) (zones []ZoneType, err error) { + args := DescribeZonesArgs{ + RegionId: regionId, + } + response := DescribeZonesResponse{} + + err = client.Invoke("DescribeZones", &args, &response) + + if err == nil { + return response.Zones.Zone, nil + } + + return []ZoneType{}, err +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/certificates.go b/vendor/github.com/denverdino/aliyungo/slb/certificates.go new file mode 100644 index 0000000000..5cbc4ac093 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/certificates.go @@ -0,0 +1,108 @@ +package slb + +import "github.com/denverdino/aliyungo/common" + +type UploadServerCertificateArgs struct { + RegionId common.Region + ServerCertificate string + ServerCertificateName string + PrivateKey string +} + +type UploadServerCertificateResponse struct { + common.Response + ServerCertificateId string + ServerCertificateName string + Fingerprint string +} + +// UploadServerCertificate Upload server certificate +// +// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&UploadServerCertificate +func (client *Client) UploadServerCertificate(args *UploadServerCertificateArgs) (response *UploadServerCertificateResponse, err error) { + response = &UploadServerCertificateResponse{} + err = client.Invoke("UploadServerCertificate", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type DeleteServerCertificateArgs struct { + RegionId common.Region + ServerCertificateId string +} + +type DeleteServerCertificateResponse struct { + common.Response +} + +// DeleteServerCertificate Delete server certificate +// +// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&DeleteServerCertificate +func (client *Client) DeleteServerCertificate(regionId common.Region, serverCertificateId string) (err error) { + args := &DeleteServerCertificateArgs{ + RegionId: regionId, + ServerCertificateId: serverCertificateId, + } + response := &DeleteServerCertificateResponse{} + return client.Invoke("DeleteServerCertificate", args, response) +} + +type SetServerCertificateNameArgs struct { + RegionId common.Region + ServerCertificateId string + ServerCertificateName string +} + +type SetServerCertificateNameResponse struct { + common.Response +} + +// SetServerCertificateName Set name of server certificate +// +// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&SetServerCertificateName +func (client *Client) SetServerCertificateName(regionId common.Region, serverCertificateId string, name string) (err error) { + args := &SetServerCertificateNameArgs{ + RegionId: regionId, + ServerCertificateId: serverCertificateId, + ServerCertificateName: name, + } + response := &SetServerCertificateNameResponse{} + return client.Invoke("SetServerCertificateName", args, response) +} + +type DescribeServerCertificatesArgs struct { + RegionId common.Region + ServerCertificateId string +} + +type ServerCertificateType struct { + RegionId common.Region + ServerCertificateId string + ServerCertificateName string + Fingerprint string +} + +type DescribeServerCertificatesResponse struct { + common.Response + ServerCertificates struct { + ServerCertificate []ServerCertificateType + } +} + +// DescribeServerCertificates Describe server certificates +// +// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&DescribeServerCertificates +func (client *Client) DescribeServerCertificatesArgs(regionId common.Region, serverCertificateId string) (serverCertificates []ServerCertificateType, err error) { + args := &DescribeServerCertificatesArgs{ + RegionId: regionId, + ServerCertificateId: serverCertificateId, + } + response := &DescribeServerCertificatesResponse{} + err = client.Invoke("DescribeServerCertificates", args, response) + if err != nil { + return nil, err + } + return response.ServerCertificates.ServerCertificate, err +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/client.go b/vendor/github.com/denverdino/aliyungo/slb/client.go new file mode 100644 index 0000000000..8dd26b6913 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/client.go @@ -0,0 +1,31 @@ +package slb + +import ( + "github.com/denverdino/aliyungo/common" + "os" +) + +type Client struct { + common.Client +} + +const ( + // SLBDefaultEndpoint is the default API endpoint of SLB services + SLBDefaultEndpoint = "https://slb.aliyuncs.com" + SLBAPIVersion = "2014-05-15" +) + +// NewClient creates a new instance of ECS client +func NewClient(accessKeyId, accessKeySecret string) *Client { + endpoint := os.Getenv("SLB_ENDPOINT") + if endpoint == "" { + endpoint = SLBDefaultEndpoint + } + return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret) +} + +func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client { + client := &Client{} + client.Init(endpoint, SLBAPIVersion, accessKeyId, accessKeySecret) + return client +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/listeners.go b/vendor/github.com/denverdino/aliyungo/slb/listeners.go new file mode 100644 index 0000000000..d435576190 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/listeners.go @@ -0,0 +1,495 @@ +package slb + +import ( + "fmt" + "strings" + "time" + + "github.com/denverdino/aliyungo/common" +) + +type ListenerStatus string + +const ( + Starting = ListenerStatus("starting") + Running = ListenerStatus("running") + Configuring = ListenerStatus("configuring") + Stopping = ListenerStatus("stopping") + Stopped = ListenerStatus("stopped") +) + +type SchedulerType string + +const ( + WRRScheduler = SchedulerType("wrr") + WLCScheduler = SchedulerType("wlc") +) + +type FlagType string + +const ( + OnFlag = FlagType("on") + OffFlag = FlagType("off") +) + +type StickySessionType string + +const ( + InsertStickySessionType = StickySessionType("insert") + ServerStickySessionType = StickySessionType("server") +) + +const BackendServerPort = -520 + +type HealthCheckHttpCodeType string + +const ( + HTTP_2XX = HealthCheckHttpCodeType("http_2xx") + HTTP_3XX = HealthCheckHttpCodeType("http_3xx") + HTTP_4XX = HealthCheckHttpCodeType("http_4xx") + HTTP_5XX = HealthCheckHttpCodeType("http_5xx") +) + +func EncodeHealthCheckHttpCodeType(healthCheckHttpCodes []HealthCheckHttpCodeType) (HealthCheckHttpCodeType, error) { + code := "" + + if nil == healthCheckHttpCodes || len(healthCheckHttpCodes) < 1 { + return "", fmt.Errorf("Invalid size of healthCheckHttpCodes") + } + + for _, healthCheckHttpCode := range healthCheckHttpCodes { + if strings.EqualFold(string(HTTP_2XX), string(healthCheckHttpCode)) || + strings.EqualFold(string(HTTP_3XX), string(healthCheckHttpCode)) || + strings.EqualFold(string(HTTP_4XX), string(healthCheckHttpCode)) || + strings.EqualFold(string(HTTP_5XX), string(healthCheckHttpCode)) { + if "" == code { + code = string(healthCheckHttpCode) + } else { + if strings.Contains(code, string(healthCheckHttpCode)) { + return "", fmt.Errorf("Duplicates healthCheckHttpCode(%v in %v)", healthCheckHttpCode, healthCheckHttpCodes) + } + code += code + "," + string(healthCheckHttpCode) + } + } else { + return "", fmt.Errorf("Invalid healthCheckHttpCode(%v in %v)", healthCheckHttpCode, healthCheckHttpCodes) + } + } + return HealthCheckHttpCodeType(code), nil +} + +type CommonLoadBalancerListenerResponse struct { + common.Response +} + +type HTTPListenerType struct { + LoadBalancerId string + ListenerPort int + BackendServerPort int + Bandwidth int + Scheduler SchedulerType + StickySession FlagType + StickySessionType StickySessionType + CookieTimeout int + Cookie string + HealthCheck FlagType + HealthCheckDomain string + HealthCheckURI string + HealthCheckConnectPort int + HealthyThreshold int + UnhealthyThreshold int + HealthCheckTimeout int + HealthCheckInterval int + HealthCheckHttpCode HealthCheckHttpCodeType + VServerGroupId string + Gzip FlagType +} +type CreateLoadBalancerHTTPListenerArgs HTTPListenerType + +// CreateLoadBalancerHTTPListener create HTTP listener on loadbalancer +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerHTTPListener +func (client *Client) CreateLoadBalancerHTTPListener(args *CreateLoadBalancerHTTPListenerArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("CreateLoadBalancerHTTPListener", args, response) + return err +} + +type HTTPSListenerType struct { + HTTPListenerType + ServerCertificateId string +} + +type CreateLoadBalancerHTTPSListenerArgs HTTPSListenerType + +// CreateLoadBalancerHTTPSListener create HTTPS listener on loadbalancer +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerHTTPSListener +func (client *Client) CreateLoadBalancerHTTPSListener(args *CreateLoadBalancerHTTPSListenerArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("CreateLoadBalancerHTTPSListener", args, response) + return err +} + +type HealthCheckType string + +const ( + TCPHealthCheckType = HealthCheckType("tcp") + HTTPHealthCheckType = HealthCheckType("http") +) + +type TCPListenerType struct { + LoadBalancerId string + ListenerPort int + BackendServerPort int + Bandwidth int + Scheduler SchedulerType + PersistenceTimeout int + HealthCheckType HealthCheckType + HealthCheckDomain string + HealthCheckURI string + HealthCheckConnectPort int + HealthyThreshold int + UnhealthyThreshold int + HealthCheckTimeout int + HealthCheckInterval int + HealthCheckHttpCode HealthCheckHttpCodeType + VServerGroupId string +} + +type CreateLoadBalancerTCPListenerArgs TCPListenerType + +// CreateLoadBalancerTCPListener create TCP listener on loadbalancer +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerTCPListener +func (client *Client) CreateLoadBalancerTCPListener(args *CreateLoadBalancerTCPListenerArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("CreateLoadBalancerTCPListener", args, response) + return err +} + +type UDPListenerType struct { + LoadBalancerId string + ListenerPort int + BackendServerPort int + Bandwidth int + Scheduler SchedulerType + PersistenceTimeout int + HealthCheckConnectPort int + HealthyThreshold int + UnhealthyThreshold int + HealthCheckTimeout int + HealthCheckInterval int + VServerGroupId string +} +type CreateLoadBalancerUDPListenerArgs UDPListenerType + +// CreateLoadBalancerUDPListener create UDP listener on loadbalancer +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerUDPListener +func (client *Client) CreateLoadBalancerUDPListener(args *CreateLoadBalancerUDPListenerArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("CreateLoadBalancerUDPListener", args, response) + return err +} + +type CommonLoadBalancerListenerArgs struct { + LoadBalancerId string + ListenerPort int +} + +// DeleteLoadBalancerListener Delete listener +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DeleteLoadBalancerListener +func (client *Client) DeleteLoadBalancerListener(loadBalancerId string, port int) (err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("DeleteLoadBalancerListener", args, response) + return err +} + +// StartLoadBalancerListener Start listener +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&StartLoadBalancerListener +func (client *Client) StartLoadBalancerListener(loadBalancerId string, port int) (err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("StartLoadBalancerListener", args, response) + return err +} + +// StopLoadBalancerListener Stop listener +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&StopLoadBalancerListener +func (client *Client) StopLoadBalancerListener(loadBalancerId string, port int) (err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("StopLoadBalancerListener", args, response) + return err +} + +type AccessControlStatus string + +const ( + OpenWhileList = AccessControlStatus("open_white_list") + Close = AccessControlStatus("close") +) + +type SetListenerAccessControlStatusArgs struct { + LoadBalancerId string + ListenerPort int + AccessControlStatus AccessControlStatus +} + +// SetListenerAccessControlStatus Set listener access control status +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetListenerAccessControlStatus +func (client *Client) SetListenerAccessControlStatus(loadBalancerId string, port int, status AccessControlStatus) (err error) { + args := &SetListenerAccessControlStatusArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + AccessControlStatus: status, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("SetListenerAccessControlStatus", args, response) + return err +} + +type CommonListenerWhiteListItemArgs struct { + LoadBalancerId string + ListenerPort int + SourceItems string +} + +// AddListenerWhiteListItem Add listener white-list item +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&AddListenerWhiteListItem +func (client *Client) AddListenerWhiteListItem(loadBalancerId string, port int, sourceItems string) (err error) { + args := &CommonListenerWhiteListItemArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + SourceItems: sourceItems, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("AddListenerWhiteListItem", args, response) + return err +} + +// RemoveListenerWhiteListItem Remove listener white-list item +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&RemoveListenerWhiteListItem +func (client *Client) RemoveListenerWhiteListItem(loadBalancerId string, port int, sourceItems string) (err error) { + args := &CommonListenerWhiteListItemArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + SourceItems: sourceItems, + } + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("RemoveListenerWhiteListItem", args, response) + return err +} + +type SetLoadBalancerHTTPListenerAttributeArgs CreateLoadBalancerHTTPListenerArgs + +// SetLoadBalancerHTTPListenerAttribute Set HTTP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerHTTPListenerAttribute +func (client *Client) SetLoadBalancerHTTPListenerAttribute(args *SetLoadBalancerHTTPListenerAttributeArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("SetLoadBalancerHTTPListenerAttribute", args, response) + return err +} + +type SetLoadBalancerHTTPSListenerAttributeArgs CreateLoadBalancerHTTPSListenerArgs + +// SetLoadBalancerHTTPSListenerAttribute Set HTTPS listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerHTTPSListenerAttribute +func (client *Client) SetLoadBalancerHTTPSListenerAttribute(args *SetLoadBalancerHTTPSListenerAttributeArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("SetLoadBalancerHTTPSListenerAttribute", args, response) + return err +} + +type SetLoadBalancerTCPListenerAttributeArgs CreateLoadBalancerTCPListenerArgs + +// SetLoadBalancerTCPListenerAttribute Set TCP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerTCPListenerAttribute +func (client *Client) SetLoadBalancerTCPListenerAttribute(args *SetLoadBalancerTCPListenerAttributeArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("SetLoadBalancerTCPListenerAttribute", args, response) + return err +} + +type SetLoadBalancerUDPListenerAttributeArgs CreateLoadBalancerUDPListenerArgs + +// SetLoadBalancerUDPListenerAttribute Set UDP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerUDPListenerAttribute +func (client *Client) SetLoadBalancerUDPListenerAttribute(args *SetLoadBalancerUDPListenerAttributeArgs) (err error) { + response := &CommonLoadBalancerListenerResponse{} + err = client.Invoke("SetLoadBalancerUDPListenerAttribute", args, response) + return err +} + +type DescribeLoadBalancerListenerAttributeResponse struct { + common.Response + Status ListenerStatus +} + +type DescribeLoadBalancerHTTPListenerAttributeResponse struct { + DescribeLoadBalancerListenerAttributeResponse + HTTPListenerType +} + +// DescribeLoadBalancerHTTPListenerAttribute Describe HTTP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerHTTPListenerAttribute +func (client *Client) DescribeLoadBalancerHTTPListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerHTTPListenerAttributeResponse, err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response = &DescribeLoadBalancerHTTPListenerAttributeResponse{} + err = client.Invoke("DescribeLoadBalancerHTTPListenerAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type DescribeLoadBalancerHTTPSListenerAttributeResponse struct { + DescribeLoadBalancerListenerAttributeResponse + HTTPSListenerType +} + +// DescribeLoadBalancerHTTPSListenerAttribute Describe HTTPS listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerHTTPSListenerAttribute +func (client *Client) DescribeLoadBalancerHTTPSListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerHTTPSListenerAttributeResponse, err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response = &DescribeLoadBalancerHTTPSListenerAttributeResponse{} + err = client.Invoke("DescribeLoadBalancerHTTPSListenerAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type DescribeLoadBalancerTCPListenerAttributeResponse struct { + DescribeLoadBalancerListenerAttributeResponse + TCPListenerType +} + +// DescribeLoadBalancerTCPListenerAttribute Describe TCP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerTCPListenerAttribute +func (client *Client) DescribeLoadBalancerTCPListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerTCPListenerAttributeResponse, err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response = &DescribeLoadBalancerTCPListenerAttributeResponse{} + err = client.Invoke("DescribeLoadBalancerTCPListenerAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type DescribeLoadBalancerUDPListenerAttributeResponse struct { + DescribeLoadBalancerListenerAttributeResponse + UDPListenerType +} + +// DescribeLoadBalancerUDPListenerAttribute Describe UDP listener attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerUDPListenerAttribute +func (client *Client) DescribeLoadBalancerUDPListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerUDPListenerAttributeResponse, err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response = &DescribeLoadBalancerUDPListenerAttributeResponse{} + err = client.Invoke("DescribeLoadBalancerUDPListenerAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type ListenerType string + +const ( + UDP = ListenerType("UDP") + TCP = ListenerType("TCP") + HTTP = ListenerType("HTTP") + HTTPS = ListenerType("HTTPS") +) + +const DefaultWaitForInterval = 5 //5 seconds +const DefaultTimeout = 60 //60 seconds + +// WaitForListener waits for listener to given status +func (client *Client) WaitForListener(loadBalancerId string, port int, listenerType ListenerType) (status ListenerStatus, err error) { + timeout := DefaultTimeout + + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + + method := fmt.Sprintf("DescribeLoadBalancer%sListenerAttribute", listenerType) + response := &DescribeLoadBalancerListenerAttributeResponse{} + + for { + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return response.Status, common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + //Sleep first to ensure the previous request is sent + err = client.Invoke(method, args, response) + if err != nil { + return "", err + } + if response.Status == Running || response.Status == Stopped { + break + } + } + return response.Status, nil +} + +type DescribeListenerAccessControlAttributeResponse struct { + common.Response + AccessControlStatus AccessControlStatus + SourceItems string +} + +// DescribeListenerAccessControlAttribute Describe listener access control attribute +// +// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeListenerAccessControlAttribute +func (client *Client) DescribeListenerAccessControlAttribute(loadBalancerId string, port int) (response *DescribeListenerAccessControlAttributeResponse, err error) { + args := &CommonLoadBalancerListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: port, + } + response = &DescribeListenerAccessControlAttributeResponse{} + err = client.Invoke("DescribeListenerAccessControlAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go b/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go new file mode 100644 index 0000000000..d090570cee --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go @@ -0,0 +1,238 @@ +package slb + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type AddressType string + +const ( + InternetAddressType = AddressType("internet") + IntranetAddressType = AddressType("intranet") +) + +type InternetChargeType string + +const ( + PayByBandwidth = InternetChargeType("paybybandwidth") + PayByTraffic = InternetChargeType("paybytraffic") +) + +type CreateLoadBalancerArgs struct { + RegionId common.Region + LoadBalancerName string + AddressType AddressType + VSwitchId string + InternetChargeType InternetChargeType + Bandwidth int + ClientToken string +} + +type CreateLoadBalancerResponse struct { + common.Response + LoadBalancerId string + Address string + NetworkType string + VpcId string + VSwitchId string + LoadBalancerName string +} + +// CreateLoadBalancer create loadbalancer +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&CreateLoadBalancer +func (client *Client) CreateLoadBalancer(args *CreateLoadBalancerArgs) (response *CreateLoadBalancerResponse, err error) { + response = &CreateLoadBalancerResponse{} + err = client.Invoke("CreateLoadBalancer", args, response) + if err != nil { + return nil, err + } + return response, err +} + +type DeleteLoadBalancerArgs struct { + LoadBalancerId string +} + +type DeleteLoadBalancerResponse struct { + common.Response +} + +// DeleteLoadBalancer delete loadbalancer +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DeleteLoadBalancer +func (client *Client) DeleteLoadBalancer(loadBalancerId string) (err error) { + args := &DeleteLoadBalancerArgs{ + LoadBalancerId: loadBalancerId, + } + response := &DeleteLoadBalancerResponse{} + err = client.Invoke("DeleteLoadBalancer", args, response) + return err +} + +type ModifyLoadBalancerInternetSpecArgs struct { + LoadBalancerId string + InternetChargeType InternetChargeType + Bandwidth int +} + +type ModifyLoadBalancerInternetSpecResponse struct { + common.Response +} + +// ModifyLoadBalancerInternetSpec Modify loadbalancer internet spec +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&ModifyLoadBalancerInternetSpec + +func (client *Client) ModifyLoadBalancerInternetSpec(args *ModifyLoadBalancerInternetSpecArgs) (err error) { + response := &ModifyLoadBalancerInternetSpecResponse{} + err = client.Invoke("ModifyLoadBalancerInternetSpec", args, response) + return err +} + +type Status string + +const InactiveStatus = Status("inactive") +const ActiveStatus = Status("active") +const LockedStatus = Status("locked") + +type SetLoadBalancerStatusArgs struct { + LoadBalancerId string + LoadBalancerStatus Status +} + +type SetLoadBalancerStatusResponse struct { + common.Response +} + +// SetLoadBalancerStatus Set loadbalancer status +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&SetLoadBalancerStatus + +func (client *Client) SetLoadBalancerStatus(loadBalancerId string, status Status) (err error) { + args := &SetLoadBalancerStatusArgs{ + LoadBalancerId: loadBalancerId, + LoadBalancerStatus: status, + } + response := &SetLoadBalancerStatusResponse{} + err = client.Invoke("SetLoadBalancerStatus", args, response) + return err +} + +type SetLoadBalancerNameArgs struct { + LoadBalancerId string + LoadBalancerName string +} + +type SetLoadBalancerNameResponse struct { + common.Response +} + +// SetLoadBalancerName Set loadbalancer name +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&SetLoadBalancerName + +func (client *Client) SetLoadBalancerName(loadBalancerId string, name string) (err error) { + args := &SetLoadBalancerNameArgs{ + LoadBalancerId: loadBalancerId, + LoadBalancerName: name, + } + response := &SetLoadBalancerNameResponse{} + err = client.Invoke("SetLoadBalancerName", args, response) + return err +} + +type DescribeLoadBalancersArgs struct { + RegionId common.Region + LoadBalancerId string + LoadBalancerName string + AddressType AddressType + NetworkType string + VpcId string + VSwitchId string + Address string + InternetChargeType InternetChargeType + ServerId string +} + +type ListenerPortAndProtocolType struct { + ListenerPort int + ListenerProtocol string +} + +type BackendServerType struct { + ServerId string + Weight int +} + +type LoadBalancerType struct { + LoadBalancerId string + LoadBalancerName string + LoadBalancerStatus string + Address string + RegionId common.Region + RegionIdAlias string + AddressType AddressType + VSwitchId string + VpcId string + NetworkType string + Bandwidth int + InternetChargeType InternetChargeType + CreateTime string //Why not ISO 6801 + CreateTimeStamp util.ISO6801Time + ListenerPorts struct { + ListenerPort []int + } + ListenerPortsAndProtocol struct { + ListenerPortAndProtocol []ListenerPortAndProtocolType + } + BackendServers struct { + BackendServer []BackendServerType + } +} + +type DescribeLoadBalancersResponse struct { + common.Response + LoadBalancers struct { + LoadBalancer []LoadBalancerType + } +} + +// DescribeLoadBalancers Describe loadbalancers +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DescribeLoadBalancers + +func (client *Client) DescribeLoadBalancers(args *DescribeLoadBalancersArgs) (loadBalancers []LoadBalancerType, err error) { + response := &DescribeLoadBalancersResponse{} + err = client.Invoke("DescribeLoadBalancers", args, response) + if err != nil { + return nil, err + } + return response.LoadBalancers.LoadBalancer, err +} + +type DescribeLoadBalancerAttributeArgs struct { + LoadBalancerId string +} + +type DescribeLoadBalancerAttributeResponse struct { + common.Response + LoadBalancerType +} + +// DescribeLoadBalancerAttribute Describe loadbalancer attribute +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DescribeLoadBalancerAttribute + +func (client *Client) DescribeLoadBalancerAttribute(loadBalancerId string) (loadBalancer *LoadBalancerType, err error) { + args := &DescribeLoadBalancersArgs{ + LoadBalancerId: loadBalancerId, + } + response := &DescribeLoadBalancerAttributeResponse{} + err = client.Invoke("DescribeLoadBalancerAttribute", args, response) + if err != nil { + return nil, err + } + return &response.LoadBalancerType, err +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/regions.go b/vendor/github.com/denverdino/aliyungo/slb/regions.go new file mode 100644 index 0000000000..870ffefe7d --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/regions.go @@ -0,0 +1,34 @@ +package slb + +import "github.com/denverdino/aliyungo/common" + +type DescribeRegionsArgs struct { +} + +// +// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype®iontype +type RegionType struct { + RegionId common.Region + LocalName string +} + +type DescribeRegionsResponse struct { + common.Response + Regions struct { + Region []RegionType + } +} + +// DescribeRegions describes regions +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DescribeRegions +func (client *Client) DescribeRegions() (regions []RegionType, err error) { + response := DescribeRegionsResponse{} + + err = client.Invoke("DescribeRegions", &DescribeRegionsArgs{}, &response) + + if err != nil { + return []RegionType{}, err + } + return response.Regions.Region, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/servers.go b/vendor/github.com/denverdino/aliyungo/slb/servers.go new file mode 100644 index 0000000000..a3fb2a406c --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/servers.go @@ -0,0 +1,122 @@ +package slb + +import ( + "encoding/json" + + "github.com/denverdino/aliyungo/common" +) + +type AddBackendServersArgs struct { + LoadBalancerId string + BackendServers string +} + +type SetBackendServersArgs AddBackendServersArgs + +type AddBackendServersResponse struct { + common.Response + LoadBalancerId string + BackendServers struct { + BackendServer []BackendServerType + } +} + +type SetBackendServersResponse AddBackendServersResponse + + +// SetBackendServers set weight of backend servers + +func (client *Client) SetBackendServers(loadBalancerId string, backendServers []BackendServerType) (result []BackendServerType, err error) { + bytes, _ := json.Marshal(backendServers) + + args := &SetBackendServersArgs{ + LoadBalancerId: loadBalancerId, + BackendServers: string(bytes), + } + response := &SetBackendServersResponse{} + + err = client.Invoke("SetBackendServers", args, response) + if err != nil { + return nil, err + } + return response.BackendServers.BackendServer, err +} + + +// AddBackendServers Add backend servers +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&AddBackendServers +func (client *Client) AddBackendServers(loadBalancerId string, backendServers []BackendServerType) (result []BackendServerType, err error) { + + bytes, _ := json.Marshal(backendServers) + + args := &AddBackendServersArgs{ + LoadBalancerId: loadBalancerId, + BackendServers: string(bytes), + } + response := &AddBackendServersResponse{} + + err = client.Invoke("AddBackendServers", args, response) + if err != nil { + return nil, err + } + return response.BackendServers.BackendServer, err +} + +type RemoveBackendServersArgs struct { + LoadBalancerId string + BackendServers []string +} + +type RemoveBackendServersResponse struct { + common.Response + LoadBalancerId string + BackendServers struct { + BackendServer []BackendServerType + } +} + +// RemoveBackendServers Remove backend servers +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&RemoveBackendServers +func (client *Client) RemoveBackendServers(loadBalancerId string, backendServers []string) (result []BackendServerType, err error) { + args := &RemoveBackendServersArgs{ + LoadBalancerId: loadBalancerId, + BackendServers: backendServers, + } + response := &RemoveBackendServersResponse{} + err = client.Invoke("RemoveBackendServers", args, response) + if err != nil { + return nil, err + } + return response.BackendServers.BackendServer, err +} + +type HealthStatusType struct { + ServerId string + ServerHealthStatus string +} + +type DescribeHealthStatusArgs struct { + LoadBalancerId string + ListenerPort int +} + +type DescribeHealthStatusResponse struct { + common.Response + BackendServers struct { + BackendServer []HealthStatusType + } +} + +// DescribeHealthStatus Describe health status +// +// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&DescribeHealthStatus +func (client *Client) DescribeHealthStatus(args *DescribeHealthStatusArgs) (response *DescribeHealthStatusResponse, err error) { + response = &DescribeHealthStatusResponse{} + err = client.Invoke("DescribeHealthStatus", args, response) + if err != nil { + return nil, err + } + return response, err +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/tags.go b/vendor/github.com/denverdino/aliyungo/slb/tags.go new file mode 100644 index 0000000000..4128559d4a --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/tags.go @@ -0,0 +1,85 @@ +package slb + +import "github.com/denverdino/aliyungo/common" + +type TagItem struct { + TagKey string + TagValue string +} + +type AddTagsArgs struct { + RegionId common.Region + LoadBalancerID string + Tags string +} + +type AddTagsResponse struct { + common.Response +} + +// AddTags Add tags to resource +// +// You can read doc at https://help.aliyun.com/document_detail/42871.html +func (client *Client) AddTags(args *AddTagsArgs) error { + response := AddTagsResponse{} + err := client.Invoke("AddTags", args, &response) + if err != nil { + return err + } + return err +} + +type RemoveTagsArgs struct { + RegionId common.Region + LoadBalancerID string + Tags string +} + +type RemoveTagsResponse struct { + common.Response +} + +// RemoveTags remove tags to resource +// +// You can read doc at https://help.aliyun.com/document_detail/42872.html +func (client *Client) RemoveTags(args *RemoveTagsArgs) error { + response := RemoveTagsResponse{} + err := client.Invoke("RemoveTags", args, &response) + if err != nil { + return err + } + return err +} + +type TagItemType struct { + TagItem + InstanceCount int +} + +type DescribeTagsArgs struct { + RegionId common.Region + LoadBalancerID string + Tags string + common.Pagination +} + +type DescribeTagsResponse struct { + common.Response + common.PaginationResult + TagSets struct { + TagSet []TagItemType + } +} + +// DescribeResourceByTags describe resource by tags +// +// You can read doc at https://help.aliyun.com/document_detail/42873.html?spm=5176.doc42872.6.267.CP1iWu +func (client *Client) DescribeTags(args *DescribeTagsArgs) (tags []TagItemType, pagination *common.PaginationResult, err error) { + args.Validate() + response := DescribeTagsResponse{} + err = client.Invoke("DescribeTags", args, &response) + if err != nil { + return nil, nil, err + } + return response.TagSets.TagSet, &response.PaginationResult, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/vserver_group.go b/vendor/github.com/denverdino/aliyungo/slb/vserver_group.go new file mode 100644 index 0000000000..1fa83d09b1 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/vserver_group.go @@ -0,0 +1,159 @@ +package slb + +import ( + "github.com/denverdino/aliyungo/common" +) + +type VBackendServerType struct { + ServerId string + Weight int + Port int +} + +type VServerGroup struct { + VServerGroupName string + VServerGroupId string +} + +type VBackendServers struct { + BackendServer []VBackendServerType +} + +type CreateVServerGroupArgs struct { + LoadBalancerId string + RegionId common.Region + VServerGroupName string + VServerGroupId string + BackendServers string +} + +type SetVServerGroupAttributeArgs struct { + LoadBalancerId string + RegionId common.Region + VServerGroupName string + VServerGroupId string + BackendServers string +} + +type AddVServerGroupBackendServersArgs CreateVServerGroupArgs +type RemoveVServerGroupBackendServersArgs CreateVServerGroupArgs +type ModifyVServerGroupBackendServersArgs struct { + VServerGroupId string + RegionId common.Region + OldBackendServers string + NewBackendServers string +} + +type DeleteVServerGroupArgs struct { + VServerGroupId string + RegionId common.Region +} + +type DescribeVServerGroupsArgs struct { + LoadBalancerId string + RegionId common.Region +} + +type DescribeVServerGroupAttributeArgs struct { + VServerGroupId string + RegionId common.Region +} + +type CreateVServerGroupResponse struct { + common.Response + VServerGroupId string + VServerGroupName string + BackendServers VBackendServers +} + +type SetVServerGroupAttributeResponse struct { + common.Response + VServerGroupId string + VServerGroupName string + BackendServers VBackendServers +} + +type AddVServerGroupBackendServersResponse CreateVServerGroupResponse +type RemoveVServerGroupBackendServersResponse CreateVServerGroupResponse +type ModifyVServerGroupBackendServersResponse CreateVServerGroupResponse +type DeleteVServerGroupResponse struct{ common.Response } +type DescribeVServerGroupsResponse struct { + common.Response + VServerGroups struct { + VServerGroup []VServerGroup + } +} +type DescribeVServerGroupAttributeResponse CreateVServerGroupResponse + + +func (client *Client) CreateVServerGroup(args *CreateVServerGroupArgs) (response *CreateVServerGroupResponse, err error) { + response = &CreateVServerGroupResponse{} + err = client.Invoke("CreateVServerGroup", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) SetVServerGroupAttribute(args *SetVServerGroupAttributeArgs) (response *SetVServerGroupAttributeResponse, err error) { + response = &SetVServerGroupAttributeResponse{} + err = client.Invoke("SetVServerGroupAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) AddVServerGroupBackendServers(args *AddVServerGroupBackendServersArgs) (response *AddVServerGroupBackendServersResponse, err error) { + response = &AddVServerGroupBackendServersResponse{} + err = client.Invoke("AddVServerGroupBackendServers", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) RemoveVServerGroupBackendServers(args *RemoveVServerGroupBackendServersArgs) (response *RemoveVServerGroupBackendServersResponse, err error) { + response = &RemoveVServerGroupBackendServersResponse{} + err = client.Invoke("RemoveVServerGroupBackendServers", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) ModifyVServerGroupBackendServers(args *ModifyVServerGroupBackendServersArgs) (response *ModifyVServerGroupBackendServersResponse, err error) { + response = &ModifyVServerGroupBackendServersResponse{} + err = client.Invoke("ModifyVServerGroupBackendServers", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) DeleteVServerGroup(args *DeleteVServerGroupArgs) (response *DeleteVServerGroupResponse, err error) { + response = &DeleteVServerGroupResponse{} + err = client.Invoke("DeleteVServerGroup", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) DescribeVServerGroups(args *DescribeVServerGroupsArgs) (response *DescribeVServerGroupsResponse, err error) { + response = &DescribeVServerGroupsResponse{} + err = client.Invoke("DescribeVServerGroups", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func (client *Client) DescribeVServerGroupAttribute(args *DescribeVServerGroupAttributeArgs) (response *DescribeVServerGroupAttributeResponse, err error) { + response = &DescribeVServerGroupAttributeResponse{} + err = client.Invoke("DescribeVServerGroupAttribute", args, response) + if err != nil { + return nil, err + } + return response, err +} diff --git a/vendor/github.com/denverdino/aliyungo/util/attempt.go b/vendor/github.com/denverdino/aliyungo/util/attempt.go new file mode 100644 index 0000000000..2d07f03a83 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/util/attempt.go @@ -0,0 +1,76 @@ +package util + +import ( + "time" +) + +// AttemptStrategy is reused from the goamz package + +// AttemptStrategy represents a strategy for waiting for an action +// to complete successfully. This is an internal type used by the +// implementation of other packages. +type AttemptStrategy struct { + Total time.Duration // total duration of attempt. + Delay time.Duration // interval between each try in the burst. + Min int // minimum number of retries; overrides Total +} + +type Attempt struct { + strategy AttemptStrategy + last time.Time + end time.Time + force bool + count int +} + +// Start begins a new sequence of attempts for the given strategy. +func (s AttemptStrategy) Start() *Attempt { + now := time.Now() + return &Attempt{ + strategy: s, + last: now, + end: now.Add(s.Total), + force: true, + } +} + +// Next waits until it is time to perform the next attempt or returns +// false if it is time to stop trying. +func (a *Attempt) Next() bool { + now := time.Now() + sleep := a.nextSleep(now) + if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count { + return false + } + a.force = false + if sleep > 0 && a.count > 0 { + time.Sleep(sleep) + now = time.Now() + } + a.count++ + a.last = now + return true +} + +func (a *Attempt) nextSleep(now time.Time) time.Duration { + sleep := a.strategy.Delay - now.Sub(a.last) + if sleep < 0 { + return 0 + } + return sleep +} + +// HasNext returns whether another attempt will be made if the current +// one fails. If it returns true, the following call to Next is +// guaranteed to return true. +func (a *Attempt) HasNext() bool { + if a.force || a.strategy.Min > a.count { + return true + } + now := time.Now() + if now.Add(a.nextSleep(now)).Before(a.end) { + a.force = true + return true + } + return false +} diff --git a/vendor/github.com/denverdino/aliyungo/util/encoding.go b/vendor/github.com/denverdino/aliyungo/util/encoding.go new file mode 100644 index 0000000000..e545e069d4 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/util/encoding.go @@ -0,0 +1,152 @@ +package util + +import ( + "encoding/json" + "fmt" + "log" + "net/url" + "reflect" + "strconv" + "time" +) + +//ConvertToQueryValues converts the struct to url.Values +func ConvertToQueryValues(ifc interface{}) url.Values { + values := url.Values{} + SetQueryValues(ifc, &values) + return values +} + +//SetQueryValues sets the struct to existing url.Values following ECS encoding rules +func SetQueryValues(ifc interface{}, values *url.Values) { + setQueryValues(ifc, values, "") +} + +func setQueryValues(i interface{}, values *url.Values, prefix string) { + // add to support url.Values + mapValues, ok := i.(url.Values) + if ok { + for k, _ := range mapValues { + values.Set(k, mapValues.Get(k)) + } + return + } + + elem := reflect.ValueOf(i) + if elem.Kind() == reflect.Ptr { + elem = elem.Elem() + } + elemType := elem.Type() + for i := 0; i < elem.NumField(); i++ { + + fieldName := elemType.Field(i).Name + anonymous := elemType.Field(i).Anonymous + field := elem.Field(i) + // TODO Use Tag for validation + // tag := typ.Field(i).Tag.Get("tagname") + kind := field.Kind() + if (kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan) && field.IsNil() { + continue + } + if kind == reflect.Ptr { + field = field.Elem() + kind = field.Kind() + } + var value string + //switch field.Interface().(type) { + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i := field.Int() + if i != 0 { + value = strconv.FormatInt(i, 10) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + i := field.Uint() + if i != 0 { + value = strconv.FormatUint(i, 10) + } + case reflect.Float32: + value = strconv.FormatFloat(field.Float(), 'f', 4, 32) + case reflect.Float64: + value = strconv.FormatFloat(field.Float(), 'f', 4, 64) + case reflect.Bool: + value = strconv.FormatBool(field.Bool()) + case reflect.String: + value = field.String() + case reflect.Map: + ifc := field.Interface() + m := ifc.(map[string]string) + if m != nil { + j := 0 + for k, v := range m { + j++ + keyName := fmt.Sprintf("%s.%d.Key", fieldName, j) + values.Set(keyName, k) + valueName := fmt.Sprintf("%s.%d.Value", fieldName, j) + values.Set(valueName, v) + } + } + case reflect.Slice: + switch field.Type().Elem().Kind() { + case reflect.Uint8: + value = string(field.Bytes()) + case reflect.String: + l := field.Len() + if l > 0 { + strArray := make([]string, l) + for i := 0; i < l; i++ { + strArray[i] = field.Index(i).String() + } + bytes, err := json.Marshal(strArray) + if err == nil { + value = string(bytes) + } else { + log.Printf("Failed to convert JSON: %v", err) + } + } + default: + l := field.Len() + for j := 0; j < l; j++ { + prefixName := fmt.Sprintf("%s.%d.", fieldName, (j + 1)) + ifc := field.Index(j).Interface() + //log.Printf("%s : %v", prefixName, ifc) + if ifc != nil { + setQueryValues(ifc, values, prefixName) + } + } + continue + } + + default: + switch field.Interface().(type) { + case ISO6801Time: + t := field.Interface().(ISO6801Time) + value = t.String() + case time.Time: + t := field.Interface().(time.Time) + value = GetISO8601TimeStamp(t) + default: + ifc := field.Interface() + if ifc != nil { + if anonymous { + SetQueryValues(ifc, values) + } else { + prefixName := fieldName + "." + setQueryValues(ifc, values, prefixName) + } + continue + } + } + } + if value != "" { + name := elemType.Field(i).Tag.Get("ArgName") + if name == "" { + name = fieldName + } + if prefix != "" { + name = prefix + name + } + values.Set(name, value) + } + } +} diff --git a/vendor/github.com/denverdino/aliyungo/util/iso6801.go b/vendor/github.com/denverdino/aliyungo/util/iso6801.go new file mode 100644 index 0000000000..9c25e8f688 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/util/iso6801.go @@ -0,0 +1,80 @@ +package util + +import ( + "fmt" + "strconv" + "time" +) + +// GetISO8601TimeStamp gets timestamp string in ISO8601 format +func GetISO8601TimeStamp(ts time.Time) string { + t := ts.UTC() + return fmt.Sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()) +} + +const formatISO8601 = "2006-01-02T15:04:05Z" +const jsonFormatISO8601 = `"` + formatISO8601 + `"` +const formatISO8601withoutSeconds = "2006-01-02T15:04Z" +const jsonFormatISO8601withoutSeconds = `"` + formatISO8601withoutSeconds + `"` + +// A ISO6801Time represents a time in ISO8601 format +type ISO6801Time time.Time + +// New constructs a new iso8601.Time instance from an existing +// time.Time instance. This causes the nanosecond field to be set to +// 0, and its time zone set to a fixed zone with no offset from UTC +// (but it is *not* UTC itself). +func NewISO6801Time(t time.Time) ISO6801Time { + return ISO6801Time(time.Date( + t.Year(), + t.Month(), + t.Day(), + t.Hour(), + t.Minute(), + t.Second(), + 0, + time.UTC, + )) +} + +// IsDefault checks if the time is default +func (it *ISO6801Time) IsDefault() bool { + return *it == ISO6801Time{} +} + +// MarshalJSON serializes the ISO6801Time into JSON string +func (it ISO6801Time) MarshalJSON() ([]byte, error) { + return []byte(time.Time(it).Format(jsonFormatISO8601)), nil +} + +// UnmarshalJSON deserializes the ISO6801Time from JSON string +func (it *ISO6801Time) UnmarshalJSON(data []byte) error { + str := string(data) + + if str == "\"\"" || len(data) == 0 { + return nil + } + var t time.Time + var err error + if str[0] == '"' { + t, err = time.ParseInLocation(jsonFormatISO8601, str, time.UTC) + if err != nil { + t, err = time.ParseInLocation(jsonFormatISO8601withoutSeconds, str, time.UTC) + } + } else { + var i int64 + i, err = strconv.ParseInt(str, 10, 64) + if err == nil { + t = time.Unix(i/1000, i%1000) + } + } + if err == nil { + *it = ISO6801Time(t) + } + return err +} + +// String returns the time in ISO6801Time format +func (it ISO6801Time) String() string { + return time.Time(it).Format(formatISO8601) +} diff --git a/vendor/github.com/denverdino/aliyungo/util/signature.go b/vendor/github.com/denverdino/aliyungo/util/signature.go new file mode 100644 index 0000000000..a00b27c19b --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/util/signature.go @@ -0,0 +1,40 @@ +package util + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "net/url" + "strings" +) + +//CreateSignature creates signature for string following Aliyun rules +func CreateSignature(stringToSignature, accessKeySecret string) string { + // Crypto by HMAC-SHA1 + hmacSha1 := hmac.New(sha1.New, []byte(accessKeySecret)) + hmacSha1.Write([]byte(stringToSignature)) + sign := hmacSha1.Sum(nil) + + // Encode to Base64 + base64Sign := base64.StdEncoding.EncodeToString(sign) + + return base64Sign +} + +func percentReplace(str string) string { + str = strings.Replace(str, "+", "%20", -1) + str = strings.Replace(str, "*", "%2A", -1) + str = strings.Replace(str, "%7E", "~", -1) + + return str +} + +// CreateSignatureForRequest creates signature for query string values +func CreateSignatureForRequest(method string, values *url.Values, accessKeySecret string) string { + + canonicalizedQueryString := percentReplace(values.Encode()) + + stringToSign := method + "&%2F&" + url.QueryEscape(canonicalizedQueryString) + + return CreateSignature(stringToSign, accessKeySecret) +} diff --git a/vendor/github.com/denverdino/aliyungo/util/util.go b/vendor/github.com/denverdino/aliyungo/util/util.go new file mode 100644 index 0000000000..dd68214e3b --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/util/util.go @@ -0,0 +1,147 @@ +package util + +import ( + "bytes" + srand "crypto/rand" + "encoding/binary" + "math/rand" + "net/http" + "net/url" + "sort" + "time" +) + +const dictionary = "_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + +//CreateRandomString create random string +func CreateRandomString() string { + b := make([]byte, 32) + l := len(dictionary) + + _, err := srand.Read(b) + + if err != nil { + // fail back to insecure rand + rand.Seed(time.Now().UnixNano()) + for i := range b { + b[i] = dictionary[rand.Int()%l] + } + } else { + for i, v := range b { + b[i] = dictionary[v%byte(l)] + } + } + + return string(b) +} + +// Encode encodes the values into ``URL encoded'' form +// ("acl&bar=baz&foo=quux") sorted by key. +func Encode(v url.Values) string { + if v == nil { + return "" + } + var buf bytes.Buffer + keys := make([]string, 0, len(v)) + for k := range v { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + vs := v[k] + prefix := url.QueryEscape(k) + for _, v := range vs { + if buf.Len() > 0 { + buf.WriteByte('&') + } + buf.WriteString(prefix) + if v != "" { + buf.WriteString("=") + buf.WriteString(url.QueryEscape(v)) + } + } + } + return buf.String() +} + +func GetGMTime() string { + return time.Now().UTC().Format(http.TimeFormat) +} + +// + +func randUint32() uint32 { + return randUint32Slice(1)[0] +} + +func randUint32Slice(c int) []uint32 { + b := make([]byte, c*4) + + _, err := srand.Read(b) + + if err != nil { + // fail back to insecure rand + rand.Seed(time.Now().UnixNano()) + for i := range b { + b[i] = byte(rand.Int()) + } + } + + n := make([]uint32, c) + + for i := range n { + n[i] = binary.BigEndian.Uint32(b[i*4 : i*4+4]) + } + + return n +} + +func toByte(n uint32, st, ed byte) byte { + return byte(n%uint32(ed-st+1) + uint32(st)) +} + +func toDigit(n uint32) byte { + return toByte(n, '0', '9') +} + +func toLowerLetter(n uint32) byte { + return toByte(n, 'a', 'z') +} + +func toUpperLetter(n uint32) byte { + return toByte(n, 'A', 'Z') +} + +type convFunc func(uint32) byte + +var convFuncs = []convFunc{toDigit, toLowerLetter, toUpperLetter} + +// tools for generating a random ECS instance password +// from 8 to 30 char MUST contain digit upper, case letter and upper case letter +// http://docs.aliyun.com/#/pub/ecs/open-api/instance&createinstance +func GenerateRandomECSPassword() string { + + // [8, 30] + l := int(randUint32()%23 + 8) + + n := randUint32Slice(l) + + b := make([]byte, l) + + b[0] = toDigit(n[0]) + b[1] = toLowerLetter(n[1]) + b[2] = toUpperLetter(n[2]) + + for i := 3; i < l; i++ { + b[i] = convFuncs[n[i]%3](n[i]) + } + + s := make([]byte, l) + perm := rand.Perm(l) + for i, v := range perm { + s[v] = b[i] + } + + return string(s) + +} diff --git a/vendor/vendor.json b/vendor/vendor.json index dc1fb36f80..09c18afa77 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1121,6 +1121,30 @@ "path": "github.com/davecgh/go-spew/spew", "revision": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d" }, + { + "checksumSHA1": "ADySw3nBHyzEHB6afBSeVRN2A4g=", + "path": "github.com/denverdino/aliyungo/common", + "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", + "revisionTime": "2017-01-17T10:57:15Z" + }, + { + "checksumSHA1": "9ZY3RlumKp5DAMfL08YwMoOOT2o=", + "path": "github.com/denverdino/aliyungo/ecs", + "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", + "revisionTime": "2017-01-17T10:57:15Z" + }, + { + "checksumSHA1": "QlA7zv05k7HWeR3tg4uHqIlFcg8=", + "path": "github.com/denverdino/aliyungo/slb", + "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", + "revisionTime": "2017-01-17T10:57:15Z" + }, + { + "checksumSHA1": "Lp0KtT7ycgq31ox3Uzhpxyw0U+Y=", + "path": "github.com/denverdino/aliyungo/util", + "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", + "revisionTime": "2017-01-17T10:57:15Z" + }, { "checksumSHA1": "yDQQpeUxwqB3C+4opweg6znWJQk=", "comment": "v2.4.0-11-gf219341", diff --git a/website/source/docs/providers/alicloud/d/images.html.markdown b/website/source/docs/providers/alicloud/d/images.html.markdown new file mode 100644 index 0000000000..5c1d743ede --- /dev/null +++ b/website/source/docs/providers/alicloud/d/images.html.markdown @@ -0,0 +1,50 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_images" +sidebar_current: "docs-alicloud-datasource-images" +description: |- + Provides a list of images available to the user. +--- + +# alicloud\_images + +The Images data source list image resource list contains private images of the user and images of system resources provided by Alicloud, as well as other public images and those available on the image market. + +## Example Usage + +``` +data "alicloud_images" "multi_image" { + owners = "system" + name_regex = "^centos_6" +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `name_regex` - (Optional) A regex string to apply to the image list returned by Alicloud. +* `most_recent` - (Optional) If more than one result is returned, use the most recent image. +* `owners` - (Optional) Limit search to specific image owners. Valid items are `system`, `self`, `others`, `marketplace`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - ID of the image. +* `architecture` - Platform type of the image system:i386 | x86_64. +* `creation_time` - Time of creation. +* `description` - Description of the image. +* `image_owner_alias` - Alias of the image owner. +* `os_name` - Display name of the OS. +* `status` - Status of the image, with possible values: `UnAvailable`, `Available`, `Creating` or `CreateFailed`. +* `size` - Size of the image. +* `disk_device_mappings` - Description of the system with disks and snapshots under an image. + * `device` - Device information of the created disk: such as /dev/xvdb. + * `size` - Size of the created disk. + * `snapshot_id` - Snapshot ID. +* `product_code` - Product code of the image on the image market. +* `is_subscribed` - Whether the user has subscribed to the terms of service for the image product corresponding to the ProductCode. +* `image_version` - Version of the image. +* `progress` - Progress of image creation, presented in percentages. diff --git a/website/source/docs/providers/alicloud/d/instance_types.html.markdown b/website/source/docs/providers/alicloud/d/instance_types.html.markdown new file mode 100644 index 0000000000..1505991533 --- /dev/null +++ b/website/source/docs/providers/alicloud/d/instance_types.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_instance_types" +sidebar_current: "docs-alicloud-datasource-instance-types" +description: |- + Provides a list of Ecs Instance Types for use in alicloud_instance resource. +--- + +# alicloud\_instance\_types + +The Instance Types data source list the ecs_instance_types of Alicloud. + +## Example Usage + +``` +# Declare the data source +data "alicloud_instance_types" "1c2g" { + cpu_core_count = 1 + memory_size = 2 +} + +# Create ecs instance with the first matched instance_type + +resource "alicloud_instance" "instance" { + instance_type = "${data.alicloud_instance_types.1c2g.instance_types.0.id}" + + # Other properties... +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `cpu_core_count` - (Optional) Limit search to specific cpu core count. +* `memory_size` - (Optional) Limit search to specific memory size. +* `instance_type_family` - (Optional) Allows to filter list of Instance Types based on their +family name, for example 'ecs.n1'. + +## Attributes Reference + +The following attributes are exported: + +* `id` - ID of the instance type. +* `cpu_core_count` - Number of CPU cores. +* `memory_size` - Size of memory, measured in GB. +* `family` - The instance type family. diff --git a/website/source/docs/providers/alicloud/d/regions.html.markdown b/website/source/docs/providers/alicloud/d/regions.html.markdown new file mode 100644 index 0000000000..2999718fd6 --- /dev/null +++ b/website/source/docs/providers/alicloud/d/regions.html.markdown @@ -0,0 +1,34 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_regions" +sidebar_current: "docs-alicloud-datasource-regions" +description: |- + Provides a list of Availability Regions which can be used by an Alicloud account. +--- + +# alicloud\_regions + +The Regions data source allows access to the list of Alicloud Regions. + +## Example Usage + +``` +data "alicloud_regions" "current" { + current = true +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Optional) The full name of the region to select. +* `current` - (Optional) Set to true to match only the region configured in the provider. + +## Attributes Reference + +The following attributes are exported: + +* `id` - ID of the region. +* `local_name` - Name of the region in the local language. diff --git a/website/source/docs/providers/alicloud/d/zones.html.markdown b/website/source/docs/providers/alicloud/d/zones.html.markdown new file mode 100644 index 0000000000..cdab9fa2aa --- /dev/null +++ b/website/source/docs/providers/alicloud/d/zones.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_zones" +sidebar_current: "docs-alicloud-datasource-zones" +description: |- + Provides a list of Availability Zones which can be used by an Alicloud account. +--- + +# alicloud\_zones + +The Zones data source allows access to the list of Alicloud Zones which can be accessed by an Alicloud account within the region configured in the provider. + +## Example Usage + +``` +# Declare the data source +data "alicloud_zones" "default" { + "available_instance_type"= "ecs.s2.large" + "available_disk_category"= "cloud_ssd" +} + +# Create ecs instance with the first matched zone + +resource "alicloud_instance" "instance" { + availability_zone = "${data.alicloud_zones.default.zones.0.id}" + + # Other properties... +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `available_instance_type` - (Optional) Limit search to specific instance type. +* `available_resource_creation` - (Optional) Limit search to specific resource type. The following values are allowed `Instance`, `Disk` and `VSwitch`. +* `available_disk_category` - (Optional) Limit search to specific disk category. Can be either `cloud`, `ephemeral`, or `ephemeral_ssd`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - ID of the zone. +* `local_name` - Name of the zone in the local language. +* `available_instance_types` - Instance types allowed. +* `available_resource_creation` - Type of resource that can be created. +* `available_disk_categories` - Set of supported disk categories. diff --git a/website/source/docs/providers/alicloud/index.html.markdown b/website/source/docs/providers/alicloud/index.html.markdown new file mode 100644 index 0000000000..ee8c82a3da --- /dev/null +++ b/website/source/docs/providers/alicloud/index.html.markdown @@ -0,0 +1,113 @@ +--- +layout: "alicloud" +page_title: "Provider: alicloud" +sidebar_current: "docs-alicloud-index" +description: |- + The Alicloud provider is used to interact with many resources supported by Alicloud. The provider needs to be configured with the proper credentials before it can be used. +--- + +# Alicloud Provider + +The Alicloud provider is used to interact with the +many resources supported by [Alicloud](https://www.aliyun.com). The provider needs to be configured +with the proper credentials before it can be used. + +Use the navigation to the left to read about the available resources. + +## Example Usage + +``` +# Configure the Alicloud Provider +provider "alicloud" { + access_key = "${var.access_key}" + secret_key = "${var.secret_key}" + region = "${var.region}" +} + +# Create a web server +resource "alicloud_instance" "web" { + # cn-beijing + provider = "alicloud" + availability_zone = "cn-beijing-b" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + instance_network_type = "Classic" + internet_charge_type = "PayByBandwidth" + + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + security_groups = ["${alicloud_security_group.default.id}"] + instance_name = "web" +} + +# Create security group +resource "alicloud_security_group" "default" { + name = "default" + provider = "alicloud" + description = "default" +} +``` + +## Authentication + +The Alicloud provider offers a flexible means of providing credentials for authentication. +The following methods are supported, in this order, and explained below: + +- Static credentials +- Environment variables + +### Static credentials ### + +Static credentials can be provided by adding an `access_key` `secret_key` and `region` in-line in the +alicloud provider block: + +Usage: + +``` +provider "alicloud" { + access_key = "${var.access_key}" + secret_key = "${var.secret_key}" + region = "${var.region}" +} +``` + + +###Environment variables + +You can provide your credentials via `ALICLOUD_ACCESS_KEY` and `ALICLOUD_SECRET_KEY`, +environment variables, representing your Alicloud Access Key and Secret Key, respectively. +`ALICLOUD_REGION` is also used, if applicable: + +``` +provider "alicloud" {} +``` + +Usage: + +``` +$ export ALICLOUD_ACCESS_KEY="anaccesskey" +$ export ALICLOUD_SECRET_KEY="asecretkey" +$ export ALICLOUD_REGION="cn-beijing" +$ terraform plan +``` + + +## Argument Reference + +The following arguments are supported: + +* `access_key` - (Optional) This is the Alicloud access key. It must be provided, but + it can also be sourced from the `ALICLOUD_ACCESS_KEY` environment variable. + +* `secret_key` - (Optional) This is the Alicloud secret key. It must be provided, but + it can also be sourced from the `ALICLOUD_SECRET_KEY` environment variable. + +* `region` - (Required) This is the Alicloud region. It must be provided, but + it can also be sourced from the `ALICLOUD_REGION` environment variables. + + +## Testing + +Credentials must be provided via the `ALICLOUD_ACCESS_KEY`, and `ALICLOUD_SECRET_KEY` environment variables in order to run acceptance tests. + diff --git a/website/source/docs/providers/alicloud/r/disk.html.markdown b/website/source/docs/providers/alicloud/r/disk.html.markdown new file mode 100644 index 0000000000..d295edbff2 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/disk.html.markdown @@ -0,0 +1,56 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_disk" +sidebar_current: "docs-alicloud-resource-disk" +description: |- + Provides a ECS Disk resource. +--- + +# alicloud\_disk + +Provides a ECS disk resource. + +~> **NOTE:** One of `size` or `snapshot_id` is required when specifying an ECS disk. If all of them be specified, `size` must more than the size of snapshot which `snapshot_id` represents. Currently, `alicloud_disk` doesn't resize disk. + +## Example Usage + +``` +# Create a new ECS disk. +resource "alicloud_disk" "ecs_disk" { + # cn-beijing + availability_zone = "cn-beijing-b" + name = "New-disk" + description = "Hello ecs disk." + category = "cloud_efficiency" + size = "30" + + tags { + Name = "TerraformTest" + } +} +``` +## Argument Reference + +The following arguments are supported: + +* `availability_zone` - (Required, Forces new resource) The Zone to create the disk in. +* `name` - (Optional) Name of the ECS disk. This name can have a string of 2 to 128 characters, must contain only alphanumeric characters or hyphens, such as "-",".","_", and must not begin or end with a hyphen, and must not begin with http:// or https://. Default value is null. +* `description` - (Optional) Description of the disk. This description can have a string of 2 to 256 characters, It cannot begin with http:// or https://. Default value is null. +* `category` - (Optional, Forces new resource) Category of the disk. Valid values are `cloud`, `cloud_efficiency` and `cloud_ssd`. Default is `cloud`. +* `size` - (Required) The size of the disk in GiBs, and its value depends on `Category`. `cloud` disk value range: 5GB ~ 2000GB and other category disk value range: 20 ~ 32768. +* `snapshot_id` - (Optional) A snapshot to base the disk off of. If it is specified, `size` will be invalid and the disk size is equals to the snapshot size. +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The disk ID. +* `availability_zone` - The Zone to create the disk in. +* `name` - The disk name. +* `description` - The disk description. +* `status` - The disk status. +* `category` - The disk category. +* `size` - The disk size. +* `snapshot_id` - The disk snapshot ID. +* `tags` - The disk tags. \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/disk_attachment.html.markdown b/website/source/docs/providers/alicloud/r/disk_attachment.html.markdown new file mode 100644 index 0000000000..133f6aeb0c --- /dev/null +++ b/website/source/docs/providers/alicloud/r/disk_attachment.html.markdown @@ -0,0 +1,68 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_disk_attachment" +sidebar_current: "docs-alicloud-resource-disk-attachment" +description: |- + Provides a ECS Disk Attachment resource. +--- + +# alicloud\_disk\_attachment + +Provides an Alicloud ECS Disk Attachment as a resource, to attach and detach disks from ECS Instances. + +## Example Usage + +Basic usage + +``` +# Create a new ECS disk-attachment and use it attach one disk to a new instance. + +resource "alicloud_security_group" "ecs_sg" { + name = "terraform-test-group" + description = "New security group" +} + +resource "alicloud_disk" "ecs_disk" { + availability_zone = "cn-beijing-a" + size = "50" + + tags { + Name = "TerraformTest-disk" + } +} + +resource "alicloud_instance" "ecs_instance" { + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_type = "ecs.s1.small" + availability_zone = "cn-beijing-a" + security_groups = ["${alicloud_security_group.ecs_sg.id}"] + instance_name = "Hello" + instance_network_type = "classic" + internet_charge_type = "PayByBandwidth" + + tags { + Name = "TerraformTest-instance" + } +} + +resource "alicloud_disk_attachment" "ecs_disk_att" { + disk_id = "${alicloud_disk.ecs_disk.id}" + instance_id = "${alicloud_instance.ecs_instance.id}" + device_name = "/dev/xvdb" +} +``` +## Argument Reference + +The following arguments are supported: + +* `instance_id` - (Required, Forces new resource) ID of the Instance to attach to. +* `disk_id` - (Required, Forces new resource) ID of the Disk to be attached. +* `device_name` - (Required, Forces new resource) The device name to expose to the instance (for example, /dev/xvdb). + +## Attributes Reference + +The following attributes are exported: + +* `instance_id` - ID of the Instance. +* `disk_id` - ID of the Disk. +* `device_name` - The device name exposed to the instance. \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/eip.html.markdown b/website/source/docs/providers/alicloud/r/eip.html.markdown new file mode 100644 index 0000000000..f0ee9bcb92 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/eip.html.markdown @@ -0,0 +1,38 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_eip" +sidebar_current: "docs-alicloud-resource-eip" +description: |- + Provides a ECS EIP resource. +--- + +# alicloud\_eip + +Provides a ECS EIP resource. + +## Example Usage + +``` +# Create a new EIP. +resource "alicloud_eip" "example" { + bandwidth = "10" + internet_charge_type = "PayByBandwidth" +} +``` +## Argument Reference + +The following arguments are supported: + +* `bandwidth` - (Optional) Maximum bandwidth to the elastic public network, measured in Mbps (Mega bit per second). If this value is not specified, then automatically sets it to 5 Mbps. +* `internet_charge_type` - (Optional, Forces new resource) Internet charge type of the EIP, Valid values are `PayByBandwidth`, `PayByTraffic`. Default is `PayByBandwidth`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The EIP ID. +* `bandwidth` - The elastic public network bandwidth. +* `internet_charge_type` - The EIP internet charge type. +* `status` - The EIP current status. +* `ip_address` - The elastic ip address +* `instance` - The ID of the instance which is associated with the EIP. diff --git a/website/source/docs/providers/alicloud/r/eip_association.html.markdown b/website/source/docs/providers/alicloud/r/eip_association.html.markdown new file mode 100644 index 0000000000..afaab31ac9 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/eip_association.html.markdown @@ -0,0 +1,75 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_eip_association" +sidebar_current: "docs-alicloud-resource-eip-association" +description: |- + Provides a ECS EIP Association resource. +--- + +# alicloud\_eip\_association + +Provides an Alicloud EIP Association resource, to associate and disassociate Elastic IPs from ECS Instances. + +~> **NOTE:** `alicloud_eip_association` is useful in scenarios where EIPs are either + pre-existing or distributed to customers or users and therefore cannot be changed. + In addition, it only supports ECS-VPC. + +## Example Usage + +``` +# Create a new EIP association and use it to associate a EIP form a instance. + +resource "alicloud_vpc" "vpc" { + cidr_block = "10.1.0.0/21" +} + +resource "alicloud_vswitch" "vsw" { + vpc_id = "${alicloud_vpc.vpc.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "cn-beijing-a" + depends_on = [ + "alicloud_vpc.vpc"] +} + +resource "alicloud_instance" "ecs_instance" { + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_type = "ecs.s1.small" + availability_zone = "cn-beijing-a" + security_groups = ["${alicloud_security_group.group.id}"] + vswitch_id = "${alicloud_vswitch.vsw.id}" + instance_name = "hello" + instance_network_type = "vpc" + + tags { + Name = "TerraformTest-instance" + } +} + +resource "alicloud_eip" "eip" { +} + +resource "alicloud_eip_association" "eip_asso" { + allocation_id = "${alicloud_eip.eip.id}" + instance_id = "${alicloud_instance.ecs_instance.id}" +} + +resource "alicloud_security_group" "group" { + name = "terraform-test-group" + description = "New security group" + vpc_id = "${alicloud_vpc.vpc.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `allocation_id` - (Optional, Forces new resource) The allocation EIP ID. +* `instance_id` - (Optional, Forces new resource) The ID of the instance. + +## Attributes Reference + +The following attributes are exported: + +* `allocation_id` - As above. +* `instance_id` - As above. \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/instance.html.markdown b/website/source/docs/providers/alicloud/r/instance.html.markdown new file mode 100644 index 0000000000..c4a36a16f1 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/instance.html.markdown @@ -0,0 +1,100 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_instance" +sidebar_current: "docs-alicloud-resource-instance" +description: |- + Provides a ECS instance resource. +--- + +# alicloud\_ecs + +Provides a ECS instance resource. + +## Example Usage + +``` +# Create a new ECS instance for classic +resource "alicloud_security_group" "classic" { + name = "tf_test_foo" + description = "foo" +} + +resource "alicloud_instance" "classic" { + # cn-beijing + availability_zone = "cn-beijing-b" + security_group_id = "${alicloud_security_group.classic.id}" + + allocate_public_ip = "true" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" +} + +# Create a new ECS instance for VPC +resource "alicloud_vpc" "default" { + # Other parameters... +} + +resource "alicloud_vswitch" "default" { + # Other parameters... +} + +resource "alicloud_slb" "vpc" { + name = "test-slb-tf" + vpc_id = "${alicloud_vpc.default.id}" + vswitch_id = "${alicloud_vswitch.default.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `availability_zone` - (Required) The Zone to start the instance in. +* `image_id` - (Required) The Image to use for the instance. +* `instance_type` - (Required) The type of instance to start. +* `security_group_ids` - (Required) A list of security group ids to associate with. If you are creating Instances in a VPC, use `vpc_security_group_ids` instead. +`security_group_ids` instead. +* `instance_name` - (Optional) The name of the ECS. This instance_name can have a string of 2 to 128 characters, must contain only alphanumeric characters or hyphens, such as "-",".","_", and must not begin or end with a hyphen, and must not begin with http:// or https://. If not specified, +Terraform will autogenerate a name beginning with `tf-ecs`. +* `allocate_public_ip` - (Optional) Associate a public ip address with an instance in a VPC or Classic. Boolean value, Default is false. +* `io_optimized` - (Optional) Valid + values are `none`, `optimized`, If `optimized`, the launched ECS instance will be I/O optimized. Default is `optimized`. +* `system_disk_category` - (Optional) Valid values are `cloud`, `cloud_efficiency`, `cloud_ssd`, For I/O optimized instance type, `cloud_ssd` and `cloud_efficiency` disks are supported. For non I/O Optimized instance type, `cloud` disk are supported. +* `system_disk_size` - (Optional) Size of the system disk, value range: 40GB ~ 500GB. Default is 40GB. +* `description` - (Optional) Description of the instance, This description can have a string of 2 to 256 characters, It cannot begin with http:// or https://. Default value is null. +* `internet_charge_type` - (Optional) Internet charge type of the instance, Valid values are `PayByBandwidth`, `PayByTraffic`. Default is `PayByBandwidth`. +* `internet_max_bandwidth_in` - (Optional) Maximum incoming bandwidth from the public network, measured in Mbps (Mega bit per second). Value range: [1, 200]. If this value is not specified, then automatically sets it to 200 Mbps. +* `internet_max_bandwidth_out` - (Optional) Maximum outgoing bandwidth to the public network, measured in Mbps (Mega bit per second). Value range: +`internet_charge_type` is `PayByBandwidth`: this value range [0, 100], If this value is not specified, then automatically sets it to 0 Mbps; If `internet_charge_type` is `PayByTraffic`: this value range [1, 100]. this value must be set value, such as 5. +* `host_name` - (Optional) Host name of the ECS, which is a string of at least two characters. “hostname” cannot start or end with “.” or “-“. In addition, two or more consecutive “.” or “-“ symbols are not allowed. On Windows, the host name can contain a maximum of 15 characters, which can be a combination of uppercase/lowercase letters, numerals, and “-“. The host name cannot contain dots (“.”) or contain only numeric characters. +On other OSs such as Linux, the host name can contain a maximum of 30 characters, which can be segments separated by dots (“.”), where each segment can contain uppercase/lowercase letters, numerals, or “_“. +* `password` - (Optional) Password to an instance is a string of 8 to 30 characters. It must contain uppercase/lowercase letters and numerals, but cannot contain special symbols. +* `vswitch_id` - (Optional) The virtual switch ID to launch in VPC. If you want to create instances in VPC network, this parameter must be set. +* `instance_charge_type` - (Optional) Valid values are `PrePaid`, `PostPaid`, The default is `PostPaid`. +* `period` - (Optional) The time that you have bought the resource, in month. Only valid when instance_charge_type is set as `PrePaid`. Value range [1, 12]. +* `private_ip` - (Optional) Private IP address to associate with the instance in a VPC. +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The instance ID. +* `availability_zone` - The Zone to start the instance in. +* `instance_name` - The instance name. +* `host_name` - The instance host name. +* `description` - The instance description. +* `status` - The instance status. +* `image_id` - The instance Image Id. +* `instance_type` - The instance type. +* `instance_network_type` - The instance network type and it has two values: `vpc` and `classic`. +* `io_optimized` - The instance whether I/O optimized. +* `private_ip` - The instance private ip. +* `public_ip` - The instance public ip. +* `vswitch_id` - If the instance created in VPC, then this value is virtual switch ID. +* `tags` - The instance tags, use jsonencode(item) to display the value. \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/nat_gateway.html.markdown b/website/source/docs/providers/alicloud/r/nat_gateway.html.markdown new file mode 100644 index 0000000000..bc3597864c --- /dev/null +++ b/website/source/docs/providers/alicloud/r/nat_gateway.html.markdown @@ -0,0 +1,74 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_nat_gateway" +sidebar_current: "docs-alicloud-resource-nat-gateway" +description: |- + Provides a resource to create a VPC NAT Gateway. +--- + +# alicloud\_nat\_gateway + +Provides a resource to create a VPC NAT Gateway. + +## Example Usage + +Basic usage + +``` +resource "alicloud_vpc" "vpc" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "vsw" { + vpc_id = "${alicloud_vpc.vpc.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} + +resource "alicloud_nat_gateway" "nat_gateway" { + vpc_id = "${alicloud_vpc.vpc.id}" + spec = "Small" + name = "test_foo" + bandwidth_packages = [{ + ip_count = 1 + bandwidth = 5 + zone = "cn-beijing-b" + }, { + ip_count = 2 + bandwidth = 10 + zone = "cn-beijing-b" + }] + depends_on = [ + "alicloud_vswitch.vsw"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vpc_id` - (Required, Forces New Resorce) The VPC ID. +* `spec` - (Required, Forces New Resorce) The specification of the nat gateway. Valid values are `Small`, `Middle` and `Large`. Details refer to [Nat Gateway Specification](https://help.aliyun.com/document_detail/42757.html?spm=5176.doc32322.6.559.kFNBzv) +* `name` - (Optional) Name of the nat gateway. The value can have a string of 2 to 128 characters, must contain only alphanumeric characters or hyphens, such as "-",".","_", and must not begin or end with a hyphen, and must not begin with http:// or https://. Defaults to null. +* `description` - (Optional) Description of the nat gateway, This description can have a string of 2 to 256 characters, It cannot begin with http:// or https://. Defaults to null. +* `bandwidth_packages` - (Required) A list of bandwidth packages for the nat gatway. + +## Block bandwidth package + +The bandwidth package mapping supports the following: + +* `ip_count` - (Required) The IP number of the current bandwidth package. Its value range from 1 to 50. +* `bandwidth` - (Required) The bandwidth value of the current bandwidth package. Its value range from 5 to 5000. +* `zone` - (Optional) The AZ for the current bandwidth. If this value is not specified, Terraform will set a random AZ. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the nat gateway. +* `name` - The name of the nat gateway. +* `description` - The description of the nat gateway. +* `spec` - The specification of the nat gateway. +* `vpc_id` - The VPC ID for the nat gateway. +* `bandwidth_package_ids` - A list ID of the bandwidth packages, and split them with commas diff --git a/website/source/docs/providers/alicloud/r/security_group.html.markdown b/website/source/docs/providers/alicloud/r/security_group.html.markdown new file mode 100644 index 0000000000..6a65f0e8c8 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/security_group.html.markdown @@ -0,0 +1,53 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_security_group" +sidebar_current: "docs-alicloud-resource-security-group" +description: |- + Provides a Alicloud Security Group resource. +--- + +# alicloud\_security\_group + +Provides a security group resource. + +~> **NOTE:** `alicloud_security_group` is used to build and manage a security group, and `alicloud_security_group_rule` can define ingress or egress rules for it. + +## Example Usage + +Basic Usage + +``` +resource "alicloud_security_group" "group" { + name = "terraform-test-group" + description = "New security group" +} +``` +Basic usage for vpc + +``` +resource "alicloud_security_group" "group" { + name = "new-group" + vpc_id = "${alicloud_vpc.vpc.id}" +} + +resource "alicloud_vpc" "vpc" { + cidr_block = "10.1.0.0/21" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Optional) The name of the security group. Defaults to null. +* `description` - (Optional, Forces new resource) The security group description. Defaults to null. +* `vpc_id` - (Optional, Forces new resource) The VPC ID. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the security group +* `vpc_id` - The VPC ID. +* `name` - The name of the security group +* `description` - The description of the security group \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/security_group_rule.html.markdown b/website/source/docs/providers/alicloud/r/security_group_rule.html.markdown new file mode 100644 index 0000000000..49bc269ee1 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/security_group_rule.html.markdown @@ -0,0 +1,61 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_security_group_rule" +sidebar_current: "docs-alicloud-resource-security-group-rule" +description: |- + Provides a Alicloud Security Group Rule resource. +--- + +# alicloud\_security\_group\_rule + +Provides a security group rule resource. +Represents a single `ingress` or `egress` group rule, which can be added to external Security Groups. + +~> **NOTE:** `nic_type` should set to `intranet` when security group type is `vpc`. In this situation it does not distinguish between intranet and internet, the rule is effective on them both. + + +## Example Usage + +Basic Usage + +``` +resource "alicloud_security_group" "default" { + name = "default" +} + +resource "alicloud_security_group_rule" "allow_all_tcp" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "1/65535" + priority = 1 + security_group_id = "${alicloud_security_group.default.id}"ecs.InstanceAttributesType + cidr_ip = "0.0.0.0/0" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `type` - (Required) The type of rule being created. Valid options are `ingress` (inbound) or `egress` (outbound). +* `ip_protocol` - (Required) The protocol. Can be `tcp`, `udp`, `icmp`, `gre` or `all`. +* `port_range` - (Required) The range of port numbers relevant to the IP protocol. When the protocol is tcp or udp, the default port number range is 1-65535. For example, `1/200` means that the range of the port numbers is 1-200. +* `security_group_id` - (Required) The security group to apply this rule to. +* `nic_type` - (Optional, Forces new resource) Network type, can be either `internet` or `intranet`, the default value is `internet`. +* `policy` - (Optional, Forces new resource) Authorization policy, can be either `accept` or `drop`, the default value is `accept`. +* `priority` - (Optional, Forces new resource) Authorization policy priority, with parameter values: `1-100`, default value: 1. +* `cidr_ip` - (Optional, Forces new resource) The target IP address range. The default value is 0.0.0.0/0 (which means no restriction will be applied). Other supported formats include 10.159.6.18/12. Only IPv4 is supported. +* `source_security_group_id` - (Optional, Forces new resource) The target security group ID within the same region. Either the `source_security_group_id` or `cidr_ip` must be set. If both are set, then `cidr_ip` is authorized by default. If this field is specified, but no `cidr_ip` is specified, the `nic_type` can only select `intranet`. +* `source_group_owner_account` - (Optional, Forces new resource) The Alibaba Cloud user account Id of the target security group when security groups are authorized across accounts. This parameter is invalid if `cidr_ip` has already been set. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the security group rule +* `type` - The type of rule, `ingress` or `egress` +* `name` - The name of the security group +* `port_range` - The range of port numbers +* `ip_protocol` - The protocol of the security group rule \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/slb.html.markdown b/website/source/docs/providers/alicloud/r/slb.html.markdown new file mode 100644 index 0000000000..d021985c39 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/slb.html.markdown @@ -0,0 +1,90 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_slb" +sidebar_current: "docs-alicloud-resource-slb" +description: |- + Provides an Application Load Banlancer resource. +--- + +# alicloud\_slb + +Provides an Application Load Balancer resource. + +## Example Usage + +``` +# Create a new load balancer for classic +resource "alicloud_slb" "classic" { + name = "test-slb-tf" + internet = true + internet_charge_type = "paybybandwidth" + bandwidth = 5 + listener = [ + { + "instance_port" = "2111" + "lb_port" = "21" + "lb_protocol" = "tcp" + "bandwidth" = "5" + },{ + "instance_port" = "8000" + "lb_port" = "80" + "lb_protocol" = "http" + "bandwidth" = "5" + },{ + "instance_port" = "1611" + "lb_port" = "161" + "lb_protocol" = "udp" + "bandwidth" = "5" + }] +} + +# Create a new load balancer for VPC +resource "alicloud_vpc" "default" { + # Other parameters... +} + +resource "alicloud_vswitch" "default" { + # Other parameters... +} + +resource "alicloud_slb" "vpc" { + name = "test-slb-tf" + vswitch_id = "${alicloud_vswitch.default.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Optional) The name of the SLB. This name must be unique within your AliCloud account, can have a maximum of 80 characters, +must contain only alphanumeric characters or hyphens, such as "-","/",".","_", and must not begin or end with a hyphen. If not specified, +Terraform will autogenerate a name beginning with `tf-lb`. +* `internet` - (Optional, Forces New Resource) If true, the SLB addressType will be internet, false will be intranet, Default is false. If load balancer launched in VPC, this value must be "false". +* `internet_charge_type` - (Optional, Forces New Resource) Valid + values are `paybybandwidth`, `paybytraffic`. If this value is "paybybandwidth", then argument "internet" must be "true". Default is "paybytraffic". If load balancer launched in VPC, this value must be "paybytraffic". +* `bandwidth` - (Optional) Valid + value is between 1 and 1000, If argument "internet_charge_type" is "paybytraffic", then this value will be ignore. +* `listener` - (Optional) Additional SLB listener. See [Block listener](#block-listener) below for details. +* `vswitch_id` - (Required for a VPC SLB, Forces New Resource) The VSwitch ID to launch in. + +## Block listener + +The listener mapping supports the following: + +* `instance_port` - (Required) The port on which the backend servers are listening. Valid value is between 1 to 65535. +* `lb_port` - (Required) The port on which the load balancer is listening. Valid value is between 1 to 65535. +* `lb_protocol` - (Required) The protocol to listen on. Valid values are `http` and and `tcp` and `udp`. +* `bandwidth` - (Required) The bandwidth on which the load balancer is listening. Valid values is -1 or between 1 and 1000. If -1, the bindwidth will haven’t upper limit. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the load balancer. +* `name` - The name of the load balancer. +* `internet` - The internet of the load balancer. +* `internet_charge_type` - The internet_charge_type of the load balancer. +* `bandwidth` - The bandwidth of the load balancer. +* `vswitch_id` - The VSwitch ID of the load balancer. Only available on SLB launched in a VPC. +* `address` - The IP address of the load balancer. \ No newline at end of file diff --git a/website/source/docs/providers/alicloud/r/slb_attachment.html.markdown b/website/source/docs/providers/alicloud/r/slb_attachment.html.markdown new file mode 100644 index 0000000000..bc35e2e8eb --- /dev/null +++ b/website/source/docs/providers/alicloud/r/slb_attachment.html.markdown @@ -0,0 +1,43 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_slb_attachment" +sidebar_current: "docs-alicloud-resource-slb-attachment" +description: |- + Provides an Application Load Banlancer Attachment resource. +--- + +# alicloud\_slb\_attachment + +Provides an Application Load Balancer Attachment resource. + +## Example Usage + +``` +# Create a new load balancer attachment for classic +resource "alicloud_slb" "default" { + # Other parameters... +} + +resource "alicloud_instance" "default" { + # Other parameters... +} + +resource "alicloud_slb_attachment" "default" { + slb_id = "${alicloud_slb.default.id}" + instances = ["${alicloud_instance.default.id}"] +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `slb_id` - (Required) The ID of the SLB.. +* `instances` - (Required) A list of instance ids to added backend server in the SLB. If dettachment instances then this value set []. + +## Attributes Reference + +The following attributes are exported: + +* `backend_servers` - The backend servers of the load balancer. diff --git a/website/source/docs/providers/alicloud/r/vpc.html.markdown b/website/source/docs/providers/alicloud/r/vpc.html.markdown new file mode 100644 index 0000000000..af34cf110b --- /dev/null +++ b/website/source/docs/providers/alicloud/r/vpc.html.markdown @@ -0,0 +1,41 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_vpc" +sidebar_current: "docs-alicloud-resource-vpc" +description: |- + Provides a Alicloud VPC resource. +--- + +# alicloud\_vpc + +Provides a VPC resource. + +~> **NOTE:** Terraform will auto build a router and a route table while it uses `alicloud_vpc` to build a vpc resource. + +## Example Usage + +Basic Usage + +``` +resource "alicloud_vpc" "vpc" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} +``` +## Argument Reference + +The following arguments are supported: + +* `cidr_block` - (Required, Forces new resource) The CIDR block for the VPC. +* `name` - (Optional) The name of the VPC. Defaults to null. +* `description` - (Optional) The VPC description. Defaults to null. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the VPC. +* `cidr_block` - The CIDR block for the VPC. +* `name` - The name of the VPC. +* `description` - The description of the VPC. +* `router_id` - The ID of the router created by default on VPC creation. diff --git a/website/source/docs/providers/alicloud/r/vroute_entry.html.markdown b/website/source/docs/providers/alicloud/r/vroute_entry.html.markdown new file mode 100644 index 0000000000..0b9d695378 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/vroute_entry.html.markdown @@ -0,0 +1,53 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_route_entry" +sidebar_current: "docs-alicloud-resource-route-entry" +description: |- + Provides a Alicloud Route Entry resource. +--- + +# alicloud\_route\_entry + +Provides a route entry resource. + +## Example Usage + +Basic Usage + +``` +resource "alicloud_vpc" "vpc" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_route_entry" "default" { + router_id = "${alicloud_vpc.default.router_id}" + route_table_id = "${alicloud_vpc.default.router_table_id}" + destination_cidrblock = "${var.entry_cidr}" + nexthop_type = "Instance" + nexthop_id = "${alicloud_instance.snat.id}" +} + +resource "alicloud_instance" "snat" { + // ... +} +``` +## Argument Reference + +The following arguments are supported: + +* `router_id` - (Required, Forces new resource) The ID of the virtual router attached to Vpc. +* `route_table_id` - (Required, Forces new resource) The ID of the route table. +* `destination_cidrblock` - (Required, Forces new resource) The RouteEntry's target network segment. +* `nexthop_type` - (Required, Forces new resource) The next hop type. Available value is Instance. +* `nexthop_id` - (Required, Forces new resource) The route entry's next hop. + +## Attributes Reference + +The following attributes are exported: + +* `router_id` - (Required, Forces new resource) The ID of the virtual router attached to Vpc. +* `route_table_id` - (Required, Forces new resource) The ID of the route table. +* `destination_cidrblock` - (Required, Forces new resource) The RouteEntry's target network segment. +* `nexthop_type` - (Required, Forces new resource) The next hop type. Available value is Instance. +* `nexthop_id` - (Required, Forces new resource) The route entry's next hop. diff --git a/website/source/docs/providers/alicloud/r/vswitch.html.markdown b/website/source/docs/providers/alicloud/r/vswitch.html.markdown new file mode 100644 index 0000000000..cce548c69e --- /dev/null +++ b/website/source/docs/providers/alicloud/r/vswitch.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_vswitch" +sidebar_current: "docs-alicloud-resource-vswitch" +description: |- + Provides a Alicloud VPC switch resource. +--- + +# alicloud\_vswitch + +Provides a VPC switch resource. + +## Example Usage + +Basic Usage + +``` +resource "alicloud_vpc" "vpc" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "vsw" { + vpc_id = "${alicloud_vpc.vpc.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "cn-beijing-b" +} +``` +## Argument Reference + +The following arguments are supported: + +* `availability_zone` - (Required, Forces new resource) The AZ for the switch. +* `vpc_id` - (Required, Forces new resource) The VPC ID. +* `cidr_block` - (Required, Forces new resource) The CIDR block for the switch. +* `name` - (Optional) The name of the switch. Defaults to null. +* `description` - (Optional) The switch description. Defaults to null. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the switch. +* `availability_zone` The AZ for the switch. +* `cidr_block` - The CIDR block for the switch. +* `vpc_id` - The VPC ID. +* `name` - The name of the switch. +* `description` - The description of the switch.