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:
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
**/.git
|
||||
37
Dockerfile
37
Dockerfile
@@ -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"]
|
||||
3
Pipfile
3
Pipfile
@@ -7,6 +7,9 @@ name = "pypi"
|
||||
python-openstacksdk = "*"
|
||||
prometheus_client = "*"
|
||||
cryptography = "*"
|
||||
tcpping2 = "*"
|
||||
argparse = "*"
|
||||
selenium = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.8"
|
||||
113
Pipfile.lock
generated
113
Pipfile.lock
generated
@@ -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",
|
||||
|
||||
47
README.md
47
README.md
@@ -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
9
docker-entrypoint.sh
Normal 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
76
lib/horizon.py
Normal 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
111
lib/instance_deploy.py
Normal 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")
|
||||
@@ -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)
|
||||
|
||||
@@ -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 $@
|
||||
Reference in New Issue
Block a user