.. _lcm-walkthru: Cluster Install Walk-through ============================ This is a step-by-step sequence of API calls that simulate a typical zero-to-cluster installation workflow scenario. #. Create a :ref:`lcm-repository` #. Create a :ref:`lcm-machine-credential` with appropriate settings for your cluster. You may need to create multiple if you have disparate credentials across your cluster. #. Create a :ref:`lcm-config-profile` with appropriate configuration settings where you wish to override the defaults. If you wish to vary your configs for each datacenter or even at the node-level, you will need to create multiple. #. Create a :ref:`lcm-cluster` model, including datacenters and nodes (see :ref:`lcm-datacenter` and :ref:`lcm-node`). Assign repository, machine credentials, and config profiles to the appropriate cluster model objects. #. Create an :ref:`lcm-install-job` job. #. Watch the job (see :ref:`lcm-jobs`) for status. Example: .. code-block:: python #!/usr/bin/env python # (C) DataStax, 2015. All Rights Reserved # # Demonstrates installation of DataStax Community on existing empty servers # using the LCM API # # Example Usage: # node-install-demo.py (target node ip one) (target node ip two) (...) import sys import os import requests import json import threading # CONFIGURABLE GLOBAL VARIABLES server_ip = "127.0.0.1" base_url = 'http://%s:8888/api/v1/lcm/' % server_ip homedir = os.getenv("HOME") # private_key_path = os.path.join(homedir, ".ssh/id_rsa") # private_key = open(private_key_path).read() # HELPER FUNCTIONS def do_post(url, post_data): """Convenience function to simplify posting data to the LCM api.""" print "Posting to:", url result = requests.post(base_url + url, data=json.dumps(post_data), headers={'Content-Type': 'application/json'}) result_data = json.loads(result.text) print "Response is:", result.text return result_data # CLUSTER SETUP # Credentials to log-in to the DSE nodes over SSH and obtain root privileges # # Log in over SSH as root authenticating with a password # Run commands directly, without su or sudo # machine_credential = do_post("machine_credentials/", # {"name": "mc11", # "login-user": "root", # "login-password": "root", # "become-mode": "direct"}) # Log in over SSH as vagrant, authenticating with an ssh-key # Run commands through su when root privileges are necessary # machine_credential = do_post("provisioning/machine_credentials/", # {"name": "mc11", # "login-user": "vagrant", # "ssh-private-key": private_key, # "become-mode": "su", # "become-user": "root", # "become-password": "password"}) # Log in over SSH as ubuntu, authenticating with a password # Run commands through sudo when root privileges are necessary machine_credential = do_post("machine_credentials/", {"name": "mc11", "login-user": "johndoe", "login-password": "foobie", "become-mode": "sudo", "become-password": "foobie"}) # Describes many DSE settings and LCM options to the LCM api. # Other than the name and datastax-version, all attributes are optional # and have sensible defaults. cluster_profile = do_post( "config_profiles/", {"name": "cp1", "datastax-version": "4.8.1", "json": {'cassandra-yaml': {"start-native-transport": True}, 'logback-xml': {'file-appender-min-index': 2, 'loggers': [{'name': 'com.thinkaurelius.thrift', 'level': 'ERROR'}]}, 'java-setup': {'major-version': "1.8.0" # 'update-version': "77", # 'build-version': '02' }, 'package-proxy': { 'enabled': False # 'protocol': 'http', # 'host': 'proxy.lan', # 'port': '3128' # 'authentication-required': False, # 'username': 'user', # 'password': 'pw' }}, "comment": "no comment"}) repository = do_post( "repositories/", {"name": "my-repo-credentials", "username": "johndoe", "password": "foobie" }) # Describes a cluster to the LCM api. cluster = do_post("clusters/", {"name": "cluster01", "machine-credential-id": machine_credential["id"], "repository-id": repository["id"], "config-profile-id": cluster_profile["id"]}) # Describes a data-center to the LCM api. # Machine-credentials, repository-credentials, and config-profiles can also be # assigned to datacenters, or will be inherited from the cluster if unset. datacenter = do_post("datacenters/", {"name": "datacenter01", "cluster-id": cluster["id"]}) # Describes a list of nodes to the LCM api using ip-addresses specified as # command-line arguments to this script, ie: # node-install-demo.py 10.0.1.10 10.0.1.11 ip_addresses = sys.argv[1:] nodes = [do_post("nodes/", {"name": ip, "ssh-management-address": ip, "datacenter-id": datacenter["id"]}) for ip in ip_addresses] # Request an install job to execute the installation and configuration of the # cluster. Until this point, we've been describing future state. Now LCM will # execute the changes necessary to achieve that state. install_job = do_post("actions/install", {"job-scope": "cluster", "resource-id": cluster["id"]}) # Poll the LCM api for updates on the status of the job and progress events displayed_events = {} def check_job_status(job_id, displayed_events): # Fetch the most recent 200 job-events url = "job_events/?job-id=%s&per-page=200" % job_id result = requests.get(base_url + url) # Display events if we haven't seen them before search_results = json.loads(result.text) for search_result in search_results['results']: job_event_result = requests.get(search_result['href']) job_event = json.loads(job_event_result.text) if job_event['id'] not in displayed_events: displayed_events[job_event['id']] = True # Job events are extremely detailed, select some of the more # human-friendly fields for display print "Node %s: %s %s (%s): %s" % (job_event['node-id'], job_event['event-type'], job_event['event-subtype'], job_event['message'], job_event['event-resource']) # Check if the entire job has been completed job_url = "jobs/%s" % job_id job_response = requests.get(base_url + job_url) job_results = json.loads(job_response.text) # Poll every 5 seconds, displaying status until the job is complete if job_results['status'] == 'RUNNING': t = threading.Timer(5.0, check_job_status, [job_id, displayed_events]) t.start() elif job_results['status'] == 'COMPLETE': print "Job completed successfully." # Initiate status polling t = threading.Timer(5.0, check_job_status, [install_job["id"], displayed_events]) t.start()