4 # Copyright 2016 RIFT.IO Inc
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
19 import rift
.rwcal
.openstack
as openstack_drv
29 logging
.basicConfig(level
=logging
.DEBUG
)
30 logger
= logging
.getLogger()
32 rwlog_handler
= rwlogger
.RwLogger(category
="rw-cal-log",
33 subcategory
="openstack",)
34 logger
.addHandler(rwlog_handler
)
35 #logger.setLevel(logging.DEBUG)
38 FILE_LOCK
= '/tmp/_openstack_prepare_vm.lock'
40 # This will create it if it does not exist already
41 self
.filename
= FileLock
.FILE_LOCK
44 # Bitwise OR fcntl.LOCK_NB if you need a non-blocking lock
46 logger
.info("<PID: %d> Attempting to acquire log." %os.getpid())
47 self
.handle
= open(self
.filename
, 'w')
48 fcntl
.flock(self
.handle
, fcntl
.LOCK_EX
)
49 logger
.info("<PID: %d> Lock successfully acquired." %os.getpid())
52 fcntl
.flock(self
.handle
, fcntl
.LOCK_UN
)
54 logger
.info("<PID: %d> Released lock." %os.getpid())
57 if self
.handle
and self
.handle
.closed
== False:
61 def allocate_floating_ip(drv
, argument
):
62 #### Allocate a floating_ip
63 available_ip
= [ ip
for ip
in drv
.nova_floating_ip_list() if ip
.instance_id
== None ]
65 if argument
.pool_name
:
66 ### Filter further based on IP address
67 available_ip
= [ ip
for ip
in available_ip
if ip
.pool
== argument
.pool_name
]
70 logger
.info("<PID: %d> No free floating_ips available. Allocating fresh from pool: %s" %(os
.getpid(), argument
.pool_name
))
71 pool_name
= argument
.pool_name
if argument
.pool_name
is not None else None
72 floating_ip
= drv
.nova_floating_ip_create(pool_name
)
74 floating_ip
= random
.choice(available_ip
)
75 logger
.info("<PID: %d> Selected floating_ip: %s from available free pool" %(os
.getpid(), floating_ip
))
80 def handle_floating_ip_assignment(drv
, server
, argument
, management_ip
):
82 ### Try 3 time (<<<magic number>>>)
84 for attempt
in range(RETRY
):
87 floating_ip
= allocate_floating_ip(drv
, argument
)
88 logger
.info("Assigning the floating_ip: %s to VM: %s" %(floating_ip
, server
['name']))
89 drv
.nova_floating_ip_assign(argument
.server_id
,
92 logger
.info("Assigned floating_ip: %s to management_ip: %s" %(floating_ip
, management_ip
))
93 except Exception as e
:
94 logger
.error("Could not assign floating_ip: %s to VM: %s. Exception: %s" %(floating_ip
, server
['name'], str(e
)))
96 if attempt
== (RETRY
-1):
97 logger
.error("Max attempts %d reached for floating_ip allocation. Giving up" %attempt
)
100 logger
.error("Retrying floating ip allocation. Current retry count: %d" %attempt
)
106 def assign_floating_ip_address(drv
, argument
):
107 if not argument
.floating_ip
:
110 server
= drv
.nova_server_get(argument
.server_id
)
113 server
= drv
.nova_server_get(argument
.server_id
)
114 for network_name
,network_info
in server
['addresses'].items():
115 if network_info
and network_name
== argument
.mgmt_network
:
116 for n_info
in network_info
:
117 if 'OS-EXT-IPS:type' in n_info
and n_info
['OS-EXT-IPS:type'] == 'fixed':
118 management_ip
= n_info
['addr']
119 handle_floating_ip_assignment(drv
, server
, argument
, management_ip
)
122 logger
.info("Waiting for management_ip to be assigned to server: %s" %(server
['name']))
125 logger
.info("No management_ip IP available to associate floating_ip for server: %s" %(server
['name']))
129 def create_port_metadata(drv
, argument
):
130 if argument
.port_metadata
== False:
133 ### Get Management Network ID
134 network_list
= drv
.neutron_network_list()
135 mgmt_network_id
= [net
['id'] for net
in network_list
if net
['name'] == argument
.mgmt_network
][0]
136 port_list
= [ port
for port
in drv
.neutron_port_list(**{'device_id': argument
.server_id
})
137 if port
['network_id'] != mgmt_network_id
]
140 meta_data
['rift-meta-ports'] = str(len(port_list
))
142 for port
in port_list
:
144 info
.append('"port_name":"'+port
['name']+'"')
145 if 'mac_address' in port
:
146 info
.append('"hw_addr":"'+port
['mac_address']+'"')
147 if 'network_id' in port
:
148 #info.append('"network_id":"'+port['network_id']+'"')
149 net_name
= [net
['name'] for net
in network_list
if net
['id'] == port
['network_id']]
151 info
.append('"network_name":"'+net_name
[0]+'"')
152 if 'fixed_ips' in port
:
153 ip_address
= port
['fixed_ips'][0]['ip_address']
154 info
.append('"ip":"'+ip_address
+'"')
156 meta_data
['rift-meta-port-'+str(port_id
)] = '{' + ','.join(info
) + '}'
159 nvconn
= drv
.nova_drv
._get
_nova
_connection
()
160 nvconn
.servers
.set_meta(argument
.server_id
, meta_data
)
162 def get_volume_id(server_vol_list
, name
):
163 if server_vol_list
is None:
166 for os_volume
in server_vol_list
:
168 " Device name is of format /dev/vda"
169 vol_name
= (os_volume
['device']).split('/')[2]
173 return os_volume
['volumeId']
175 def create_volume_metadata(drv
, argument
):
176 if argument
.vol_metadata
is None:
179 yaml_vol_str
= argument
.vol_metadata
.read()
180 yaml_vol_cfg
= yaml
.load(yaml_vol_str
)
182 srv_volume_list
= drv
.nova_volume_list(argument
.server_id
)
183 for volume
in yaml_vol_cfg
:
184 if 'custom_meta_data' not in volume
:
187 for vol_md_item
in volume
['custom_meta_data']:
188 if 'value' not in vol_md_item
:
190 vmd
[vol_md_item
['name']] = vol_md_item
['value']
193 vol_id
= get_volume_id(srv_volume_list
, volume
['name'])
195 logger
.error("Server %s Could not find volume %s" %(argument
.server_id
, volume
['name']))
197 drv
.cinder_volume_set_metadata(vol_id
, vmd
)
200 def prepare_vm_after_boot(drv
,argument
):
201 ### Important to call create_port_metadata before assign_floating_ip_address
202 ### since assign_floating_ip_address can wait thus delaying port_metadata creation
204 ### Wait for a max of 5 minute for server to come up -- Needs fine tuning
207 for i
in range(int(wait_time
/sleep_time
)):
208 server
= drv
.nova_server_get(argument
.server_id
)
209 if server
['status'] == 'ACTIVE':
210 logger
.info("Server %s to reached active state" %(server
['name']))
212 elif server
['status'] == 'BUILD':
213 logger
.info("Waiting for server: %s to build. Current state: %s" %(server
['name'], server
['status']))
214 time
.sleep(sleep_time
)
216 logger
.info("Server %s reached state: %s" %(server
['name'], server
['status']))
219 logger
.error("Server %s did not reach active state in %d seconds. Current state: %s" %(server
['name'], wait_time
, server
['status']))
222 #create_port_metadata(drv, argument)
223 create_volume_metadata(drv
, argument
)
224 assign_floating_ip_address(drv
, argument
)
231 parser
= argparse
.ArgumentParser(description
='Script to create openstack resources')
232 parser
.add_argument('--auth_url',
236 help='Keystone Auth URL')
238 parser
.add_argument('--username',
242 help = "Username for openstack installation")
244 parser
.add_argument('--password',
248 help = "Password for openstack installation")
250 parser
.add_argument('--tenant_name',
252 dest
= "tenant_name",
254 help = "Tenant name openstack installation")
256 parser
.add_argument('--user_domain',
258 dest
= "user_domain",
261 help = "User domain name for openstack installation")
263 parser
.add_argument('--project_domain',
265 dest
= "project_domain",
268 help = "Project domain name for openstack installation")
270 parser
.add_argument('--region',
273 default
= "RegionOne",
275 help = "Region name for openstack installation")
277 parser
.add_argument('--mgmt_network',
279 dest
= "mgmt_network",
281 help = "mgmt_network")
283 parser
.add_argument('--server_id',
287 help = "Server ID on which boot operations needs to be performed")
289 parser
.add_argument('--floating_ip',
290 action
= "store_true",
291 dest
= "floating_ip",
293 help = "Floating IP assignment required")
295 parser
.add_argument('--pool_name',
299 help = "Floating IP pool name")
302 parser
.add_argument('--port_metadata',
303 action
= "store_true",
304 dest
= "port_metadata",
306 help = "Create Port Metadata")
308 parser
.add_argument("--vol_metadata", type=argparse
.FileType('r'))
310 argument
= parser
.parse_args()
312 if not argument
.auth_url
:
313 logger
.error("ERROR: AuthURL is not configured")
316 logger
.info("Using AuthURL: %s" %(argument
.auth_url
))
318 if not argument
.username
:
319 logger
.error("ERROR: Username is not configured")
322 logger
.info("Using Username: %s" %(argument
.username
))
324 if not argument
.password
:
325 logger
.error("ERROR: Password is not configured")
328 logger
.info("Using Password: %s" %(argument
.password
))
330 if not argument
.tenant_name
:
331 logger
.error("ERROR: Tenant Name is not configured")
334 logger
.info("Using Tenant Name: %s" %(argument
.tenant_name
))
336 if not argument
.mgmt_network
:
337 logger
.error("ERROR: Management Network Name is not configured")
340 logger
.info("Using Management Network: %s" %(argument
.mgmt_network
))
342 if not argument
.server_id
:
343 logger
.error("ERROR: Server ID is not configured")
346 logger
.info("Using Server ID : %s" %(argument
.server_id
))
354 logger
.error("fork failed: %d (%s)\n" % (e
.errno
, e
.strerror
))
358 drv
= openstack_drv
.OpenstackDriver(username
= argument
.username
,
359 password
= argument
.password
,
360 auth_url
= argument
.auth_url
,
361 tenant_name
= argument
.tenant_name
,
362 mgmt_network
= argument
.mgmt_network
,
363 user_domain_name
= argument
.user_domain
,
364 project_domain_name
= argument
.project_domain
,
365 region
= argument
.region
)
367 prepare_vm_after_boot(drv
, argument
)
370 if __name__
== "__main__":