def initial_configurations(): # Begin configuration this is only run once in Public Packages if conf.get_config("AMI", "CurrentStatus") != "Complete!": # Configure DataStax variables try: import ds2_configure ds2_configure.run() except: conf.set_config( "AMI", "Error", "Exception seen in %s. Please check ~/datastax_ami/ami.log for more info." % 'ds1_launcher.py') logger.exception('ds1_launcher.py') # Set ulimit hard limits logger.pipe('echo "* soft nofile 32768"', 'sudo tee -a /etc/security/limits.conf') logger.pipe('echo "* hard nofile 32768"', 'sudo tee -a /etc/security/limits.conf') logger.pipe('echo "root soft nofile 32768"', 'sudo tee -a /etc/security/limits.conf') logger.pipe('echo "root hard nofile 32768"', 'sudo tee -a /etc/security/limits.conf') # Change permission back to being ubuntu's and cassandra's logger.exe('sudo chown -hR ubuntu:ubuntu /home/ubuntu') logger.exe('sudo chown -hR cassandra:cassandra /raid0/cassandra', False) logger.exe('sudo chown -hR cassandra:cassandra /mnt/cassandra', False) else: logger.info('Skipping initial configurations.')
def opscenter_installation(): if instance_data['launchindex'] == 0 and options.opscenter != "no": logger.info('Installing OpsCenter...') logger.exe('sudo apt-get install -y opscenter libssl0.9.8') logger.exe('sudo service opscenterd stop') elif options.opscenter == "no": conf.set_config("OpsCenter", "NoOpsCenter", True)
def check_and_launch_opscenter(): if config_data['launchindex'] == 0 and conf.get_config( "OpsCenter", "DNS") and not conf.get_config( "AMI", "CompletedFirstBoot") and not conf.get_config( "OpsCenter", "NoOpsCenter"): logger.exe('sudo service opscenterd restart') conf.set_config("AMI", "CompletedFirstBoot", True)
def construct_opscenter_conf(): try: with open( os.path.join(config_data['opsc_conf_path'], 'opscenterd.conf'), 'r') as f: opsc_conf = f.read() # Configure OpsCenter opsc_conf = opsc_conf.replace('port = 8080', 'port = 7199') opsc_conf = opsc_conf.replace('interface = 127.0.0.1', 'interface = 0.0.0.0') conf.set_config("OpsCenter", "port", 8888) if options.opscenterinterface: conf.set_config("OpsCenter", "port", options.opscenterinterface) opsc_conf = opsc_conf.replace( 'port = 8888', 'port = %s' % options.opscenterinterface) # Deprecated opsc_conf = opsc_conf.replace( 'seed_hosts = localhost', 'seed_hosts = {0}'.format(config_data['opscenterseed'])) with open( os.path.join(config_data['opsc_conf_path'], 'opscenterd.conf'), 'w') as f: f.write(opsc_conf) logger.info('opscenterd.conf configured.') except: logger.info( 'opscenterd.conf not configured since conf was unable to be located.' )
def initial_configurations(): # Begin configuration this is only run once in Public Packages if not conf.get_config("AMI", "CurrentStatus"): # Configure DataStax variables try: import ds2_configure ds2_configure.run() except: conf.set_config("AMI", "Error", "Exception seen in %s. Please check ~/datastax_ami/ami.log for more info." % 'ds1_launcher.py') logger.exception('ds1_launcher.py') # Change permission back to being ubuntu's and cassandra's logger.exe('sudo chown -hR ubuntu:ubuntu /home/ubuntu') logger.exe('sudo chown -hR cassandra:cassandra /raid0/cassandra', False) logger.exe('sudo chown -hR cassandra:cassandra /mnt/cassandra', False) # Ensure permissions directory_list = [ ('/home/ubuntu', 'ubuntu', 'ubuntu'), ('/raid0/cassandra', 'cassandra', 'cassandra'), ('/mnt/cassandra', 'cassandra', 'cassandra') ] for directory in directory_list: if os.path.isdir(directory[0]): logger.info('Checking permissions for: %s' % directory[0]) attempt = 0 max_attempts = 10 permissions_set = False while attempt < max_attempts: logger.info('Attempt #%s' % attempt) stat_info = os.stat(directory[0]) uid = stat_info.st_uid gid = stat_info.st_gid user = pwd.getpwuid(uid)[0] group = grp.getgrgid(gid)[0] if user == directory[1] and group == directory[2]: permissions_set = True break attempt += 1 time.sleep(1) if not permissions_set: logger.warn('Permissions not set correctly. Please run manually:') logger.warn('sudo chown -hR %s:%s %s' % (directory[1], directory[2], directory[0])) logger.warn('sudo service dse restart') else: logger.info('Permissions set for %s as %s:%s' % (directory[0], user, group)) else: logger.info('Skipping initial configurations.')
def start(ask_serverurl=True, ask_username=True, ask_password=True, ask_oauth2=True): """ Interactive configuration. """ conf.load_or_create() serverurl = conf.get_config('serverurl') username = conf.get_config('username') password = conf.get_config('password') client = conf.get_config('client') secret = conf.get_config('secret') if ask_serverurl or serverurl == "": serverurl = __serverurl(serverurl == "") if ask_username or username == "": username = __username(username == "") if ask_password or password == "": password = __password(password == "") if ask_oauth2 or client == "" or secret == "": client = __client(client == "") secret = __secret(secret == "") if serverurl != "": conf.set_config('serverurl', serverurl) if username != "": conf.set_config('username', username) if password != "": conf.set_config('password', password) if client != "": conf.set_config('client', client) if secret != "": conf.set_config('secret', secret) # username/password and client/secret check testresponse = api.api_token() if testresponse.has_error(): conf.save() if testresponse.error == api.Error.http_bad_request: print(testresponse.error_description) if testresponse.error_text == "invalid_grant": start(ask_serverurl=False, ask_oauth2=False) return elif testresponse.error_text == "invalid_client": start(ask_serverurl=False, ask_username=False, ask_password=False) return print("An unknown error occured on the server side. Please try again later.") print() exit(-1) print() if conf.save(): print("The config was saved successfully.") else: print("An error occured while saving the configuration. Please try again.") print() exit(-1)
def opscenter_installation(): if instance_data['launchindex'] == 0 and options.opscenter != "no": logger.info('Installing OpsCenter...') if conf.get_config("AMI", "Type") == "Community": logger.exe('sudo apt-get -y install opscenter-free libssl0.9.8') elif conf.get_config("AMI", "Type") == "Enterprise": logger.exe('sudo apt-get -y install opscenter libssl0.9.8') logger.exe('sudo service opscenterd stop') elif options.opscenter == "no": conf.set_config("OpsCenter", "NoOpsCenter", True)
def use_ec2_userdata(): if not options: exit_path("No parsed options found.") if not options.totalnodes: exit_path("Missing required --totalnodes (-n) switch.") if (options.analyticsnodes + options.searchnodes) > options.totalnodes: exit_path( "Total nodes assigned (--analyticsnodes + --searchnodes) > total available nodes (--totalnodes)" ) if os.path.isfile('/etc/datastax_ami.conf'): if options.version and options.version.lower() == "community": logger.error( "The Dynamic DataStax AMI will automatically install DataStax Enterprise." ) conf.set_config("AMI", "Type", "Enterprise") else: if options.version: if options.version.lower() == "community": conf.set_config("AMI", "Type", "Community") elif options.version.lower() == "enterprise": conf.set_config("AMI", "Type", "Enterprise") else: exit_path("Invalid --version (-v) argument.") else: exit_path("Missing required --version (-v) switch.") if conf.get_config("AMI", "Type") == "Community" and (options.cfsreplication or options.analyticsnodes or options.searchnodes): exit_path( 'CFS Replication, Analytics Nodes, and Search Node settings can only be set in DataStax Enterprise installs.' ) if options.email: logger.info('Setting up diagnostic email using: {0}'.format( options.email)) conf.set_config("AMI", "Email", options.email) if options.clustername: logger.info('Using cluster name: {0}'.format(options.clustername)) instance_data['clustername'] = options.clustername if options.customreservation: instance_data['reservationid'] = options.customreservation logger.info('Using cluster size: {0}'.format(options.totalnodes)) conf.set_config("Cassandra", "TotalNodes", options.totalnodes) logger.info('Using seed indexes: {0}'.format(options.seed_indexes)) if options.reflector: logger.info('Using reflector: {0}'.format(options.reflector))
def use_ec2_userdata(): if not options: exit_path("No parsed options found.") if not options.totalnodes: exit_path("Missing required --totalnodes (-n) switch.") if (options.analyticsnodes + options.searchnodes) > options.totalnodes: exit_path( "Total nodes assigned (--analyticsnodes + --searchnodes) > total available nodes (--totalnodes)" ) if conf.get_config("AMI", "Type") == "Community" and (options.cfsreplication or options.analyticsnodes or options.searchnodes): exit_path( 'CFS Replication, Analytics Nodes, and Search Node settings can only be set in DataStax Enterprise installs.' ) if options.email: logger.info('Setting up diagnostic email using: {0}'.format( options.email)) conf.set_config("AMI", "Email", options.email) if options.clustername: logger.info('Using cluster name: {0}'.format(options.clustername)) instance_data['clustername'] = options.clustername if options.customreservation: instance_data['reservationid'] = options.customreservation if options.seeds: instance_data['seeds'] = options.seeds if options.opscenterip: instance_data['opscenterip'] = options.opscenterip if options.stop_services: with open(ami_disabled, 'w') as f: f.write('') options.realtimenodes = (options.totalnodes - options.analyticsnodes - options.searchnodes) options.seed_indexes = [ 0, options.realtimenodes, options.realtimenodes + options.analyticsnodes ] logger.info('Using cluster size: {0}'.format(options.totalnodes)) conf.set_config("Cassandra", "TotalNodes", options.totalnodes) logger.info('Using seed indexes: {0}'.format(options.seed_indexes)) if options.reflector: logger.info('Using reflector: {0}'.format(options.reflector))
def get_seed_list(): # Read seed list from reflector index_set = set(options.seed_indexes) if options.totalnodes in index_set: index_set.remove(options.totalnodes) expected_responses = len(index_set) time_in_loop = time.time() continue_loop = True logger.info('Reflector loop...') while continue_loop: if time.time() - time_in_loop > 10 * 60: exit_path( 'EC2 is experiencing some issues and has not allocated all of the resources in under 10 minutes.', '\n\nAborting the clustering of this reservation. Please try again.' ) if options.reflector: reflector = options.reflector else: reflector = 'http://reflector2.datastax.com/reflector2.php' req = urllib2.Request( '{0}?indexid={1}&reservationid={2}&internalip={3}&externaldns={4}&second_seed_index={5}&third_seed_index={6}' .format(reflector, instance_data['launchindex'], instance_data['reservationid'], instance_data['internalip'], instance_data['publichostname'], options.seed_indexes[1], options.seed_indexes[2])) req.add_header('User-agent', 'DataStaxSetup') try: response = urllib2.urlopen(req).read() response = json.loads(response) status = "{0} Reflector: Received {1} of {2} responses from: {3}".format( time.strftime("%m/%d/%y-%H:%M:%S", time.localtime()), response['number_of_returned_ips'], expected_responses, response['seeds']) conf.set_config("AMI", "CurrentStatus", status) logger.info(status) if response['number_of_returned_ips'] == expected_responses: conf.set_config("OpsCenter", "DNS", response['opscenter_dns']) config_data['seed_list'] = set(response['seeds']) config_data['opscenterseed'] = response['seeds'][0] continue_loop = False else: time.sleep(2 + random.randint(0, options.totalnodes / 4 + 1)) except: traceback.print_exc(file=sys.stdout) time.sleep(2 + random.randint(0, 5))
def run(): # Remove script files logger.exe('sudo rm ds2_configure.py') logger.info( 'Deleting ds2_configure.py now. This AMI will never change any configs after this first run.' ) additional_pre_configurations() clear_motd() try: get_ec2_data() except urllib2.HTTPError: exit_path( "Clusters within a VPC or backed by Spot Instances are not supported." ) parse_ec2_userdata() if not options.raidonly: use_ec2_userdata() confirm_authentication() if options.javaversion: install_java() setup_repos() clean_installation() opscenter_installation() get_seed_list() checkpoint_info() if not options.raidonly: calculate_tokens() construct_yaml() construct_opscenter_conf() construct_opscenter_cluster_conf() construct_env() construct_dse() prepare_for_raid() if not options.raidonly: construct_core_site() construct_mapred_site() sync_clocks() additional_post_configurations() logger.info("ds2_configure.py completed!\n") conf.set_config("AMI", "CurrentStatus", "Complete!")
def get_ec2_data(): conf.set_config("AMI", "CurrentStatus", "Installation started") # Try to get EC2 User Data try: req = curl_instance_data('http://instance-data/latest/user-data/') instance_data['userdata'] = get_user_data(req) logger.info("Started with user data set to:") logger.info(instance_data['userdata']) except Exception, e: instance_data['userdata'] = '' exit_path("No User Data was set.")
def get_ec2_data(): conf.set_config("AMI", "CurrentStatus", "Installation started") # Try to get EC2 User Data try: req = curl_instance_data('http://169.254.169.254/latest/user-data/') instance_data['userdata'] = get_user_data(req) logger.info("Started with user data set to:") logger.info(instance_data['userdata']) except Exception, e: instance_data[ 'userdata'] = '--totalnodes 1 --version Community --clustername "Test Cluster - No AMI Parameters"' logger.info("No userdata found. Starting 1 node clusters, by default.")
def prepare_for_raid(): # Only create raid0 once. Mount all times in init.d script. A failsafe against deleting this file. if conf.get_config("AMI", "RAIDAttempted"): return conf.set_config("AMI", "CurrentStatus", "Raiding started") # Remove EC2 default /mnt from fstab fstab = '' file_to_open = '/etc/fstab' logger.exe('sudo chmod 777 {0}'.format(file_to_open)) with open(file_to_open, 'r') as f: for line in f: if not "/mnt" in line: fstab += line with open(file_to_open, 'w') as f: f.write(fstab) logger.exe('sudo chmod 644 {0}'.format(file_to_open)) # Create a list of devices devices = glob.glob('/dev/xvd*') devices.remove('/dev/xvda1') devices.sort() logger.info('Unformatted devices: {0}'.format(devices)) # Check if there are enough drives to start a RAID set if len(devices) > 1: time.sleep(3) # was at 20 mnt_point = mount_raid(devices) # Not enough drives to RAID together. else: mnt_point = format_xfs(devices) if not options.raidonly: # Change cassandra.yaml to point to the new data directories with open(os.path.join(config_data['conf_path'], 'cassandra.yaml'), 'r') as f: yaml = f.read() yaml = yaml.replace('/var/lib/cassandra/data', os.path.join(mnt_point, 'cassandra', 'data')) yaml = yaml.replace( '/var/lib/cassandra/saved_caches', os.path.join(mnt_point, 'cassandra', 'saved_caches')) yaml = yaml.replace('/var/lib/cassandra/commitlog', os.path.join(mnt_point, 'cassandra', 'commitlog')) with open(os.path.join(config_data['conf_path'], 'cassandra.yaml'), 'w') as f: f.write(yaml) # Never create raid array again conf.set_config("AMI", "RAIDAttempted", True) logger.info("Mounted Raid.\n") conf.set_config("AMI", "MountDirectory", mnt_point) conf.set_config("AMI", "CurrentStatus", "Raiding complete")
def use_ec2_userdata(): if not options: exit_path("No parsed options found.") if not options.totalnodes: exit_path("Missing required --totalnodes (-n) switch.") if (options.analyticsnodes + options.searchnodes) > options.totalnodes: exit_path("Total nodes assigned (--analyticsnodes + --searchnodes) > total available nodes (--totalnodes)") if conf.get_config("AMI", "Type") == "Community" and (options.cfsreplication or options.analyticsnodes or options.searchnodes): exit_path('CFS Replication, Analytics Nodes, and Search Node settings can only be set in DataStax Enterprise installs.') if options.email: logger.info('Setting up diagnostic email using: {0}'.format(options.email)) conf.set_config("AMI", "Email", options.email) if options.clustername: logger.info('Using cluster name: {0}'.format(options.clustername)) instance_data['clustername'] = options.clustername if options.customreservation: instance_data['reservationid'] = options.customreservation if options.seeds: instance_data['seeds'] = options.seeds if options.opscenterip: instance_data['opscenterip'] = options.opscenterip if options.stop_services: with open(ami_disabled, 'w') as f: f.write('') options.realtimenodes = (options.totalnodes - options.analyticsnodes - options.searchnodes) options.seed_indexes = [0, options.realtimenodes, options.realtimenodes + options.analyticsnodes] logger.info('Using cluster size: {0}'.format(options.totalnodes)) conf.set_config("Cassandra", "TotalNodes", options.totalnodes) logger.info('Using seed indexes: {0}'.format(options.seed_indexes)) if options.reflector: logger.info('Using reflector: {0}'.format(options.reflector))
def exit_path(errorMsg, append_msg=False): if not append_msg: # Remove passwords from printing: -p p = re.search('(-p\s+)(\S*)', instance_data['userdata']) if p: instance_data['userdata'] = instance_data['userdata'].replace(p.group(2), '****') # Remove passwords from printing: --password p = re.search('(--password\s+)(\S*)', instance_data['userdata']) if p: instance_data['userdata'] = instance_data['userdata'].replace(p.group(2), '****') append_msg = " Aborting installation.\n\nPlease verify your settings:\n{0}".format(instance_data['userdata']) errorMsg += append_msg logger.error(errorMsg) conf.set_config("AMI", "Error", errorMsg) raise AttributeError(errorMsg)
def get_ec2_data(): conf.set_config("AMI", "CurrentStatus", "Installation started") # Try to get EC2 User Data try: req = curl_instance_data('http://169.254.169.254/latest/user-data/') instance_data['userdata'] = get_user_data(req) logger.info("Started with user data set to:") logger.info(instance_data['userdata']) # Trim leading Rightscale UserData instance_data['userdata'] = instance_data['userdata'][instance_data['userdata'].find('--'):] if len(instance_data['userdata']) < 2: raise Exception logger.info("Using user data:") logger.info(instance_data['userdata']) except Exception, e: instance_data['userdata'] = '--totalnodes 1 --version Community --clustername "Test Cluster - No AMI Parameters"' logger.info("No userdata found. Starting 1 node clusters, by default.")
def checkpoint_info(): if options.raidonly: conf.set_config("AMI", "RaidOnly", "True") else: logger.info("Seed list: {0}".format(config_data['seed_list'])) logger.info("OpsCenter: {0}".format(config_data['opscenterseed'])) logger.info("Options: {0}".format(options)) conf.set_config("AMI", "LeadingSeed", config_data['opscenterseed']) conf.set_config("AMI", "CurrentStatus", "Installation complete")
def parse_ec2_userdata(): # Setup parser parser = ArgumentParser() # Option that requires either: Enterprise or Community parser.add_argument("--version", action="store", type=str, dest="version") # Option that specifies how the ring will be divided parser.add_argument("--totalnodes", action="store", type=int, dest="totalnodes") # Option that specifies the cluster's name parser.add_argument("--clustername", action="store", type=str, dest="clustername") # Option that allows for a release version of Enterprise or Community e.g. 1.0.2 parser.add_argument("--release", action="store", type=str, dest="release") # Option that forces the rpc binding to the internal IP address of the instance parser.add_argument("--rpcbinding", action="store_true", dest="rpcbinding", default=False) # Option for multi-region parser.add_argument("--multiregion", action="store_true", dest="multiregion", default=False) parser.add_argument("--seeds", action="store", dest="seeds") parser.add_argument("--opscenterip", action="store", dest="opscenterip") # Option that specifies how the number of Analytics nodes parser.add_argument("--analyticsnodes", action="store", type=int, dest="analyticsnodes") # Option that specifies how the number of Search nodes parser.add_argument("--searchnodes", action="store", type=int, dest="searchnodes") # Option that forces Hadoop analytics nodes over Spark analytics nodes parser.add_argument("--hadoop", action="store_true", dest="hadoop") # Option that specifies the CassandraFS replication factor parser.add_argument("--cfsreplicationfactor", action="store", type=int, dest="cfsreplication") # Option that specifies the username parser.add_argument("--username", action="store", type=str, dest="username") # Option that specifies the password parser.add_argument("--password", action="store", type=str, dest="password") # Option that specifies the installation of OpsCenter on the first node parser.add_argument("--opscenter", action="store", type=str, dest="opscenter") # Option that specifies an alternative reflector.php parser.add_argument("--reflector", action="store", type=str, dest="reflector") # Unsupported dev options # Option that allows for just configuring opscenter parser.add_argument("--opscenteronly", action="store_true", dest="opscenteronly") # Option that allows for just configuring RAID0 on the attached drives parser.add_argument("--raidonly", action="store_true", dest="raidonly") # Option that allows for an OpsCenter to enable the SSL setting parser.add_argument("--opscenterssl", action="store_true", dest="opscenterssl") # Option that enforces a bootstrapping node parser.add_argument("--bootstrap", action="store_true", dest="bootstrap", default=False) # Option that enforces vnodes parser.add_argument("--vnodes", action="store_true", dest="vnodes", default=False) # Option that allows for an emailed report of the startup diagnostics parser.add_argument("--email", action="store", type=str, dest="email") # Option that allows heapsize to be changed parser.add_argument("--heapsize", action="store", type=str, dest="heapsize") # Option that allows an interface port for OpsCenter to be set parser.add_argument("--opscenterinterface", action="store", type=str, dest="opscenterinterface") # Option that allows a custom reservation id to be set parser.add_argument("--customreservation", action="store", type=str, dest="customreservation") # Option that allows custom scripts to be executed parser.add_argument("--base64postscript", action="store", type=str, dest="base64postscript") # Option that allows to download and execute a custom script parser.add_argument("--postscript_url", action="store", type=str, dest="postscript_url") # Grab provided reflector through provided userdata global options try: (options, unknown) = parser.parse_known_args(shlex.split(instance_data['userdata'])) except: exit_path("One of the options was not set correctly.") if not options.analyticsnodes: options.analyticsnodes = 0 if not options.searchnodes: options.searchnodes = 0 if os.path.isfile('/etc/datastax_ami.conf'): if options.version and options.version.lower() == "community": logger.error("The Dynamic DataStax AMI will automatically install DataStax Enterprise.") conf.set_config("AMI", "Type", "Enterprise") else: if options.version: if options.version.lower() == "community": conf.set_config("AMI", "Type", "Community") elif options.version.lower() == "enterprise": conf.set_config("AMI", "Type", "Enterprise") else: exit_path("Invalid --version (-v) argument.")
def parse_ec2_userdata(): # Setup parser parser = ArgumentParser() # Option that requires either: Enterprise or Community parser.add_argument("--version", action="store", type=str, dest="version") # Option that specifies how the ring will be divided parser.add_argument("--totalnodes", action="store", type=int, dest="totalnodes") # Option that specifies the cluster's name parser.add_argument("--clustername", action="store", type=str, dest="clustername") # Option that allows for a release version of Enterprise or Community e.g. 1.0.2 parser.add_argument("--release", action="store", type=str, dest="release") # Option that forces the rpc binding to the internal IP address of the instance parser.add_argument("--rpcbinding", action="store_true", dest="rpcbinding", default=False) # How to make use of EBS if available. unused/data/commitlog/all parser.add_argument("--ebs-role", action="store", type=str, dest="ebs", default="unused") # How to make use of ephemeral store. unused/data/commitlog/all parser.add_argument("--ephemeral-role", action="store", type=str, dest="ephemeral", default="all") # Option for multi-region parser.add_argument("--multiregion", action="store_true", dest="multiregion", default=False) parser.add_argument("--seeds", action="store", dest="seeds") parser.add_argument("--opscenterip", action="store", dest="opscenterip") # Option that specifies how the number of Analytics nodes parser.add_argument("--analyticsnodes", action="store", type=int, dest="analyticsnodes") # Option that specifies how the number of Search nodes parser.add_argument("--searchnodes", action="store", type=int, dest="searchnodes") # Option that forces Hadoop analytics nodes over Spark analytics nodes parser.add_argument("--hadoop", action="store_true", dest="hadoop") # Option that specifies the CassandraFS replication factor parser.add_argument("--cfsreplicationfactor", action="store", type=int, dest="cfsreplication") # Option that specifies the username parser.add_argument("--username", action="store", type=str, dest="username") # Option that specifies the password parser.add_argument("--password", action="store", type=str, dest="password") # Option that specifies the installation of OpsCenter on the first node parser.add_argument("--opscenter", action="store", type=str, dest="opscenter") # Option that specifies an alternative reflector.php parser.add_argument("--reflector", action="store", type=str, dest="reflector") # Unsupported dev options # Option that allows for just configuring opscenter parser.add_argument("--opscenteronly", action="store_true", dest="opscenteronly") # Option that allows for just configuring RAID0 on the attached drives parser.add_argument("--raidonly", action="store_true", dest="raidonly") # Option that allows for an OpsCenter to enable the SSL setting parser.add_argument("--opscenterssl", action="store_true", dest="opscenterssl") # Option that enforces a bootstrapping node parser.add_argument("--bootstrap", action="store_true", dest="bootstrap", default=False) # Option that enforces vnodes parser.add_argument("--vnodes", action="store_true", dest="vnodes", default=False) # Option that allows for an emailed report of the startup diagnostics parser.add_argument("--email", action="store", type=str, dest="email") # Option that allows heapsize to be changed parser.add_argument("--heapsize", action="store", type=str, dest="heapsize") # Option that allows an interface port for OpsCenter to be set parser.add_argument("--opscenterinterface", action="store", type=str, dest="opscenterinterface") # Option that allows a custom reservation id to be set parser.add_argument("--customreservation", action="store", type=str, dest="customreservation") # Option that allows custom scripts to be executed parser.add_argument("--base64postscript", action="store", type=str, dest="base64postscript") # Option that allows to download and execute a custom script parser.add_argument("--postscript_url", action="store", type=str, dest="postscript_url") # Option that stops scylla-server on instance startup parser.add_argument("--stop-services", action="store_true", dest="stop_services", default=False) # Option that enables developer mode parser.add_argument("--developer-mode", action="store_true", dest="developer_mode", default=False) # Grab provided reflector through provided userdata global options try: (options, unknown) = parser.parse_known_args( shlex.split(instance_data['userdata'])) except: exit_path("One of the options was not set correctly.") if not options.analyticsnodes: options.analyticsnodes = 0 if not options.searchnodes: options.searchnodes = 0 if os.path.isfile('/etc/datastax_ami.conf'): if options.version and options.version.lower() == "community": logger.error( "The Dynamic DataStax AMI will automatically install DataStax Enterprise." ) conf.set_config("AMI", "Type", "Enterprise") else: if options.version: if options.version.lower() == "community": conf.set_config("AMI", "Type", "Community") elif options.version.lower() == "enterprise": conf.set_config("AMI", "Type", "Enterprise") else: exit_path("Invalid --version (-v) argument.") valid_block_options = ["unused", "data", "commitlog", "all"] if not options.ebs in valid_block_options: exit_path("Invalid option for EBS placement: %s" % (options.ebs)) if not options.ephemeral in valid_block_options: exit_path("Invalid option for ephemeral placement: %s" % (options.ephemeral)) # both set to the same thing - can't respect that. if options.ebs == options.ephemeral: exit_path( "Invalid option for disk placement: both ebs and ephemeral equal at %s" % (options.ebs)) conf.set_config("AMI", "ebs_role", options.ebs) conf.set_config("AMI", "ephemeral_role", options.ephemeral)
def mount_raid(devices): # Make sure the devices are umounted, then run fdisk on each device logger.info( 'Clear "invalid flag 0x0000 of partition table 4" by issuing a write, then running fdisk on each device...' ) formatCommands = "echo 'n\np\n1\n\n\nt\nfd\nw'" for device in devices: logger.info('Confirming devices are not mounted:') logger.exe('sudo umount {0}'.format(device), expectError=True) logger.pipe("echo 'w'", 'sudo fdisk -c -u {0}'.format(device)) logger.pipe(formatCommands, 'sudo fdisk -c -u {0}'.format(device)) # Create a list of partitions to RAID logger.exe('sudo fdisk -l') partitions = glob.glob('/dev/xvd*[0-9]') if '/dev/xvda1' in partitions: partitions.remove('/dev/xvda1') partitions.sort() logger.info( 'Partitions about to be added to RAID0 set: {0}'.format(partitions)) # Make sure the partitions are umounted and create a list string partion_list = '' for partition in partitions: logger.info('Confirming partitions are not mounted:') logger.exe('sudo umount ' + partition, expectError=True) partion_list = ' '.join(partitions).strip() logger.info('Creating the RAID0 set:') time.sleep(3) # was at 10 conf.set_config("AMI", "CurrentStatus", "Raid creation") # Continuously create the Raid device, in case there are errors raid_created = False while not raid_created: logger.exe( 'sudo mdadm --create /dev/md0 --chunk=256 --level=0 --raid-devices={0} {1}' .format(len(partitions), partion_list), expectError=True) raid_created = True logger.pipe('echo DEVICE {0}'.format(partion_list), 'sudo tee /etc/mdadm/mdadm.conf') time.sleep(5) # New parsing and elimination of the name= field due to 12.04's new RAID'ing methods response = logger.exe('sudo mdadm --examine --scan')[0] response = ' '.join(response.split(' ')[0:-1]) with open('/etc/mdadm/mdadm.conf', 'a') as f: f.write(response) logger.exe('sudo update-initramfs -u') time.sleep(10) conf.set_config('AMI', 'raid_readahead', 128) logger.exe('sudo blockdev --setra %s /dev/md0' % (conf.get_config('AMI', 'raid_readahead'))) logger.info('Formatting the RAID0 set:') time.sleep(10) raidError = logger.exe('sudo mkfs.xfs -f /dev/md0', expectError=True)[1] if raidError: logger.exe('sudo mdadm --stop /dev/md_d0', expectError=True) logger.exe('sudo mdadm --zero-superblock /dev/sdb1', expectError=True) raid_created = False # Configure fstab and mount the new RAID0 device mnt_point = '/raid0' logger.exe('sudo mkdir {0}'.format(mnt_point)) create_cassandra_directories(mnt_point=mnt_point, device='/dev/md0') logger.info('Showing RAID0 details:') logger.exe('cat /proc/mdstat') logger.exe('sudo mdadm --detail /dev/md0') # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html logger.pipe('echo "30720"', 'sudo tee /proc/sys/dev/raid/speed_limit_min') return mnt_point
def use_ec2_userdata(): if not options: exit_path("EC2 User Data must be set for the DataStax AMI to run.") if not options.totalnodes: exit_path("Missing required --totalnodes (-n) switch.") if (options.analyticsnodes + options.searchnodes) > options.totalnodes: exit_path( "Total nodes assigned (--analyticsnodes + --searchnodes) > total available nodes (--totalnodes)" ) if options.javaversion: if options.javaversion.lower() == '1.7': conf.set_config("AMI", "JavaType", "1.7") else: conf.set_config("AMI", "JavaType", "1.6") if options.version: if options.version.lower() == "community": conf.set_config("AMI", "Type", "Community") elif options.version.lower() == "enterprise": conf.set_config("AMI", "Type", "Enterprise") else: exit_path("Invalid --version (-v) argument.") else: exit_path("Missing required --version (-v) switch.") if conf.get_config("AMI", "Type") == "Community" and (options.cfsreplication or options.analyticsnodes or options.searchnodes): exit_path( 'CFS Replication, Vanilla Nodes, and adding an Analytic Node settings can only be set in DataStax Enterprise installs.' ) if options.email: logger.info('Setting up diagnostic email using: {0}'.format( options.email)) conf.set_config("AMI", "Email", options.email) if options.clustername: logger.info('Using cluster name: {0}'.format(options.clustername)) instance_data['clustername'] = options.clustername if options.customreservation: instance_data['reservationid'] = options.customreservation logger.info('Using cluster size: {0}'.format(options.totalnodes)) conf.set_config("Cassandra", "TotalNodes", options.totalnodes) logger.info('Using seed indexes: {0}'.format(options.seed_indexes)) if options.reflector: logger.info('Using reflector: {0}'.format(options.reflector))
def clean_installation(): logger.info('Performing deployment install...') if conf.get_config("AMI", "Type") == "Community": if options.release and options.release.startswith('1.0'): cassandra_release = options.release if cassandra_release == '1.0.11-1': cassandra_release = '1.0.11' logger.exe( 'sudo apt-get install -y python-cql cassandra={0} dsc={1}'. format(cassandra_release, options.release)) conf.set_config('AMI', 'package', 'dsc') conf.set_config('Cassandra', 'partitioner', 'random_partitioner') elif options.release and options.release.startswith('1.1'): dsc_release = cassandra_release = options.release if dsc_release in ['1.1.6', '1.1.7', '1.1.9']: dsc_release = dsc_release + '-1' logger.exe( 'sudo apt-get install -y python-cql cassandra={0} dsc1.1={1}'. format(cassandra_release, dsc_release)) conf.set_config('AMI', 'package', 'dsc1.1') conf.set_config('Cassandra', 'partitioner', 'random_partitioner') elif options.release and options.release.startswith('1.2'): dsc_release = cassandra_release = options.release dsc_release = dsc_release + '-1' logger.exe( 'sudo apt-get install -y python-cql cassandra={0} dsc12={1}'. format(cassandra_release, dsc_release)) conf.set_config('AMI', 'package', 'dsc12') conf.set_config('Cassandra', 'partitioner', 'murmur') conf.set_config('Cassandra', 'vnodes', 'True') else: logger.exe('sudo apt-get install -y python-cql dsc20') conf.set_config('AMI', 'package', 'dsc12') conf.set_config('Cassandra', 'partitioner', 'murmur') conf.set_config('Cassandra', 'vnodes', 'True') # logger.exe('sudo apt-get install -y dsc-demos') logger.exe('sudo service cassandra stop') elif conf.get_config("AMI", "Type") == "Enterprise": if options.release: install_list = 'sudo apt-get install -y dse-full={0} dse={0} dse-demos={0} dse-hive={0} dse-libcassandra={0} dse-libhadoop={0} dse-libhive={0} dse-libpig={0} dse-pig={0}' if options.release.startswith('1'): logger.exe(install_list.format(options.release)) conf.set_config('AMI', 'package', 'dse-full') conf.set_config('Cassandra', 'partitioner', 'random_partitioner') elif options.release.startswith('2'): install_list += ' dse-liblog4j={0} dse-libsolr={0} dse-libsqoop={0} dse-libtomcat={0}' if options.release.startswith( '2.1') or options.release.startswith('2.2'): install_list += ' dse-libmahout={0}' logger.exe(install_list.format(options.release)) conf.set_config('AMI', 'package', 'dse-full') conf.set_config('Cassandra', 'partitioner', 'random_partitioner') elif options.release.startswith('3'): install_list += ' dse-liblog4j={0} dse-libsolr={0} dse-libsqoop={0} dse-libtomcat={0} dse-libmahout={0} dse-libhadoop-native={0}' logger.exe(install_list.format(options.release)) conf.set_config('AMI', 'package', 'dse-full') if options.release.startswith('3.0'): conf.set_config('Cassandra', 'partitioner', 'random_partitioner') else: conf.set_config('Cassandra', 'partitioner', 'murmur') conf.set_config('Cassandra', 'vnodes', 'False') else: exit_path( "--release should be in the format similar to `1.0.2-1` or `2.0`." ) else: logger.exe('sudo apt-get install -y dse-full') conf.set_config('AMI', 'package', 'dse-full') conf.set_config('Cassandra', 'partitioner', 'murmur') conf.set_config('Cassandra', 'vnodes', 'False') logger.exe('sudo service dse stop') # Remove the presaved information from startup logger.exe('sudo rm -rf /var/lib/cassandra') logger.exe('sudo rm -rf /var/log/cassandra') logger.exe('sudo mkdir -p /var/lib/cassandra') logger.exe('sudo mkdir -p /var/log/cassandra') logger.exe('sudo chown -R cassandra:cassandra /var/lib/cassandra') logger.exe('sudo chown -R cassandra:cassandra /var/log/cassandra')
def mount_raid(devices): # Make sure the devices are umounted, then run fdisk on each device logger.info( 'Clear "invalid flag 0x0000 of partition table 4" by issuing a write, then running fdisk on each device...' ) formatCommands = "echo 'n\np\n1\n\n\nt\nfd\nw'" for device in devices: logger.info('Confirming devices are not mounted:') logger.exe('sudo umount {0}'.format(device), False) logger.pipe("echo 'w'", 'sudo fdisk -c -u {0}'.format(device)) logger.pipe(formatCommands, 'sudo fdisk -c -u {0}'.format(device)) # Create a list of partitions to RAID logger.exe('sudo fdisk -l') partitions = glob.glob('/dev/xvd*[0-9]') partitions.remove('/dev/xvda1') partitions.sort() logger.info( 'Partitions about to be added to RAID0 set: {0}'.format(partitions)) # Make sure the partitions are umounted and create a list string partion_list = '' for partition in partitions: logger.info('Confirming partitions are not mounted:') logger.exe('sudo umount ' + partition, False) partion_list = ' '.join(partitions).strip() logger.info('Creating the RAID0 set:') time.sleep(3) # was at 10 conf.set_config("AMI", "CurrentStatus", "Raid creation") # Continuously create the Raid device, in case there are errors raid_created = False while not raid_created: logger.exe( 'sudo mdadm --create /dev/md0 --chunk=256 --level=0 --raid-devices={0} {1}' .format(len(partitions), partion_list), expectError=True) raid_created = True logger.pipe('echo DEVICE {0}'.format(partion_list), 'sudo tee /etc/mdadm/mdadm.conf') time.sleep(5) # New parsing and elimination of the name= field due to 12.04's new RAID'ing methods response = logger.exe('sudo mdadm --examine --scan')[0] response = ' '.join(response.split(' ')[0:-1]) with open('/etc/mdadm/mdadm.conf', 'a') as f: f.write(response) logger.exe('sudo update-initramfs -u') time.sleep(10) conf.set_config('AMI', 'raid_readahead', 512) logger.exe('sudo blockdev --setra %s /dev/md0' % (conf.get_config('AMI', 'raid_readahead'))) logger.info('Formatting the RAID0 set:') time.sleep(10) raidError = logger.exe('sudo mkfs.xfs -f /dev/md0', expectError=True)[1] if raidError: logger.exe('sudo mdadm --stop /dev/md_d0', expectError=True) logger.exe('sudo mdadm --zero-superblock /dev/sdb1', expectError=True) raid_created = False # Configure fstab and mount the new RAID0 device mnt_point = '/raid0' logger.pipe( "echo '/dev/md0\t{0}\txfs\tdefaults,nobootwait,noatime\t0\t0'".format( mnt_point), 'sudo tee -a /etc/fstab') logger.exe('sudo mkdir {0}'.format(mnt_point)) logger.exe('sudo mount -a') logger.exe('sudo mkdir -p {0}'.format(os.path.join(mnt_point, 'cassandra'))) if conf.get_config("AMI", "RaidOnly"): logger.pipe( 'yes', 'sudo adduser --no-create-home --disabled-password cassandra') while True: output = logger.exe('id cassandra') if not output[1] and not 'no such user' in output[0].lower(): break time.sleep(1) logger.exe('sudo chown -R cassandra:cassandra {0}'.format( os.path.join(mnt_point, 'cassandra'))) # Create symlink for Cassandra logger.exe('sudo rm -rf /var/lib/cassandra') logger.exe('sudo ln -s {0} /var/lib/cassandra'.format( os.path.join(mnt_point, 'cassandra'))) logger.exe('sudo chown -R cassandra:cassandra /var/lib/cassandra') logger.info('Showing RAID0 details:') logger.exe('cat /proc/mdstat') logger.exe('echo "15000" > /proc/sys/dev/raid/speed_limit_min') logger.exe('sudo mdadm --detail /dev/md0') return mnt_point
def clean_installation(): logger.info('Performing deployment install...') # Hold onto baked limits conf before installation logger.exe( 'sudo mv /etc/security/limits.d/cassandra.conf /etc/security/limits.d/cassandra.conf.bak' ) if conf.get_config("AMI", "Type") == "Community": if options.release and options.release.startswith('1.0'): cassandra_release = options.release if cassandra_release == '1.0.11-1': cassandra_release = '1.0.11' logger.exe( 'sudo apt-get install -y python-cql datastax-agent cassandra={0} dsc={1}' .format(cassandra_release, options.release)) conf.set_config('AMI', 'package', 'dsc') conf.set_config('Cassandra', 'partitioner', 'random_partitioner') elif options.release and options.release.startswith('1.1'): dsc_release = cassandra_release = options.release if not dsc_release in ['1.1.1', '1.1.2', '1.1.3', '1.1.5']: dsc_release = dsc_release + '-1' logger.exe( 'sudo apt-get install -y python-cql datastax-agent cassandra={0} dsc1.1={1}' .format(cassandra_release, dsc_release)) conf.set_config('AMI', 'package', 'dsc1.1') conf.set_config('Cassandra', 'partitioner', 'random_partitioner') elif options.release and options.release.startswith('1.2'): dsc_release = cassandra_release = options.release dsc_release = dsc_release + '-1' logger.exe( 'sudo apt-get install -y python-cql datastax-agent cassandra={0} dsc12={1}' .format(cassandra_release, dsc_release)) conf.set_config('AMI', 'package', 'dsc12') conf.set_config('Cassandra', 'partitioner', 'murmur') conf.set_config('Cassandra', 'vnodes', 'True') elif options.release and options.release.startswith('2.0'): dsc_release = cassandra_release = options.release dsc_release = dsc_release + '-1' logger.exe( 'sudo apt-get install -y python-cql datastax-agent cassandra={0} dsc20={1}' .format(cassandra_release, dsc_release)) conf.set_config('AMI', 'package', 'dsc20') conf.set_config('Cassandra', 'partitioner', 'murmur') conf.set_config('Cassandra', 'vnodes', 'True') else: logger.exe( 'sudo apt-get install -y python-cql datastax-agent dsc20') conf.set_config('AMI', 'package', 'dsc20') conf.set_config('Cassandra', 'partitioner', 'murmur') conf.set_config('Cassandra', 'vnodes', 'True') logger.exe('sudo service cassandra stop') elif conf.get_config("AMI", "Type") == "Enterprise": config_data['conf_path'] = os.path.expanduser("/etc/dse/cassandra/") if options.release: install_list = 'sudo apt-get install -y dse-full={0} dse={0} dse-demos={0} dse-hive={0} dse-libcassandra={0} dse-libhadoop={0} dse-libhive={0} dse-libpig={0} dse-pig={0}' if options.release.startswith('1'): logger.exe(install_list.format(options.release)) conf.set_config('AMI', 'package', 'dse-full') conf.set_config('Cassandra', 'partitioner', 'random_partitioner') elif options.release.startswith('2'): install_list += ' dse-liblog4j={0} dse-libsolr={0} dse-libsqoop={0} dse-libtomcat={0}' if options.release.startswith( '2.1') or options.release.startswith('2.2'): install_list += ' dse-libmahout={0}' logger.exe(install_list.format(options.release)) conf.set_config('AMI', 'package', 'dse-full') conf.set_config('Cassandra', 'partitioner', 'random_partitioner') elif options.release.startswith('3'): install_list += ' dse-liblog4j={0} dse-libsolr={0} dse-libsqoop={0} dse-libtomcat={0} dse-libmahout={0} dse-libhadoop-native={0}' logger.exe(install_list.format(options.release)) conf.set_config('AMI', 'package', 'dse-full') if options.release.startswith('3.0'): conf.set_config('Cassandra', 'partitioner', 'random_partitioner') else: conf.set_config('Cassandra', 'partitioner', 'murmur') conf.set_config('Cassandra', 'vnodes', 'False') else: exit_path( "--release should be in the format similar to `1.0.2-1` or `2.0`." ) else: logger.exe('sudo apt-get install -y dse-full') conf.set_config('AMI', 'package', 'dse-full') conf.set_config('Cassandra', 'partitioner', 'murmur') conf.set_config('Cassandra', 'vnodes', 'False') logger.exe('sudo service dse stop') # Remove the presaved information from startup logger.exe('sudo rm -rf /var/lib/cassandra') logger.exe('sudo rm -rf /var/log/cassandra') logger.exe('sudo mkdir -p /var/lib/cassandra') logger.exe('sudo mkdir -p /var/log/cassandra') logger.exe('sudo chown -R cassandra:cassandra /var/lib/cassandra') logger.exe('sudo chown -R cassandra:cassandra /var/log/cassandra') # Replace baked image conf after installation logger.exe( 'sudo mv /etc/security/limits.d/cassandra.conf.bak /etc/security/limits.d/cassandra.conf' )
def get_seed_list(): # Read seed list from reflector index_set = set(options.seed_indexes) if options.totalnodes in index_set: index_set.remove(options.totalnodes) expected_responses = len(index_set) time_in_loop = time.time() continue_loop = True logger.info('Reflector loop...') while continue_loop: if time.time() - time_in_loop > 10 * 60: exit_path('EC2 is experiencing some issues and has not allocated all of the resources in under 10 minutes.', '\n\nAborting the clustering of this reservation. Please try again.') if options.reflector: reflector = options.reflector else: reflector = 'http://reflector.scylladb.com/reflector.php' req = urllib2.Request('{0}?indexid={1}&reservationid={2}&internalip={3}&externaldns={4}&second_seed_index={5}&third_seed_index={6}'.format( reflector, instance_data['launchindex'], instance_data['reservationid'], instance_data['internalip'], instance_data['publichostname'], options.seed_indexes[1], options.seed_indexes[2] )) req.add_header('User-agent', 'DataStaxSetup') try: response = urllib2.urlopen(req).read() response = json.loads(response) status = "{0} Reflector: Received {1} of {2} responses from: {3}".format( time.strftime("%m/%d/%y-%H:%M:%S", time.localtime()), response['number_of_returned_ips'], expected_responses, response['seeds'] ) conf.set_config("AMI", "CurrentStatus", status) logger.info(status) if response['number_of_returned_ips'] == expected_responses: conf.set_config("OpsCenter", "DNS", response['opscenter_dns']) config_data['seed_list'] = set(response['seeds']) config_data['opscenterseed'] = response['seeds'][0] continue_loop = False else: time.sleep(2 + random.randint(0, options.totalnodes / 4 + 1)) except: if expected_responses == 1: conf.set_config("AMI", "CurrentStatus", "Bypassing reflector for 1 node cluster...") conf.set_config("OpsCenter", "DNS", instance_data['publichostname']) config_data['seed_list'] = set([instance_data['internalip']]) config_data['opscenterseed'] = instance_data['internalip'] continue_loop = False traceback.print_exc(file=sys.stdout) time.sleep(2 + random.randint(0, 5))