1
0
mirror of https://github.com/jcwimer/openstack-exporter synced 2026-03-24 17:44:42 +00:00

Added instance deploy metrics and horizon selenium metrics

This commit is contained in:
2020-11-24 18:34:05 -05:00
parent 33e23eb496
commit 5e6e34be4c
10 changed files with 418 additions and 50 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
**/.git

View File

@@ -1,14 +1,49 @@
FROM python:3.8
RUN pip install pipenv
RUN mkdir /python
### CACHE PIP DEPENDENCIES
COPY Pipfile* /tmp/
RUN cd /tmp && pipenv lock --requirements > requirements.txt
RUN pip install -r /tmp/requirements.txt
###
### install Chromium
RUN apt-get update
# stretch uses chromium package not chromium-browser
RUN apt-get install chromium=83.0* -y
# install chromedriver
RUN apt-get install -yqq unzip
RUN wget -O /tmp/chromedriver.zip https://chromedriver.storage.googleapis.com/83.0.4103.39/chromedriver_linux64.zip
RUN unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/
# install xvfb
RUN apt-get install -yqq xvfb
RUN apt-get -qq clean \
&& apt-get autoremove -y \
&& rm -rf \
/var/lib/apt/lists/* \
/tmp/* \
/var/tmp/*
# set display port and dbus env to avoid hanging
ENV DISPLAY=:99
ENV DBUS_SESSION_BUS_ADDRESS=/dev/null
COPY docker-entrypoint.sh /root/docker-entrypoint.sh
RUN mkdir /app
WORKDIR /app
RUN chmod a+x /root/docker-entrypoint.sh
###
WORKDIR /python
COPY . .
# Python output in stdout
ENV PYTHONUNBUFFERED=1
ENV PYTHONIOENCODING=UTF-8
CMD ["python", "/python/openstack_exporter.py"]
ENTRYPOINT ["bash", "/root/docker-entrypoint.sh","python", "/python/openstack_exporter.py"]

View File

@@ -7,6 +7,9 @@ name = "pypi"
python-openstacksdk = "*"
prometheus_client = "*"
cryptography = "*"
tcpping2 = "*"
argparse = "*"
selenium = "*"
[requires]
python_version = "3.8"

113
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "0e95ce77cd9f07771ffa18845b80641875c5f30d6c1638d2b5e34131693fa32f"
"sha256": "1899c86f141fcf09325101960a96e6f82223038176bacd1026dbb585d85ae062"
},
"pipfile-spec": 6,
"requires": {
@@ -23,6 +23,14 @@
],
"version": "==1.4.4"
},
"argparse": {
"hashes": [
"sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4",
"sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"
],
"index": "pypi",
"version": "==1.4.0"
},
"certifi": {
"hashes": [
"sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd",
@@ -32,44 +40,42 @@
},
"cffi": {
"hashes": [
"sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d",
"sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b",
"sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4",
"sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f",
"sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3",
"sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579",
"sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537",
"sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e",
"sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05",
"sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171",
"sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca",
"sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522",
"sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c",
"sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc",
"sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d",
"sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808",
"sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828",
"sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869",
"sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d",
"sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9",
"sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0",
"sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc",
"sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15",
"sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c",
"sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a",
"sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3",
"sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1",
"sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768",
"sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d",
"sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b",
"sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e",
"sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d",
"sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730",
"sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394",
"sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1",
"sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591"
"sha256:00a1ba5e2e95684448de9b89888ccd02c98d512064b4cb987d48f4b40aa0421e",
"sha256:00e28066507bfc3fe865a31f325c8391a1ac2916219340f87dfad602c3e48e5d",
"sha256:045d792900a75e8b1e1b0ab6787dd733a8190ffcf80e8c8ceb2fb10a29ff238a",
"sha256:0638c3ae1a0edfb77c6765d487fee624d2b1ee1bdfeffc1f0b58c64d149e7eec",
"sha256:105abaf8a6075dc96c1fe5ae7aae073f4696f2905fde6aeada4c9d2926752362",
"sha256:155136b51fd733fa94e1c2ea5211dcd4c8879869008fc811648f16541bf99668",
"sha256:1a465cbe98a7fd391d47dce4b8f7e5b921e6cd805ef421d04f5f66ba8f06086c",
"sha256:1d2c4994f515e5b485fd6d3a73d05526aa0fcf248eb135996b088d25dfa1865b",
"sha256:2c24d61263f511551f740d1a065eb0212db1dbbbbd241db758f5244281590c06",
"sha256:51a8b381b16ddd370178a65360ebe15fbc1c71cf6f584613a7ea08bfad946698",
"sha256:594234691ac0e9b770aee9fcdb8fa02c22e43e5c619456efd0d6c2bf276f3eb2",
"sha256:5cf4be6c304ad0b6602f5c4e90e2f59b47653ac1ed9c662ed379fe48a8f26b0c",
"sha256:64081b3f8f6f3c3de6191ec89d7dc6c86a8a43911f7ecb422c60e90c70be41c7",
"sha256:6bc25fc545a6b3d57b5f8618e59fc13d3a3a68431e8ca5fd4c13241cd70d0009",
"sha256:798caa2a2384b1cbe8a2a139d80734c9db54f9cc155c99d7cc92441a23871c03",
"sha256:7c6b1dece89874d9541fc974917b631406233ea0440d0bdfbb8e03bf39a49b3b",
"sha256:840793c68105fe031f34d6a086eaea153a0cd5c491cde82a74b420edd0a2b909",
"sha256:8d6603078baf4e11edc4168a514c5ce5b3ba6e3e9c374298cb88437957960a53",
"sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35",
"sha256:9f7a31251289b2ab6d4012f6e83e58bc3b96bd151f5b5262467f4bb6b34a7c26",
"sha256:9ffb888f19d54a4d4dfd4b3f29bc2c16aa4972f1c2ab9c4ab09b8ab8685b9c2b",
"sha256:a7711edca4dcef1a75257b50a2fbfe92a65187c47dab5a0f1b9b332c5919a3fb",
"sha256:af5c59122a011049aad5dd87424b8e65a80e4a6477419c0c1015f73fb5ea0293",
"sha256:b18e0a9ef57d2b41f5c68beefa32317d286c3d6ac0484efd10d6e07491bb95dd",
"sha256:b4e248d1087abf9f4c10f3c398896c87ce82a9856494a7155823eb45a892395d",
"sha256:ba4e9e0ae13fc41c6b23299545e5ef73055213e466bd107953e4a013a5ddd7e3",
"sha256:c6332685306b6417a91b1ff9fae889b3ba65c2292d64bd9245c093b1b284809d",
"sha256:d9efd8b7a3ef378dd61a1e77367f1924375befc2eba06168b6ebfa903a5e59ca",
"sha256:df5169c4396adc04f9b0a05f13c074df878b6052430e03f50e68adf3a57aa28d",
"sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775",
"sha256:ec80dc47f54e6e9a78181ce05feb71a0353854cc26999db963695f950b5fb375",
"sha256:f032b34669220030f905152045dfa27741ce1a6db3324a5bc0b96b6c7420c87b",
"sha256:f60567825f791c6f8a592f3c6e3bd93dd2934e3f9dac189308426bd76b00ef3b",
"sha256:f803eaa94c2fcda012c047e62bc7a51b0bdabda1cad7a92a522694ea2d76e49f"
],
"version": "==1.14.3"
"version": "==1.14.4"
},
"chardet": {
"hashes": [
@@ -115,10 +121,10 @@
},
"dogpile.cache": {
"hashes": [
"sha256:eba3eb532be75a930f7a70c40c9a66829a3f7281650ad3cd3a786b2e4ba68e83"
"sha256:40147b19696f387415a7efaaa4cf8ea0b5d31bdd1b53e5187e75d48ddfee9f0e"
],
"markers": "python_version >= '3.6'",
"version": "==1.1.0"
"version": "==1.1.1"
},
"idna": {
"hashes": [
@@ -141,16 +147,16 @@
"sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9",
"sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.0"
},
"jsonpatch": {
"hashes": [
"sha256:83ff23119b336ea2feffa682307eb7269b58097b4e88c089a4950d946442db16",
"sha256:e45df18b0ab7df1925f20671bbc3f6bd0b4b556fb4b9c5d97684b0a7eac01744"
"sha256:4d08af10d71723b5b2924da6ba90f273a4d1a5c6accfb605eb970cb2f9b29cf9",
"sha256:86eaaccfac8891c6a09ab48aec13fb5921ff32c838e7a19981f0d2b94915f668"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.26"
"version": "==1.27"
},
"jsonpointer": {
"hashes": [
@@ -253,11 +259,13 @@
"sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
"sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76",
"sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
"sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e",
"sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648",
"sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
"sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f",
"sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
"sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee",
"sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a",
"sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
"sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
"sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"
@@ -279,12 +287,20 @@
],
"version": "==1.4.0"
},
"selenium": {
"hashes": [
"sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c",
"sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d"
],
"index": "pypi",
"version": "==3.141.0"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"stevedore": {
@@ -295,6 +311,13 @@
"markers": "python_version >= '3.6'",
"version": "==3.2.2"
},
"tcpping2": {
"hashes": [
"sha256:91466021341db977760c3e72ddbb47f95a80209b349b2f1073621b3d9ee13c8c"
],
"index": "pypi",
"version": "==0.1.6"
},
"urllib3": {
"hashes": [
"sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08",

View File

@@ -5,12 +5,53 @@ This is a prometheus exporter for Openstack
### With Docker
This script will forward all "OS_" variables to the container and start the container on port 8000
```
bash run-with-docker.sh
bash run-with-docker.sh --cloud_name openstack
```
The docker image includes Chromium for getting Horizon metrics.
### With Python
Need Python 3.8 and pipenv
```
pipenv sync
pipenv run python openstack_exporter.py
```
pipenv run python openstack_exporter.py --cloud_name openstack
```
### Optional Params
To deploy an instance and test the time to ping on all hypervisors, use the following flags:
```
--image IMAGE_NAME_OR_ID --flavor FLAVOR_NAME_OR_ID --network NETWORK_NAME_OR_ID --instance_deploy
```
The network used needs to have TCP port 22 (uses TCP instead of ICMP to ping) open in the default security group.
To capture horizon login response time, use the following flag:
```
--horizon_url "http://url_here"
```
This will use selenium to log into horizon. You will need Chrome or Chromium installed to use this feature. You will see these metrics:
```
openstack_api_response_seconds{api_name="horizon",cloud_name="CLOUD_NAME"}
openstack_api_status{api_name="horizon",cloud_name="CLOUD_NAME"}
```
# Information
### Standard Metrics Provided
| Metric | Description|
|--------------------------|----------|
| openstack_api_response_seconds{api_name="API_NAME",cloud_name="CLOUD_NAME"} | Seconds for the api to respond via openstack sdk. nova, neutron, and cinder are currently recorded. |
| openstack_api_status{api_name="API_NAME",cloud_name="CLOUD_NAME"} | Status of the openstack api. 1 = up 0 = down. nova, neutron, and cinder are currently recorded. |
| openstack_hypervisor_running_vms{hypervisor_hostname="HYPERVISOR_NAME",cloud_name="CLOUD_NAME"} | Number of running VMs on every hypervisor in the region. |
| openstack_hypervisor_used_ram_mb{hypervisor_hostname="HYPERVISOR_NAME",cloud_name="CLOUD_NAME"} | Amount of RAM in MB used (as reported by nova-compute) for every hypervisor in the region. |
| openstack_hypervisor_total_ram_mb{hypervisor_hostname="HYPERVISOR_NAME",cloud_name="CLOUD_NAME"} | Amount of RAM in MB in total (as reported by nova-compute) for every hypervisor in the region. |
| openstack_hypervisor_used_cpus{hypervisor_hostname="HYPERVISOR_NAME",cloud_name="CLOUD_NAME"} | Number of vcpus used (as reported by nova-compute) for every hypervisor in the region. |
| openstack_hypervisor_total_cpus{hypervisor_hostname="HYPERVISOR_NAME",cloud_name="CLOUD_NAME"} | Number of vcpus in total (as reported by nova-compute) for every hypervisor in the region. |
| openstack_hypervisor_enabled{hypervisor_hostname="HYPERVISOR_NAME",cloud_name="CLOUD_NAME"} | nova-compute status for every hypervisor in the region. 1 = enabled 0 = disabled|
| openstack_hypervisor_up{hypervisor_hostname="HYPERVISOR_NAME",cloud_name="CLOUD_NAME"} | nova-compute state for every hypervisor in the region. 1 = up 0 = down |
| openstack_hypervisor_local_gb_total{hypervisor_hostname="HYPERVISOR_NAME",cloud_name="CLOUD_NAME"} | Total local disk in GB (as reported by nova-compute) for every hypervisor in the region. |
| openstack_hypervisor_local_gb_used{hypervisor_hostname="HYPERVISOR_NAME",cloud_name="CLOUD_NAME"} | Total local disk used in GB (as reported by nova-compute) for every hypervisor in the region. |
### Optional Metrics (use flags when running)
| Metric | Description |
|-----|-----|
|openstack_instance_deploy_seconds_to_ping{hypervisor_hostname="HYPERVISOR_NAME",cloud_name="CLOUD_NAME"} | Seconds from deploy command to ping when creating an instance for every hypervisor in the region. Requires --flavor, --image, --network, and --instance_deploy flags. The network used needs to have TCP port 22 (uses TCP instead of ICMP to ping) open in the default security group. |
|openstack_horizon_response_seconds{cloud_name="CLOUD_NAME"} | Seconds it takes for Chromium to log into Horizon. Requires --horizon_url flag. |
|openstack_horizon_status{cloud_name="CLOUD_NAME"} | Horizon status. 1 = up 0 = down. Requires --horizon_url flag. |

9
docker-entrypoint.sh Normal file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
set -e
export DISPLAY=:99
export DBUS_SESSION_BUS_ADDRESS=/dev/null
echo "Starting X virtual framebuffer (Xvfb) in background..."
Xvfb -ac :99 -screen 0 1280x1024x16 > /dev/null 2>&1 &
exec "$@"

76
lib/horizon.py Normal file
View File

@@ -0,0 +1,76 @@
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
import os
import time
import openstack
import datetime
import prometheus_client as prom
openstack_username = os.getenv('OS_USERNAME')
openstack_password = os.getenv('OS_PASSWORD')
api_metrics = prom.Gauge('openstack_horizon_response_seconds', 'Time for horizon login via Chrome.', ['cloud_name'])
api_status = prom.Gauge('openstack_horizon_status', 'Horizon current status. 1 = up 0 = down.',['cloud_name'])
def get_metrics(horizon_url,cloud_name):
chrome_options = webdriver.ChromeOptions()
chrome_options.binary_location = "/usr/bin/chromium"
chrome_options.add_argument('--ignore-certificate-errors')
chrome_options.add_argument('--disable-popup-blocking')
chrome_options.add_argument('--disable-translate')
chrome_options.add_argument('--disable-notifications')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
driver=webdriver.Chrome(chrome_options=chrome_options)
driver.implicitly_wait(10)
# driver.get("https://httpstat.us/200")
# if "200 OK" in driver.page_source:
# print('Selenium successfully opened with Chrome (under the Xvfb display) and navigated to "https://httpstat.us/200", you\'re all set!')
print(f"Attempting to log into Horizon at {horizon_url}")
timeout = 30
driver.get(horizon_url)
try:
# wait till page loads
WebDriverWait(driver, timeout).until(EC.title_contains("OpenStack"))
except TimeoutException:
print("Timed out waiting for root url to load")
api_status.labels(cloud_name).set(0)
return None
#Print Page
# the following javascript scrolls down the entire page body. Since Twitter
# uses "inifinite scrolling", more content will be added to the bottom of the
# DOM as you scroll... since it is in the loop, it will scroll down up to 100
# times.
for _ in range(100):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# print all of the page source that was loaded
# print(driver.page_source.encode("utf-8"))
# Log in
driver.find_element_by_id("id_username").send_keys(f"{openstack_username}")
driver.find_element_by_id ("id_password").send_keys(f"{openstack_password}")
start_time = datetime.datetime.now()
driver.find_element_by_id("loginBtn").click()
try:
WebDriverWait(driver, timeout).until(EC.title_contains("OpenStack"))
end_time = datetime.datetime.now()
time_took = end_time - start_time
seconds_took = time_took.seconds
print(f"Horizon took {seconds_took} seconds to log in.")
api_metrics.labels(cloud_name).set(seconds_took)
api_status.labels(cloud_name).set(1)
except:
print("Timed out waiting for login to load")
api_status.labels(cloud_name).set(1)
finally:
driver.quit()

111
lib/instance_deploy.py Normal file
View File

@@ -0,0 +1,111 @@
from tcpping2 import Ping
import os
import time
import openstack
import datetime
import traceback
import prometheus_client as prom
instance_deploy_metrics = prom.Gauge('openstack_instance_deploy_seconds_to_ping', 'Time to deploy an instance and ping it.', ['hypervisor_hostname'])
def run_pings(ip_address):
try:
ping = Ping(ip_address, 22)
# Ping 1 time
run_ping = ping.ping(1)
ping_success_rate = run_ping['success_rate']
print(f'Ping success rate: {ping_success_rate}%')
return ping_success_rate
# If ping fails
except socket.gaierror:
time.sleep(5)
def wait_for_ping(ip_address):
print('Waiting for instance to respond to ping. This will time out in 10 minutes.')
timeout = time.time() + 60 * 10 # 10 minutes from now
ping_success_rate = 0
while ping_success_rate != 100.0:
ping_success_rate = run_pings(ip_address)
time.sleep(5)
if time.time() > timeout:
print("Timed out waiting for ping to the instance.")
return False
return True
def get_image(connection, image):
try:
image_found = connection.image.find_image(image, ignore_missing=True)
return image_found
except:
print(f"Had issues finding image {image}.")
print(traceback.print_exc())
return None
def get_flavor(connection, flavor):
try:
flavor_found = connection.compute.find_flavor(flavor, ignore_missing=True)
return flavor_found
except:
print(f"Had issues finding flavor {flavor}.")
print(traceback.print_exc())
return None
def get_network(connection, network):
try:
network_found = connection.network.find_network(network)
return network_found
except:
print(f"Had issues finding network {network}.")
print(traceback.print_exc())
return None
def cleanup(connection, instance_name):
print(f"Cleaning up {instance_name} instance.")
server = connection.compute.find_server(instance_name)
if server:
try:
connection.compute.delete_server(server.id)
except:
print(f"Failed to delete server: {instance_name}")
print(traceback.print_exc())
def create_instance(connection, flavor, image, network, hypervisor):
instance_name = f"{hypervisor}-metric"
availability_zone = str(f"nova:{hypervisor}")
print(f"Creating an instance called: {instance_name}")
try:
server = connection.compute.create_server(
networks=[{"uuid": network.id}],
image_id=image.id,
flavor_id=flavor.id,
name=f"{instance_name}",
availability_zone=availability_zone,
)
server = connection.compute.wait_for_server(server, status="ACTIVE", wait=600)
ip_address = server.addresses[network.name][0]['addr']
if wait_for_ping(ip_address) is True:
return True
else:
return False
except:
print(f"Failed to create instance {instance_name}.")
print(traceback.print_exc())
cleanup(connection, f"{instance_name}")
def get_metrics(connection, flavor, image, network):
instance_image = get_image(connection, image)
instance_flavor = get_flavor(connection, flavor)
instance_network = get_network(connection, network)
for hypervisor in connection.list_hypervisors():
availability_zone = str(f"nova:{hypervisor.name}")
start_time = datetime.datetime.now()
if create_instance(connection, instance_flavor, instance_image, instance_network, hypervisor.name) is True:
end_time = datetime.datetime.now()
time_took = end_time - start_time
seconds_took = time_took.seconds
print(f'Instance creation on {hypervisor.name} took {seconds_took} seconds.')
instance_deploy_metrics.labels(f'{hypervisor.name}').set(seconds_took)
cleanup(connection, f"{hypervisor.name}-metric")

View File

@@ -4,16 +4,81 @@ import openstack
import time
import argparse
import sys
from lib import instance_deploy
from lib import api_metrics
from lib import hypervisor_metrics
from lib import horizon
def openstack_connection():
conn = openstack.connect(cloud='envvars')
return conn
# Set up argparse
def parse_cli_arguments():
parser = argparse.ArgumentParser(
description='Openstack Prometheus Exporter',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
required = parser.add_argument_group(title='required arguments')
required.add_argument(
'--cloud_name',
required=True,
help='Give the cloud a name.',
)
# This gets the optional arguments to print below the required ones.
optional = parser.add_argument_group(title='optional arguments')
optional.add_argument(
'--instance_deploy',
action="store_true",
dest='instancedeploy',
default=False,
help='Enables instance deploy metrics. Requires --flavor --network and --image.'
)
optional.add_argument(
'--flavor',
dest='flavor',
type=str,
help='Flavor name or ID to use for instance deploy metrics.'
)
optional.add_argument(
'--network',
dest='network',
type=str,
help='Pingable (via TCP) network to use for instance deploy metrics.'
)
optional.add_argument(
'--image',
dest='image',
type=str,
help='Image name or ID to use for instance deploy metrics.'
)
optional.add_argument(
'--horizon_url',
dest='horizon_url',
type=str,
help='Url for Horizon.'
)
args = parser.parse_args()
# Validation
if args.instancedeploy is True and (args.image is None or args.flavor is None or args.network is None):
parser.error("argument --instance_deploy: requires --image, --flavor, and --network.")
elif args.image and (args.instancedeploy is False or args.flavor is None or args.network is None):
parser.error("argument --image: requires --instance_deploy, --flavor, and --network.")
elif args.network and (args.instancedeploy is False or args.flavor is None or args.image is None):
parser.error("argument --network: requires --instance_deploy, --flavor, and --image.")
elif args.flavor and (args.instancedeploy is False or args.image is None or args.network is None):
parser.error("argument --flavor: requires --instance_deploy, --image, and --network.")
return args
if __name__ == '__main__':
print("Starting server on port 8000")
prom.start_http_server(8000)
args = parse_cli_arguments()
while True:
try:
print("Gathering metrics...")
@@ -22,6 +87,10 @@ if __name__ == '__main__':
api_metrics.generate_neutron_metrics(connection)
api_metrics.generate_cinder_metrics(connection)
hypervisor_metrics.generate_hypervisor_metrics(connection)
if args.instancedeploy and args.flavor and args.image and args.network:
instance_deploy.get_metrics(connection, args.flavor, args.image, args.network)
if args.horizon_url is not None:
horizon.get_metrics(args.horizon_url, args.cloud_name)
connection.close()
print("Waiting 30 seconds to gather more metrics.")
time.sleep(30)

View File

@@ -3,4 +3,4 @@ OPENSTACK_VARS=""
for var in $(env | grep OS_); do
OPENSTACK_VARS+="-e $var ";
done
docker run -d -p 8000:8000 $OPENSTACK_VARS jcwimer/openstack_exporter
docker run --name openstack_exporter -d -p 8000:8000 $OPENSTACK_VARS jcwimer/openstack_exporter $@