/silverlining/commands/create_node.py
Python | 125 lines | 105 code | 13 blank | 7 comment | 25 complexity | 4d06f8f57bcc7348e8535b2f4b4cbe5c MD5 | raw file
1"""Creates a new server/node""" 2import re 3import time 4from cmdutils import CommandError 5from silverlining.etchosts import set_etc_hosts 6from silversupport.shell import run 7#from silverlining.nodeset import NodeSet 8 9ESTIMATED_TIME = 60 10AFTER_PING_WAIT = 10 11 12 13def command_create_node(config): 14 #if NodeSet.is_nodeset(config.args.node): 15 # return command_create_nodeset(config) 16 node_hostname = config.node_hostname 17 node_exists = False 18 if config.args.if_not_exists: 19 config.logger.info('Checking if node %s exists' % node_hostname) 20 for node in config.driver.list_nodes(): 21 if node.name == node_hostname: 22 config.logger.notify('Node exists: %s' % node) 23 node_exists = True 24 break 25 else: 26 config.logger.info('No node by the name %s exists; continuing with create' 27 % node_hostname) 28 if not node_exists: 29 config.logger.info('Getting image/size info') 30 image = config.select_image(image_match=config.args.image) 31 size = config.select_size(size_match=config.args.size) 32 config.logger.notify('Creating node (image=%s; size=%s)' % ( 33 image.name, size.name)) 34 if not re.search(r'^[a-z0-9.-]+$', node_hostname): 35 raise CommandError( 36 "Invalid hostname (must contain only letters, numbers, ., and -): %r" 37 % node_hostname) 38 assert node_hostname 39 resp = config.driver.create_node( 40 name=node_hostname, 41 image=image, 42 size=size, 43 ex_files={'/root/.ssh/authorized_keys': config.get('root_authorized_keys')}, 44 ) 45 public_ip = resp.public_ip[0] 46 config.logger.notify('Status %s at IP %s' % ( 47 resp.state, public_ip)) 48 set_etc_hosts(config, [node_hostname], public_ip) 49 50 if config.args.setup_node or config.args.wait: 51 if not node_exists: 52 wait_for_node_ready_ping(config, public_ip) 53 config.logger.notify('Waiting %s seconds for full boot' 54 % AFTER_PING_WAIT) 55 time.sleep(AFTER_PING_WAIT) 56 else: 57 return 58 59 if config.args.setup_node: 60 from silverlining.commands.setup_node import command_setup_node 61 config.args.node = node_hostname 62 config.logger.notify('Setting up server') 63 command_setup_node(config) 64 65def command_create_nodeset(config): 66 nodeset = NodeSet(config.args.node) 67 nodes = nodeset.nodes 68 all_nodes = config.driver.list_nodes() 69 for node_name, node_config in sorted(nodes.items()): 70 for node in all_nodes: 71 if node.name == node_name: 72 config.logger.notify('Node exists: %s' % node_name) 73 break 74 else: 75 create_node(config, node_name, node_config) 76 77def conditional_create_node(config, node_name, node_config): 78 config.logger.info('Checking if node %s (%s) exists' % (node_name, node_config.create_node_type)) 79 80 81def wait_for_node_ready(config, node_name): 82 """Polls the server until it is ready""" 83 ## FIXME: the Rackspace API is too slow to use this technique 84 config.logger.start_progress('Waiting for server to be ready...') 85 config.logger.debug('Waiting an initial %s seconds' % ESTIMATED_TIME) 86 time.sleep(ESTIMATED_TIME) 87 while 1: 88 config.logger.show_progress() 89 for node in config.driver.list_nodes(): 90 if node.name == node_name: 91 if node.state == 'ACTIVE': 92 active = True 93 else: 94 active = False 95 break 96 else: 97 config.logger.warn( 98 "No node with the name %s listed" % node_name) 99 config.logger.warn( 100 "Continuing, but this may not complete") 101 time.sleep(10) 102 config.logger.end_progress('server active.') 103 104 105def wait_for_node_ready_ping(config, node_hostname): 106 """Poll a server via ping until it is ready""" 107 start = time.time() 108 config.logger.start_progress('Waiting for server to be ready...') 109 config.logger.debug('Waiting an initial %s seconds' % ESTIMATED_TIME) 110 time.sleep(ESTIMATED_TIME) 111 while 1: 112 config.logger.show_progress() 113 stdout, stderr, returncode = run( 114 ['ping', '-c', '2', '-w', '10', node_hostname], 115 capture_stdout=True, capture_stderr=True) 116 if returncode: 117 config.logger.debug('Ping did not return successful result') 118 else: 119 config.logger.end_progress( 120 "Server created (%s to create)" % format_time(time.time()-start)) 121 break 122 123 124def format_time(secs): 125 return '%i:%02i' % (int(secs/60), int(secs)%60)