Skip to content

Instantly share code, notes, and snippets.

@gpetrousov
Last active April 7, 2025 19:42

Revisions

  1. gpetrousov renamed this gist Apr 7, 2025. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. gpetrousov revised this gist Mar 24, 2025. 1 changed file with 43 additions and 68 deletions.
    111 changes: 43 additions & 68 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -1,110 +1,85 @@
    import requests
    import json

    # Disable SSL nags
    requests.packages.urllib3.disable_warnings()


    def get_proxmox_ticket(username, password, api_url):
    """Retrieves a Proxmox API ticket and cookie."""
    auth_data = {
    'username': username,
    'password': password
    }
    auth_response = requests.post(f'{api_url}/access/ticket', json=auth_data, verify=False)
    auth_response.raise_for_status()
    ticket = auth_response.json()['data']['ticket']
    csrf_token = auth_response.json()['data']['CSRFPreventionToken']
    # cookie = auth_response.cookies['PVEAuthCookie']
    return ticket, csrf_token


    def create_proxmox_cluster(hostname, username, password, api_url):
    """Creates a Proxmox cluster."""
    ticket, csrf_token = get_proxmox_ticket(username, password, api_url)

    headers = {
    'CSRFPreventionToken': csrf_token,
    'Content-Type': 'application/x-www-form-urlencoded',
    }
    cookies = {'PVEAuthCookie': ticket}

    data = {
    'clustername': "Experimental",
    'link0': hostname,
    }

    create_response = requests.post(f'{api_url}/cluster/config/', headers=headers, cookies=cookies, data=data, verify=False)

    # Check if cluster exists
    if create_response.reason == "cluster config '/etc/pve/corosync.conf' already exists":
    return "Cluster exists"
    else:
    return create_response.json()


    node1_hostname = "<IP of node 1>"
    # Node1 data
    node1_hostname = "10.1.1.135"
    node1_username = "root@pam"
    node1_password = "<SSH password>"
    node1_password = "toortoor"
    node1_api_url = f"https://{node1_hostname}:8006/api2/json"

    node2_hostname = "IP of node 2"
    # Node2 data
    node2_hostname = "10.1.1.136"
    node2_username = "root@pam"
    node2_password = "<SSH password>"
    node2_password = "toortoor"
    node2_api_url = f"https://{node2_hostname}:8006/api2/json"

    # 1. Create the cluster on node1
    create_result = create_proxmox_cluster(node1_hostname, node1_username, node1_password, node1_api_url)
    print(f"Cluster creation result: {json.dumps(create_result, indent=2)}")

    # 2. Get join information from node1
    auth_data_node1 = {
    "username": node1_username,
    "password": node1_password
    # 0. Create ticket
    node1_auth_data = {
    'username': node1_username,
    'password': node1_password
    }
    auth_response_node1 = requests.post(f"{node1_api_url}/access/ticket", json=auth_data_node1, verify=False)
    auth_response_node1.raise_for_status()
    ticket_node1 = auth_response_node1.json()["data"]["ticket"]
    csrf_token_node1 = auth_response_node1.json()["data"]["CSRFPreventionToken"]
    auth_response = requests.post(f'{node1_api_url}/access/ticket', json=node1_auth_data, verify=False)
    auth_response.raise_for_status()
    node1_ticket = auth_response.json()['data']['ticket']
    csrf_token = auth_response.json()['data']['CSRFPreventionToken']

    headers_node1 = {
    'CSRFPreventionToken': csrf_token_node1,
    # 1. Create the cluster on node1
    node1_headers = {
    'CSRFPreventionToken': csrf_token,
    'Content-Type': 'application/x-www-form-urlencoded',
    }
    cookies_nodes1 = {'PVEAuthCookie': ticket_node1}
    node1_cookies = {'PVEAuthCookie': node1_ticket}

    cluster_data = {
    'clustername': "Experimetal",
    'link0': node1_hostname,
    }

    create_response = requests.post(f'{node1_api_url}/cluster/config/', headers=node1_headers, cookies=node1_cookies, data=cluster_data, verify=False)

    # Check if cluster exists
    # status_response = requests.get(f'{node1_api_url}/cluster/config/nodes/', headers=node1_headers, cookies=node1_cookies, verify=False)
    # print(status_response.json())

    if create_response.reason == "cluster config '/etc/pve/corosync.conf' already exists":
    print("Cluster exists")
    else:
    print(create_response.json())

    # 2. Get join information from node1
    print("Getting node1 cluster join info")
    join_info_response = requests.get(f"{node1_api_url}/cluster/config/join", headers=headers_node1, cookies=cookies_nodes1, verify=False)
    join_info_response = requests.get(f"{node1_api_url}/cluster/config/join", headers=node1_headers, cookies=node1_cookies, verify=False)
    join_info_response.raise_for_status()

    join_fingerprint = join_info_response.json()["data"]["nodelist"][0]["pve_fp"]


    # 2.5 Craft request to join existing cluster
    auth_data_node2 = {
    node2_auth_data = {
    "username": node2_username,
    "password": node2_password
    }
    auth_response_node2 = requests.post(f"{node2_api_url}/access/ticket", json=auth_data_node2, verify=False)
    auth_response_node2 = requests.post(f"{node2_api_url}/access/ticket", json=node2_auth_data, verify=False)
    auth_response_node2.raise_for_status()
    ticket_node2 = auth_response_node2.json()["data"]["ticket"]
    node2_ticket = auth_response_node2.json()["data"]["ticket"]
    csrf_token_node2 = auth_response_node2.json()["data"]["CSRFPreventionToken"]

    headers_node2 = {
    node2_headers = {
    'CSRFPreventionToken': csrf_token_node2,
    'Content-Type': 'application/x-www-form-urlencoded',
    }
    data_node2 = {
    node2_data = {
    "fingerprint": join_fingerprint,
    "hostname": node1_hostname,
    "password": node1_password,
    "link0": node2_hostname
    }
    cookies_nodes2 = {'PVEAuthCookie': ticket_node2}
    node2_cookies = {'PVEAuthCookie': node2_ticket}

    # 3. Add node2 to the cluster
    print("Attempting to join")
    join_response = requests.post(f"{node2_api_url}/cluster/config/join", headers=headers_node2, data=data_node2, cookies=cookies_nodes2, verify=False)
    join_response = requests.post(f"{node2_api_url}/cluster/config/join", headers=node2_headers, data=node2_data, cookies=node2_cookies, verify=False)
    join_response.raise_for_status()
    print(join_response.json())
    print("Join done!!")
  3. gpetrousov created this gist Mar 24, 2025.
    110 changes: 110 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,110 @@
    import requests
    import json

    # Disable SSL nags
    requests.packages.urllib3.disable_warnings()


    def get_proxmox_ticket(username, password, api_url):
    """Retrieves a Proxmox API ticket and cookie."""
    auth_data = {
    'username': username,
    'password': password
    }
    auth_response = requests.post(f'{api_url}/access/ticket', json=auth_data, verify=False)
    auth_response.raise_for_status()
    ticket = auth_response.json()['data']['ticket']
    csrf_token = auth_response.json()['data']['CSRFPreventionToken']
    # cookie = auth_response.cookies['PVEAuthCookie']
    return ticket, csrf_token


    def create_proxmox_cluster(hostname, username, password, api_url):
    """Creates a Proxmox cluster."""
    ticket, csrf_token = get_proxmox_ticket(username, password, api_url)

    headers = {
    'CSRFPreventionToken': csrf_token,
    'Content-Type': 'application/x-www-form-urlencoded',
    }
    cookies = {'PVEAuthCookie': ticket}

    data = {
    'clustername': "Experimental",
    'link0': hostname,
    }

    create_response = requests.post(f'{api_url}/cluster/config/', headers=headers, cookies=cookies, data=data, verify=False)

    # Check if cluster exists
    if create_response.reason == "cluster config '/etc/pve/corosync.conf' already exists":
    return "Cluster exists"
    else:
    return create_response.json()


    node1_hostname = "<IP of node 1>"
    node1_username = "root@pam"
    node1_password = "<SSH password>"
    node1_api_url = f"https://{node1_hostname}:8006/api2/json"

    node2_hostname = "IP of node 2"
    node2_username = "root@pam"
    node2_password = "<SSH password>"
    node2_api_url = f"https://{node2_hostname}:8006/api2/json"

    # 1. Create the cluster on node1
    create_result = create_proxmox_cluster(node1_hostname, node1_username, node1_password, node1_api_url)
    print(f"Cluster creation result: {json.dumps(create_result, indent=2)}")

    # 2. Get join information from node1
    auth_data_node1 = {
    "username": node1_username,
    "password": node1_password
    }
    auth_response_node1 = requests.post(f"{node1_api_url}/access/ticket", json=auth_data_node1, verify=False)
    auth_response_node1.raise_for_status()
    ticket_node1 = auth_response_node1.json()["data"]["ticket"]
    csrf_token_node1 = auth_response_node1.json()["data"]["CSRFPreventionToken"]

    headers_node1 = {
    'CSRFPreventionToken': csrf_token_node1,
    'Content-Type': 'application/x-www-form-urlencoded',
    }
    cookies_nodes1 = {'PVEAuthCookie': ticket_node1}

    print("Getting node1 cluster join info")
    join_info_response = requests.get(f"{node1_api_url}/cluster/config/join", headers=headers_node1, cookies=cookies_nodes1, verify=False)
    join_info_response.raise_for_status()

    join_fingerprint = join_info_response.json()["data"]["nodelist"][0]["pve_fp"]


    # 2.5 Craft request to join existing cluster
    auth_data_node2 = {
    "username": node2_username,
    "password": node2_password
    }
    auth_response_node2 = requests.post(f"{node2_api_url}/access/ticket", json=auth_data_node2, verify=False)
    auth_response_node2.raise_for_status()
    ticket_node2 = auth_response_node2.json()["data"]["ticket"]
    csrf_token_node2 = auth_response_node2.json()["data"]["CSRFPreventionToken"]

    headers_node2 = {
    'CSRFPreventionToken': csrf_token_node2,
    'Content-Type': 'application/x-www-form-urlencoded',
    }
    data_node2 = {
    "fingerprint": join_fingerprint,
    "hostname": node1_hostname,
    "password": node1_password,
    "link0": node2_hostname
    }
    cookies_nodes2 = {'PVEAuthCookie': ticket_node2}

    # 3. Add node2 to the cluster
    print("Attempting to join")
    join_response = requests.post(f"{node2_api_url}/cluster/config/join", headers=headers_node2, data=data_node2, cookies=cookies_nodes2, verify=False)
    join_response.raise_for_status()
    print(join_response.json())
    print("Join done!!")