def test_put(service, tmpdir): image = testutils.create_tempfile(tmpdir, "image", "-------|after") ticket = testutils.create_ticket(url="file://" + str(image)) tickets.add(ticket) uri = "/images/" + ticket["uuid"] res = http.unix_request(service.address, "PUT", uri, "content") assert res.status == http_client.OK assert res.getheader("content-length") == "0" with io.open(str(image)) as f: assert f.read() == "content|after"
def test_images_upload_max_size(tmpdir): image_size = 100 content = "b" * image_size image = testutils.create_tempfile(tmpdir, "image", "") ticket = testutils.create_ticket(url="file://" + str(image), size=image_size) tickets.add(ticket) res = http.put("/images/" + ticket["uuid"], content) assert res.status == 200 assert image.read() == content
def test_images_options_write(): ticket = testutils.create_ticket(ops=["write"]) tickets.add(ticket) res = http.options("/images/" + ticket["uuid"]) # Having "write" imply also "read". allows = {"OPTIONS", "GET", "PUT", "PATCH"} features = {"zero", "flush"} assert res.status == 200 assert set(res.getheader("allow").split(',')) == allows assert set(json.loads(res.read())["features"]) == features
def test_tickets_idle_time_inactive(fake_time): ticket = testutils.create_ticket() tickets.add(ticket) # Ticket idle time starts with ticket is added. assert tickets.get(ticket["uuid"]).idle_time == 0 # Simulate time passing without any request. fake_time.now += 200 assert tickets.get(ticket["uuid"]).idle_time == 200
def test_tickets_extend_invalid_timeout(fake_time): ticket = testutils.create_ticket() tickets.add(ticket) prev_ticket = tickets.get(ticket["uuid"]).info() body = json.dumps({"timeout": "invalid"}) res = http.unix_request(config.tickets.socket, "PATCH", "/tickets/%(uuid)s" % ticket, body) cur_ticket = tickets.get(ticket["uuid"]).info() assert res.status == 400 assert cur_ticket == prev_ticket
def test_tickets_delete_all(): # Example usage: move host to maintenance for i in range(5): ticket = testutils.create_ticket( url="file:///var/run/vdsm/storage/foo%s" % i) tickets.add(ticket) res = http.unix_request(config.tickets.socket, "DELETE", "/tickets/") assert res.status == 204 # Note: incorrect according to RFC, but required for vdsm. assert res.getheader("content-length") == "0" pytest.raises(KeyError, tickets.get, ticket["uuid"])
def test_images_upload_after_last_byte(tmpdir): image_size = 100 image = testutils.create_tempfile(tmpdir, "image", "a" * image_size) ticket = testutils.create_ticket(url="file://" + str(image), size=image_size) tickets.add(ticket) res = http.put("/images/" + ticket["uuid"], "b", headers={"Content-Range": "bytes 100-101/*"}) assert res.status == 403 assert image.read() == "a" * image_size
def test_images_upload(tmpdir, flush): image = testutils.create_tempfile(tmpdir, "image", "-------|after") ticket = testutils.create_ticket(url="file://" + str(image)) tickets.add(ticket) uri = "/images/" + ticket["uuid"] if flush: uri += "?flush=" + flush res = http.put(uri, "content") assert image.read() == "content|after" assert res.status == 200 assert res.getheader("content-length") == "0"
def test_images_download_filename_in_ticket(tmpdir): size = 1024 filename = u"\u05d0.raw" # hebrew aleph image = testutils.create_tempfile(tmpdir, "image", size=size) ticket = testutils.create_ticket(url="file://" + str(image), size=size, filename=filename) tickets.add(ticket) res = http.get("/images/" + ticket["uuid"], headers={"Range": "bytes=0-1023"}) expected = "attachment; filename=\xd7\x90.raw" assert res.getheader("Content-Disposition") == expected
def test_images_download(tmpdir, rng, start, end): data = "a" * 512 + "b" * 512 + "c" * 512 image = testutils.create_tempfile(tmpdir, "image", data) ticket = testutils.create_ticket(url="file://" + str(image), size=len(data)) tickets.add(ticket) res = http.get("/images/" + ticket["uuid"], headers={"Range": rng}) assert res.status == 206 received = res.read() assert received == data[start:end] content_range = 'bytes %d-%d/%d' % (start, end - 1, len(data)) assert res.getheader("Content-Range") == content_range
def test_tickets_idle_time_patch(fake_time, tmpdir, msg): image = testutils.create_tempfile(tmpdir, "image", "a" * 8192) ticket = testutils.create_ticket(url="file://" + str(image)) tickets.add(ticket) # Request must reset idle time. fake_time.now += 200 body = json.dumps(msg).encode('ascii') http.patch("/images/" + ticket["uuid"], body, headers={"content-type": "application/json"}) assert tickets.get(ticket["uuid"]).idle_time == 0
def test_images_download_partial_not_satistieble(tmpdir): # Image is smaller than ticket size - may happen if engine failed to detect # actual image size reported by vdsm - one byte difference is enough to # cause a failure. # See https://bugzilla.redhat.com/1512315. size = 1024 image = testutils.create_tempfile(tmpdir, "image", size=size) ticket = testutils.create_ticket(url="file://" + str(image), size=size + 1) tickets.add(ticket) unsatisfiable_range = "bytes=0-%d" % size # Max is size - 1 res = http.get("/images/" + ticket["uuid"], headers={"Range": unsatisfiable_range}) assert res.status == http_client.REQUESTED_RANGE_NOT_SATISFIABLE
def test_tickets_extend_expired_ticket(fake_time): ticket = testutils.create_ticket() tickets.add(ticket) # Make the ticket expire. fake_time.now += 500 server_ticket = tickets.get(ticket["uuid"]).info() # Extend the expired ticket. body = json.dumps({"timeout": 300}) res = http.unix_request(config.tickets.socket, "PATCH", "/tickets/%(uuid)s" % ticket, body) assert res.status == 200 server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 800
def test_images_options_ticket_expired(fake_time): ticket = testutils.create_ticket(timeout=300) tickets.add(ticket) server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 300 # Make the ticket expire fake_time.now += 300 res = http.options("/images/" + ticket["uuid"]) assert res.status == 403 server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 300
def test_images_options_extends_ticket(fake_time): ticket = testutils.create_ticket() tickets.add(ticket) server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 300 fake_time.now += 200 res = http.options("/images/" + ticket["uuid"]) assert res.status == 200 res.read() server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 500
def test_tickets_get(fake_time): ticket = testutils.create_ticket(ops=["read"], sparse=False) tickets.add(ticket) fake_time.now += 200 res = http.unix_request(config.tickets.socket, "GET", "/tickets/%(uuid)s" % ticket) assert res.status == 200 server_ticket = json.loads(res.read()) # The server adds an expires key del server_ticket["expires"] ticket["active"] = False ticket["transferred"] = 0 ticket["idle_time"] = 200 assert server_ticket == ticket
def test_images_upload_extends_ticket(tmpdir, fake_time): image = testutils.create_tempfile(tmpdir, "image", "before") ticket = testutils.create_ticket(url="file://" + str(image)) tickets.add(ticket) server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 300 fake_time.now += 200 res = http.put("/images/" + ticket["uuid"], "") assert res.status == 200 res.read() server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 500
def test_images_download_extends_ticket(tmpdir, fake_time): size = 1024 image = testutils.create_tempfile(tmpdir, "image", size=size) ticket = testutils.create_ticket(url="file://" + str(image), size=size) tickets.add(ticket) server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 300 fake_time.now += 200 res = http.get("/images/" + ticket["uuid"]) assert res.status == 200 res.read() server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 500
def test_tickets_extend(fake_time): ticket = testutils.create_ticket(sparse=False) tickets.add(ticket) patch = {"timeout": 300} body = json.dumps(patch) fake_time.now += 240 res = http.unix_request(config.tickets.socket, "PATCH", "/tickets/%(uuid)s" % ticket, body) ticket["expires"] = int(fake_time.now + ticket["timeout"]) ticket["active"] = False ticket["idle_time"] = 240 server_ticket = tickets.get(ticket["uuid"]).info() assert res.status == 200 assert res.getheader("content-length") == "0" assert server_ticket == ticket
def test_images_download_partial_no_range(tmpdir): # The image is smaller than the tiket size, but we don't request a range, # so we should get the existing length of the image, since the ticket size # is only an upper limit. Or maybe we should treat the ticket size as the # expected size? # This is another variant of https://bugzilla.redhat.com/1512315. size = 1024 image = testutils.create_tempfile(tmpdir, "image", size=size) ticket = testutils.create_ticket(url="file://" + str(image), size=size + 1) tickets.add(ticket) res = http.get("/images/" + ticket["uuid"]) assert res.status == http_client.OK # Should return the available image data, not the ticket size. Reading this # response will fail with IncompleteRead. assert res.length == 1024
def test_images_flush_extends_ticket(tmpdir, fake_time): data = "x" * 512 image = testutils.create_tempfile(tmpdir, "image", data) ticket = testutils.create_ticket(url="file://" + str(image)) tickets.add(ticket) server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 300 fake_time.now += 200 body = json.dumps({"op": "flush"}).encode("ascii") res = http.patch("/images/" + ticket["uuid"], body) assert res.status == 200 res.read() server_ticket = tickets.get(ticket["uuid"]).info() assert server_ticket["expires"] == 500
def test_images_zero(tmpdir, msg): data = "x" * 512 image = testutils.create_tempfile(tmpdir, "image", data) ticket = testutils.create_ticket(url="file://" + str(image)) tickets.add(ticket) size = msg["size"] offset = msg.get("offset", 0) body = json.dumps(msg).encode("ascii") res = http.patch("/images/" + ticket["uuid"], body) assert res.status == 200 assert res.getheader("content-length") == "0" with io.open(str(image), "rb") as f: assert f.read(offset) == data[:offset] assert f.read(size) == b"\0" * size assert f.read() == data[offset + size:]
def test_images_zero(service, tmpdir): data = "x" * 512 image = testutils.create_tempfile(tmpdir, "image", data) ticket = testutils.create_ticket(url="file://" + str(image)) tickets.add(ticket) msg = {"op": "zero", "size": 20, "offset": 10, "future": True} size = msg["size"] offset = msg.get("offset", 0) body = json.dumps(msg).encode("ascii") res = http.unix_request( service.address, "PATCH", "/images/" + ticket["uuid"], body) assert res.status == http_client.OK assert res.getheader("content-length") == "0" with io.open(str(image), "rb") as f: assert f.read(offset) == data[:offset] assert f.read(size) == b"\0" * size assert f.read() == data[offset + size:]
def test_keep_alive_connection_on_success(tmpdir, method, body): # After successful request the connection should remain open. image = testutils.create_tempfile(tmpdir, "image", size=1024) ticket = testutils.create_ticket(url="file://" + str(image), size=1024) tickets.add(ticket) uri = "/images/%(uuid)s" % ticket con = http.connection() with closing(con): # Disabling auto_open so we can test if a connection was closed. con.auto_open = False con.connect() # Send couple of requests - all should succeed. for i in range(3): con.request(method, uri, body=body) r1 = http.response(con) r1.read() assert r1.status == 200
def test_tickets_idle_time_active(fake_time, tmpdir): filename = tmpdir.join("image") # Note: must be big enough so the request remain active. size = 1024**2 * 10 with open(str(filename), 'wb') as image: image.truncate(size) ticket = testutils.create_ticket(url="file://" + str(filename), ops=["read"], size=size) tickets.add(ticket) # Start a download, but read only 1 byte to make sure the operation becomes # active but do not complete. res = http.get("/images/" + ticket["uuid"]) res.read(1) # Active ticket idle time is always 0. fake_time.now += 200 assert tickets.get(ticket["uuid"]).idle_time == 0
def test_get_forbidden(service): ticket = testutils.create_ticket(url="file:///no/such/image", ops=()) tickets.add(ticket) res = http.unix_request(service.address, "GET", "/images/" + ticket["uuid"], "content") assert res.status == http_client.FORBIDDEN
def test_images_flush_ticket_readonly(tmpdir): ticket = testutils.create_ticket(url="file:///no/such/image", ops=["read"]) tickets.add(ticket) body = json.dumps({"op": "flush"}).encode("ascii") res = http.patch("/images/" + ticket["uuid"], body) assert res.status == 403
def test_images_upload_forbidden(tmpdir): ticket = testutils.create_ticket(url="file:///no/such/image", ops=["read"]) tickets.add(ticket) res = http.put("/images/" + ticket["uuid"], "content") assert res.status == 403
def test_images_upload_content_length_missing(tmpdir): ticket = testutils.create_ticket(url="file:///no/such/image") tickets.add(ticket) res = http.raw_request("PUT", "/images/" + ticket["uuid"]) assert res.status == 400
def test_images_upload_invalid_flush(tmpdir): ticket = testutils.create_ticket(url="file:///no/such/image") tickets.add(ticket) res = http.put("/images/" + ticket["uuid"] + "?flush=invalid", "data") assert res.status == 400