3 # Copyright 2016 RIFT.IO Inc
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
25 # TODO: Pull the lastest of owned instances.
26 __default_instance_ami__
= 'ami-e421bc8c'
28 # TODO: Make VPC's per user?
29 __default_subnet__
= 'subnet-4b484363'
30 __default_security_group__
= 'sg-d9da90bc'
32 __default_instance_type__
= 'm1.medium'
33 __default_vpc__
= 'vpc-e7ed4482'
36 def __init__(self
, subnet
=None, ami
=None):
37 self
._subnet
= subnet
if subnet
is not None else __default_subnet__
38 self
._ami
= ami
if ami
is not None else __default_instance_ami__
40 self
._conn
= boto
.connect_ec2()
43 def cloud_init_current_user():
45 Return user_data configuration suitable for cloud-init that will create a user
46 with sudo and ssh key access on the remote instance.
48 ssh keys are found with the glob ~/.ssh/*pub*
50 user_data
= "users:\n"
51 user_data
+= " - name: %s\n" % (os
.getlogin(),)
52 user_data
+= " groups: [wheel, adm, systemd-journal]\n"
53 user_data
+= " sudo: [\"ALL=(ALL) NOPASSWD:ALL\"]\n"
54 user_data
+= " shell: /bin/bash\n"
55 user_data
+= " ssh_authorized_keys:\n"
56 for pub_key
in glob
.glob('%s/.ssh/*pub*' % (os
.environ
['HOME'],)):
57 with
open(pub_key
) as fp
:
58 user_data
+= " - %s" % (fp
.read(),)
64 def cloud_init_yum_repos():
66 Return a string of user_data commands that can be used to update the yum
67 repos to point to the correct location. They should be added by the caller
68 within a 'runcmd:' block.
70 ret
= " - sed -i -e 's,www\.,,' -e 's,riftio\.com/mirrors,riftio.com:8881,' /etc/yum.repos.d/*.repo\n"
73 def instances(self
, cluster_component
, cluster_instance
):
75 List of instances owned by the given cluster instance
77 @param cluster_component - parent cluster of each instance
78 @param cluster_instance - instance id of the owning cluster
79 @param n_instances - number of requested instances
81 @return - list of boto.ec2.instance.Instances provisioned
84 reservations
= self
._conn
.get_all_instances()
85 for instance
in [instance
for reservation
in reservations
for instance
in reservation
.instances
]:
87 if (tags
.get('parent_component') == cluster_component
88 and tags
.get('parent_instance') == cluster_instance
):
93 def provision_master(self
, cluster_component
, cluster_instance
):
95 Provision a master instance in EC2. The master instance is a special instance with the
98 - /home shared over NFS
100 @param cluster_component - parent cluster of each instance
101 @param cluster_instance - instance id of the owning cluster
103 @return - boto.ec2.instance.Instances provisioned
105 vpc
= boto
.vpc
.VPCConnection()
106 subnet
= vpc
.get_all_subnets(subnet_ids
=__default_subnet__
)[0]
107 cidr_block
= subnet
.cidr_block
110 user_data
= "#cloud-config\n"
111 user_data
+= "runcmd:\n"
112 user_data
+= " - echo '/home %s(rw,root_squash,sync)' > /etc/exports\n" % (cidr_block
,)
113 user_data
+= " - systemctl start nfs-server\n"
114 user_data
+= " - systemctl enable nfs-server\n"
115 user_data
+= self
.cloud_init_yum_repos()
116 user_data
+= self
.cloud_init_current_user()
119 net_if
= boto
.ec2
.networkinterface
.NetworkInterfaceSpecification(
120 subnet_id
=__default_subnet__
,
121 groups
=[__default_security_group__
,],
122 associate_public_ip_address
=True)
124 net_ifs
= boto
.ec2
.networkinterface
.NetworkInterfaceCollection(net_if
)
126 new_reservation
= self
._conn
.run_instances(
130 instance_type
=__default_instance_type__
,
131 network_interfaces
=net_ifs
,
134 instance
= new_reservation
.instances
[0]
136 instance
.add_tag('parent_component', cluster_component
)
137 instance
.add_tag('parent_instance', cluster_instance
)
138 instance
.add_tag('master', 'self')
143 def provision(self
, cluster_component
, cluster_instance
, n_instances
=1, master_instance
=None, net_ifs
=None):
145 Provision a number of EC2 instanced to be used in a cluster.
147 @param cluster_component - parent cluster of each instance
148 @param cluster_instance - instance id of the owning cluster
149 @param n_instances - number of requested instances
150 @param master_instance - if specified, the boto.ec2.instance.Instance that is providing master
151 services for this cluster
153 @return - list of boto.ec2.instance.Instances provisioned
156 cluster_instance
= int(cluster_instance
)
158 def posess_instance(instance
):
159 instances
.append(instance
)
160 instance
.add_tag('parent_component', cluster_component
)
161 instance
.add_tag('parent_instance', cluster_instance
)
162 if master_instance
is not None:
163 instance
.add_tag('master', master_instance
.id)
165 instance
.add_tag('master', 'None')
167 user_data
= "#cloud-config\n"
168 user_data
+= self
.cloud_init_current_user()
169 user_data
+= "runcmd:\n"
170 user_data
+= self
.cloud_init_yum_repos()
172 if master_instance
is not None:
173 user_data
+= " - echo '%s:/home /home nfs rw,soft,sync 0 0' >> /etc/fstab\n" % (
174 master_instance
.private_ip_address
,)
175 user_data
+= " - mount /home\n"
177 if net_ifs
is not None:
178 kwds
= {'subnet_id': __default_subnet__
}
180 kwds
= {'network_interfaces': net_ifs
}
183 new_reservation
= self
._conn
.run_instances(
185 min_count
=n_instances
,
186 max_count
=n_instances
,
187 instance_type
=__default_instance_type__
,
190 network_interfaces
=net_ifs
)
192 _
= [posess_instance(i
) for i
in new_reservation
.instances
]
196 def stop(self
, instance_id
, free_resources
=True):
198 Stop the specified instance, freeing all allocated resources (elastic ips, etc) if requested.
200 @param instance_id - name of the instance to stop
201 @param free_resource - If True that all resources that were only owned by this instance
202 will be deallocated as well.
204 self
._conn
.terminate_instances(instance_ids
=[instance_id
,])
206 def fastpath111(self
):
207 vpc_conn
= boto
.vpc
.VPCConnection()
208 vpc
= vpc_conn
.get_all_vpcs(vpc_ids
=[__default_vpc__
,])[0]
209 subnet_addrs_split
= vpc
.cidr_block
.split('.')
212 'mgmt': [s
for s
in vpc_conn
.get_all_subnets() if s
.id == __default_subnet__
][0],
219 for i
, network
in enumerate([n
for n
, s
in networks
.items() if s
== None]):
220 addr
= "%s.%s.10%d.0/25" % (subnet_addrs_split
[0], subnet_addrs_split
[1], i
)
222 subnet
= vpc_conn
.create_subnet(vpc
.id, addr
)
223 except boto
.exception
.EC2ResponseError
, e
:
224 if 'InvalidSubnet.Conflict' == e
.error_code
:
225 subnet
= vpc_conn
.get_all_subnets(filters
=[('vpcId', vpc
.id), ('cidrBlock', addr
)])[0]
229 networks
[network
] = subnet
231 def create_interfaces(nets
):
232 ret
= boto
.ec2
.networkinterface
.NetworkInterfaceCollection()
234 for i
, network
in enumerate(nets
):
235 spec
= boto
.ec2
.networkinterface
.NetworkInterfaceSpecification(
236 subnet_id
=networks
[network
].id,
237 description
='%s iface' % (network
,),
238 groups
=[__default_security_group__
],
246 ret
['cli'] = self
.provision_master('fp111', 1)
247 ret
['cli'].add_tag('Name', 'cli')
249 net_ifs
= create_interfaces(['mgmt'])
250 ret
['mgmt'] = self
.provision('fp111', 1, master_instance
=ret
['cli'], net_ifs
=net_ifs
)[0]
251 ret
['mgmt'].add_tag('Name', 'mgmt')
253 net_ifs
= create_interfaces(['mgmt', 'tg_fabric'])
254 ret
['tg1'] = self
.provision('fp111', 1, master_instance
=ret
['cli'], net_ifs
=net_ifs
)[0]
255 ret
['tg1'].add_tag('Name', 'tg1')
257 net_ifs
= create_interfaces(['mgmt', 'tg_fabric', 'tg_lb_ext'])
258 ret
['tg2'] = self
.provision('fp111', 1, master_instance
=ret
['cli'], net_ifs
=net_ifs
)[0]
259 ret
['tg2'].add_tag('Name', 'tg2')
261 net_ifs
= create_interfaces(['mgmt', 'ts_fabric'])
262 ret
['ts1'] = self
.provision('fp111', 1, master_instance
=ret
['cli'], net_ifs
=net_ifs
)[0]
263 ret
['ts1'].add_tag('Name', 'ts1')
265 net_ifs
= create_interfaces(['mgmt', 'ts_fabric', 'lb_ts_ext'])
266 ret
['ts3'] = self
.provision('fp111', 1, master_instance
=ret
['cli'], net_ifs
=net_ifs
)[0]
267 ret
['ts3'].add_tag('Name', 'ts3')
269 net_ifs
= create_interfaces(['mgmt', 'ts_fabric', 'lb_ts_ext', 'tg_lb_ext'])
270 ret
['ts2'] = self
.provision('fp111', 1, master_instance
=ret
['cli'], net_ifs
=net_ifs
)[0]
271 ret
['ts2'].add_tag('Name', 'ts2')