Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ esxi-iso/
logs/jobs/
venv
www/__pycache__/
tftpboot/iso
proxmox-iso/
4 changes: 4 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
History
=====================
3.0 (2025-11-24)
---------------------
* Added the ability to install Proxmox Servers as well as ESXi hosts (CIMC only)

2.4 (2023-11-08)
---------------------
* Fix: cleanup after failed ISO upload
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# ESXi Auto-Installer

ESXi Auto-Installer makes it easy to mass install ESXi hosts while assigning each host a unique IP address and enables SSH through a simple web-based GUI or via automation APIs.\
ESXi Auto-Installer makes it easy to mass install ESXi and Proxmox hosts while assigning each host a unique IP address and enables SSH through a simple web-based GUI or via automation APIs.\
ESXi Auto-Installer can be used for both physical servers and virtual machines by leveraging PXE boot to install ESXi.\
For Cisco UCS Severs (except B series) you can install ESXi using the Cisco IMC instead of PXE. This is simpler and generally more reliable than PXE boot installs.

ESXi Auto-Installer will:
- Install the ESXi Operating System on a physical or virtual server.
- Configure the ESXi Management interface with a unique IP address.
- Enable SSH (optional).
- Configure the ESXi or Proxmox Management interface with a unique IP address.
- Enable SSH (optional) on ESXi.

ESXi Auto-Installer API's enables you to completely automate host installations. The API's also allow you to query the installation progress so you can automatically launch your ESXi configuration scripts once the server installation is complete.
ESXi Auto-Installer API's enables you to completely automate host installations. The API's also allow you to query the installation progress so you can automatically launch your configuration scripts once the server installation is complete.

## Features
- Start deployment on multiple servers in parallel.
- Supports custom ESXi installation ISOs.
- Supports iSCSI boot installs.
- Supports iSCSI boot installs (ESXi only).
- [Web APIs for additional automation](https://ciscodevnet.github.io/esxi-auto-installer/).
- Reports the installation progress for each server.
- Fully automated installs on Cisco UCS Servers (excluding B series) via Cisco IMC.
Expand Down Expand Up @@ -67,10 +67,10 @@ Point a web browser at the system where ESXi Auto-Installer running.

## First task: Upload ISO

ESXi Auto-Installer does not come bundled with an ESXi Installation ISO file.\
The first time you visit the ESXi Auto-Installer web page, you will be asked to upload an ESXi Installation ISO.
ESXi Auto-Installer does not come bundled with an ESXi or Proxmox Installation ISO file.\
The first time you visit the ESXi Auto-Installer web page, you will be asked to upload an ESXi or Proxmox Installation ISO.
Click the Browse button on this page to locate an ISO on your local machine.\
After selecting a valid ESXi Installation ISO file, click Submit.
After selecting a valid ESXi or Proxmox Installation ISO file, click Submit.

Once the ISO is uploaded, you will be directed to the Home page.

Expand Down Expand Up @@ -152,14 +152,14 @@ CIMC: If you have a Cisco UCS Server (except UCS B series), generally the CIMC i
- Do not need access to the ESXi Mgmt Interface, so it works even on VLAN Trunking ports.
- Reliable, even if your ESXi host is in a different subnet.
- Automatically reboots the host.
- Can onl ybe used on Cisco Servers (Execpt UCS B series)
- Can only be used on Cisco Servers (Execpt UCS B series)

PXE: For all other systems, including Virtual Machines and Cisco UCS B series servers, you can use the PXE boot method.
- Sometimes requires network changes before DHCP works. This is where most problems with PXE install method come from.
- Need to know the MAC address of your Mgmt NIC.
- After submitting the request to ESXi Auto-Installer, you need to reboot the target host.
- Fast installs, especially over 40GB NICs.
- Can be used on any server, including virtual machines.
- Is hardware agnostic, even works with virtual machines.

## Common issues / FAQ

Expand Down
12 changes: 9 additions & 3 deletions auto-installer_docker/auto-installer_flask/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
FROM ubuntu:20.04
FROM ubuntu:24.04

WORKDIR /opt/eai/
RUN echo "deb [arch=amd64] http://download.proxmox.com/debian/pve bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-install-repo.list
# COPY proxmox-release-bookworm.gpg /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
ADD https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
RUN chmod a+r /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
RUN apt update -y
RUN apt install python3-pip genisoimage -y
RUN apt install python3-pip genisoimage xorriso proxmox-auto-install-assistant -y
RUN mkdir /opt/eai/custom-iso
RUN mkdir -p /opt/eai/upload/mnt
RUN mkdir -p /opt/eai/proxmox-iso
RUN mkdir -p /opt/eai/answerfiles
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install --no-cache-dir -r requirements.txt --break-system-packages
COPY . .
222 changes: 222 additions & 0 deletions auto-installer_docker/auto-installer_flask/autoinstaller_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,3 +430,225 @@ class EAIISOs(Resource):
def get(self):
# return the list of available ISO images
return get_available_isos()


class ProxmoxISOs(Resource):
def get(self):
# return the list of available Proxmox ISO images
return get_proxmox_isos()


class ProxmoxJobs(Resource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
self.reqparse.add_argument(
"installmethod",
type=str,
required=True,
help="No installation method provided",
location="json",
)
self.reqparse.add_argument(
"iso_image",
type=str,
required=True,
help="No ISO name provided",
location="json",
)
self.reqparse.add_argument(
"root_pwd",
type=str,
required=True,
help="No root password provided",
location="json",
)
self.reqparse.add_argument(
"cimc_pwd", type=str, required=True, help="No CIMC password provided", location="json"
)
self.reqparse.add_argument(
"cimc_usr",
type=str,
default="admin",
help="No CIMC account provided",
location="json",
)
self.reqparse.add_argument(
"host_gateway",
type=str,
required=True,
help="No Gateway provided",
location="json",
)
self.reqparse.add_argument(
"hosts",
type=list,
required=True,
help="No host list provided",
location="json",
)
self.reqparse.add_argument(
"keyboard", type=str, default="en-us", location="json"
)
self.reqparse.add_argument(
"country", type=str, default="us", location="json"
)
self.reqparse.add_argument(
"timezone", type=str, default="UTC", location="json"
)
self.reqparse.add_argument(
"mailto", type=str, default="[email protected]", location="json"
)
self.reqparse.add_argument(
"filesystem", type=str, default="ext4", location="json"
)
self.reqparse.add_argument(
"disk_list", type=str, default="/dev/sda", location="json"
)
self.reqparse.add_argument(
"interface", type=str, default="eno1", location="json"
)
self.reqparse.add_argument(
"cidr", type=str, default="24", location="json"
)
self.reqparse.add_argument(
"dns", type=str, location="json"
)
self.reqparse.add_argument(
"net_filter", type=str, default="ID_NET_NAME", location="json"
)
super(ProxmoxJobs, self).__init__()

def post(self):
mainlog = get_main_logger()
try:
jobid_list = []
install_data = {}
args = self.reqparse.parse_args()
mainlog.debug(f"API /proxmox-jobs endpoint called with args: {args}")

# Verify installation method
if args["installmethod"] not in ("cimc",):
mainlog.error(
f"API POST /proxmox-jobs error - Unknown installation method. Request aborted."
)
return {
"status": "error",
"message": "Unknown installation method. Proxmox only supports CIMC method.",
}, 400

# Verify gateway
try:
ipaddress.ip_address(args["host_gateway"])
except ValueError:
return {
"status": "error",
"message": "Required field is not valid: host_gateway",
}, 400

# Validate CIDR
try:
cidr_value = int(args["cidr"])
if cidr_value < 0 or cidr_value > 32:
raise ValueError
except ValueError:
return {
"status": "error",
"message": "CIDR must be between 0 and 32",
}, 400

# Verify hosts data
p = re.compile("^[A-Za-z\d\-_.]{1,253}$")
for host_data in args["hosts"]:
mainlog.debug(f"Host data: {host_data}")

# Validate hostname (FQDN)
if "hostname" in host_data:
if not re.search(p, host_data["hostname"]):
return {
"status": "error",
"message": "Required hosts field is not valid: hostname (FQDN)",
}, 400
else:
return {
"status": "error",
"message": "Required hosts field not provided: hostname",
}, 400

# Validate host IP
if "host_ip" in host_data:
try:
ipaddress.ip_address(host_data["host_ip"])
except ValueError:
return {
"status": "error",
"message": "Required hosts field is not valid: host_ip",
}, 400
else:
return {
"status": "error",
"message": "Required hosts field not provided: host_ip",
}, 400

# Validate CIMC IP
if "cimc_ip" not in host_data:
return {
"status": "error",
"message": "Required hosts field not provided: cimc_ip",
}, 400

try:
if host_data["cimc_ip"].count(".") == 3:
# It's an IPv4 address
port_separator = host_data["cimc_ip"].rfind(":")
address_string = (
host_data["cimc_ip"]
if port_separator == -1
else host_data["cimc_ip"][0:port_separator]
)
else:
# Could be IPv6 address
port_separator = host_data["cimc_ip"].rfind("]:")
address_string = (
host_data["cimc_ip"]
if port_separator == -1
else host_data["cimc_ip"][0 : port_separator + 1]
)

ipaddress.ip_address(address_string)
except ValueError:
return {
"status": "error",
"message": "Required hosts field is not valid: cimc_ip",
}, 400

# Check CIMC credentials
if not args["cimc_pwd"] or not args["cimc_usr"]:
mainlog.error(
f"API POST /proxmox-jobs error - missing CIMC credentials. Request aborted."
)
return {
"status": "error",
"message": "Missing CIMC credentials",
}, 400

# Skip arguments with None value
for k, v in args.items():
if v is not None:
install_data[k] = v

# Check if requested ISO is valid
if not install_data["iso_image"] in get_proxmox_isos():
mainlog.error(f"Requested ISO {install_data['iso_image']} not found")
return {"status": "error", "message": "Requested ISO not found"}, 404

# All data validated
mainlog.debug(f"API POST /proxmox-jobs install data: {install_data}")

# Create jobs
jobid_list = create_proxmox_jobs(install_data, args["installmethod"], mainlog)
return jobid_list
except KeyError as e:
return {
"status": "error",
"message": f"Incorrect or missing key when trying to create a new job. Expected key: {str(e)}",
}, 400
Loading