Skip to content

Commit ee659bf

Browse files
Merge pull request #17 from netunicorn/cloudflare
Cloudflare
2 parents e6516c2 + ac89a91 commit ee659bf

File tree

7 files changed

+255
-0
lines changed

7 files changed

+255
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .speedtest import CloudflareSpeedTest, CloudflareSpeedTestLinuxImplementation
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import os
2+
import random
3+
import subprocess
4+
import time
5+
from dataclasses import dataclass
6+
from typing import Optional
7+
8+
from netunicorn.base import Result, Task, TaskDispatcher, Node, Architecture, Success
9+
10+
11+
@dataclass
12+
class CloudflareSpeedTestResults:
13+
summary: dict
14+
unloaded_latency: float
15+
unloaded_jitter: float
16+
unloaded_latency_points: list[float]
17+
down_loaded_latency: float
18+
down_loaded_jitter: float
19+
down_loaded_latency_points: list[float]
20+
up_loaded_latency: float
21+
up_loaded_jitter: float
22+
up_loaded_latency_points: list[float]
23+
download_bandwidth: float
24+
download_bandwidth_points: list[dict]
25+
upload_bandwidth: float
26+
upload_bandwidth_points: list[dict]
27+
packet_loss: float
28+
packet_loss_details: dict
29+
scores: dict
30+
31+
32+
def get_measurements(driver) -> CloudflareSpeedTestResults:
33+
return CloudflareSpeedTestResults(
34+
summary=driver.execute_script(
35+
"return window.testInstance.results.getSummary()"
36+
),
37+
unloaded_latency=driver.execute_script(
38+
"return window.testInstance.results.getUnloadedLatency()"
39+
),
40+
unloaded_jitter=driver.execute_script(
41+
"return window.testInstance.results.getUnloadedJitter()"
42+
),
43+
unloaded_latency_points=driver.execute_script(
44+
"return window.testInstance.results.getUnloadedLatencyPoints()"
45+
),
46+
down_loaded_latency=driver.execute_script(
47+
"return window.testInstance.results.getDownLoadedLatency()"
48+
),
49+
down_loaded_jitter=driver.execute_script(
50+
"return window.testInstance.results.getDownLoadedJitter()"
51+
),
52+
down_loaded_latency_points=driver.execute_script(
53+
"return window.testInstance.results.getDownLoadedLatencyPoints()"
54+
),
55+
up_loaded_latency=driver.execute_script(
56+
"return window.testInstance.results.getUpLoadedLatency()"
57+
),
58+
up_loaded_jitter=driver.execute_script(
59+
"return window.testInstance.results.getUpLoadedJitter()"
60+
),
61+
up_loaded_latency_points=driver.execute_script(
62+
"return window.testInstance.results.getUpLoadedLatencyPoints()"
63+
),
64+
download_bandwidth=driver.execute_script(
65+
"return window.testInstance.results.getDownloadBandwidth()"
66+
),
67+
download_bandwidth_points=driver.execute_script(
68+
"return window.testInstance.results.getDownloadBandwidthPoints()"
69+
),
70+
upload_bandwidth=driver.execute_script(
71+
"return window.testInstance.results.getUploadBandwidth()"
72+
),
73+
upload_bandwidth_points=driver.execute_script(
74+
"return window.testInstance.results.getUploadBandwidthPoints()"
75+
),
76+
packet_loss=driver.execute_script(
77+
"return window.testInstance.results.getPacketLoss()"
78+
),
79+
packet_loss_details=driver.execute_script(
80+
"return window.testInstance.results.getPacketLossDetails()"
81+
),
82+
scores=driver.execute_script("return window.testInstance.results.getScores()"),
83+
)
84+
85+
86+
def measure(
87+
chrome_location: Optional[str] = None, webdriver_arguments: Optional[list] = None
88+
) -> CloudflareSpeedTestResults:
89+
from selenium import webdriver
90+
from selenium.webdriver.chrome.options import Options
91+
from selenium.webdriver.chrome.service import Service
92+
93+
# start python http.server
94+
http_process = subprocess.Popen(
95+
["python3", "-m", "http.server", "44345"], cwd="/tmp/cloudflare/speedtest"
96+
)
97+
98+
# start Xvfb display for the browser
99+
display_number = random.randint(100, 500)
100+
xvfb_process = subprocess.Popen(
101+
["Xvfb", f":{display_number}", "-screen", "0", "1920x1080x24"]
102+
)
103+
os.environ["DISPLAY"] = f":{display_number}"
104+
105+
options = Options()
106+
options.add_argument("--no-sandbox")
107+
options.add_argument("--disable-dev-shm-usage")
108+
if webdriver_arguments:
109+
for argument in webdriver_arguments:
110+
options.add_argument(argument)
111+
if chrome_location:
112+
options.binary_location = chrome_location
113+
114+
driver = webdriver.Chrome(service=Service(), options=options)
115+
time.sleep(1)
116+
driver.get("http://localhost:44345/test.html")
117+
time.sleep(1)
118+
119+
# check "window.testInstance.isFinished" attribute
120+
while not driver.execute_script("return window.testInstance.isFinished"):
121+
time.sleep(1)
122+
123+
# get results
124+
results = get_measurements(driver)
125+
126+
driver.close()
127+
xvfb_process.kill()
128+
http_process.kill()
129+
return results
130+
131+
132+
class CloudflareSpeedTest(TaskDispatcher):
133+
def __init__(self, *args, **kwargs):
134+
super().__init__(*args, **kwargs)
135+
self.linux_instance = CloudflareSpeedTestLinuxImplementation(name=self.name)
136+
137+
def dispatch(self, node: Node) -> Task:
138+
if node.architecture in {Architecture.LINUX_AMD64, Architecture.LINUX_ARM64}:
139+
return self.linux_instance
140+
141+
raise NotImplementedError(
142+
f"CLoudflareSpeedTest is not implemented for architecture: {node.architecture}"
143+
)
144+
145+
146+
class CloudflareSpeedTestLinuxImplementation(Task):
147+
requirements = [
148+
"apt install -y python3-pip wget xvfb procps chromium chromium-driver",
149+
"pip3 install selenium webdriver-manager",
150+
"mkdir -p /tmp/cloudflare/speedtest",
151+
"wget https://github.com/netunicorn/netunicorn-library/releases/download/cloudflare-speedtest-0.1/bundle.js -O /tmp/cloudflare/speedtest/bundle.js",
152+
"wget https://github.com/netunicorn/netunicorn-library/releases/download/cloudflare-speedtest-0.1/test.html -O /tmp/cloudflare/speedtest/test.html",
153+
]
154+
155+
def __init__(
156+
self,
157+
chrome_location: Optional[str] = None,
158+
webdriver_arguments: Optional[list] = None,
159+
*args,
160+
**kwargs,
161+
):
162+
self.chrome_location = chrome_location
163+
if not self.chrome_location:
164+
self.chrome_location = "/usr/bin/chromium"
165+
self.webdriver_arguments = webdriver_arguments
166+
super().__init__(*args, **kwargs)
167+
168+
def run(self) -> Result[CloudflareSpeedTestResults, str]:
169+
return Success(measure(self.chrome_location, self.webdriver_arguments))
170+
171+
172+
if __name__ == "__main__":
173+
CloudflareSpeedTestLinuxImplementation(
174+
chrome_location="/usr/bin/chromium-browser"
175+
).run()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Cloudflare speedtest netunicorn task sources
2+
3+
This folder contains source code for Cloudflare speedtest adapted as netunicorn task.
4+
5+
In particular, to run the speedtest, you need to bundle the cloudflare speedtest npm module with the index.js file that uses these code. When the HTML page inporting this bundle would be opened, the speedtest would be run and results would be assigned to the window variable testResults, and window.testFinished would be set to true.
6+
7+
Currently, the task refers to the bundled version of the speedtest module, which is located in the `dist` folder. To rebuild the bundle, run `npm run build` in the `tasks/measurements/cloudflare` folder.
8+
9+
The bundled version is published as "cloudflare-speedtest-bundle:0.1" release in the netunicorn-repo.
10+
11+
## How to bundle the speedtest module
12+
13+
If you want to change index.js, you'll need to create the new bundle. For this:
14+
1. Install npm
15+
2. Create a new npm project in the `tasks/measurements/cloudflare/src` folder: `npm init`
16+
3. Install the required modules: `npm install @cloudflare/speedtest babel-loader webpack webpack-cli`
17+
4. Modify `index.js` as needed.
18+
5. Run `npx webpack` to create the bundle in the folder specified in the `webpack.config.js` file.
19+
6. Publish your bundle somewhere and change the requirements of the corresponding netunicorn task to point to your new bundle.
20+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import SpeedTest from '@cloudflare/speedtest';
2+
3+
window.testFinished = false;
4+
window.testInstance = new SpeedTest();
5+
window.testInstance.onFinish = results => {
6+
window.testResults = results;
7+
window.testFinished = true;
8+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "temp",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC",
12+
"devDependencies": {
13+
"webpack": "^5.89.0",
14+
"webpack-cli": "^5.1.4"
15+
},
16+
"dependencies": {
17+
"@cloudflare/speedtest": "^1.3.0",
18+
"babel-loader": "^9.1.3"
19+
}
20+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<script src="bundle.js"></script>
7+
<title>CloudFlare SpeedTest</title>
8+
</head>
9+
10+
<body>
11+
</body>
12+
13+
</html>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module.exports = {
2+
entry: './index.js',
3+
output: {
4+
filename: 'bundle.js',
5+
path: "/tmp/cloudflare/speedtest/",
6+
},
7+
module: {
8+
rules: [
9+
{
10+
test: /\.js$/,
11+
exclude: /node_modules/,
12+
use: {
13+
loader: 'babel-loader',
14+
},
15+
},
16+
],
17+
},
18+
};

0 commit comments

Comments
 (0)