Allow generating ISO image from local file (#1046)

- Break out generate_iso() into its own class method in file_cmds
- Add form and handling of using local file to generate iso image
- Reorder index page sections to group actions that create new image files
This commit is contained in:
Daniel Markstedt 2022-12-23 16:13:52 -08:00 committed by GitHub
parent bf2b210c4c
commit cde4866844
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 248 additions and 140 deletions

View File

@ -635,30 +635,48 @@ class FileCmds:
)
tmp_full_path.unlink(True)
try:
run(
[
"genisoimage",
*iso_args,
"-o",
str(iso_filename),
tmp_dir,
],
capture_output=True,
check=True,
)
except CalledProcessError as error:
logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
return {"status": False, "msg": error.stderr.decode("utf-8")}
process = self.generate_iso(iso_filename, Path(tmp_dir), *iso_args)
if not process["status"]:
return {"status": False, "msg": process["msg"]}
parameters = {"value": " ".join(iso_args)}
return {
"status": True,
"return_code": ReturnCodes.DOWNLOADFILETOISO_SUCCESS,
"parameters": parameters,
"file_name": iso_filename.name,
"return_code": process["return_code"],
"parameters": process["parameters"],
"file_name": process["file_name"],
}
def generate_iso(self, iso_file, target_path, *iso_args):
"""
Takes
- (Path) iso_file - the path to the file to create
- (Path) target_path - the path to the file or dir to generate the iso from
- (*str) iso_args - the tuple of arguments to pass to genisoimage
"""
try:
run(
[
"genisoimage",
*iso_args,
"-o",
str(iso_file),
str(target_path),
],
capture_output=True,
check=True,
)
except CalledProcessError as error:
logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
return {"status": False, "msg": error.stderr.decode("utf-8")}
return {
"status": True,
"return_code": ReturnCodes.DOWNLOADFILETOISO_SUCCESS,
"parameters": {"value": " ".join(iso_args)},
"file_name": iso_file.name,
}
# noinspection PyMethodMayBeStatic
def download_to_dir(self, url, save_dir, file_name):
"""

View File

@ -741,7 +741,8 @@ section#files p {
}
section#files table#images form.file-attach input[type="submit"],
section#attach-devices form.device-attach input[type="submit"] {
section#attach-devices form.device-attach input[type="submit"],
section#create-iso form.iso-attach input[type="submit"] {
background: #efefef url("icons/file-device-attach.svg") no-repeat 0.5rem center;
background-size: 1rem;
padding-left: 2rem;

View File

@ -382,6 +382,161 @@
<hr/>
<section id="create-iso">
<details>
<summary class="heading">
{{ _("Create CD-ROM Image With File") }}
</summary>
<ul>
<li>{{ _("HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX.") }}</li>
<li>{{ _("If the downloaded file is a zip archive, we will attempt to unzip it and store the resulting files.") }}</li>
</ul>
</details>
<div>
<form action="/files/create_iso" method="post" class="iso-attach">
<label for="iso_url">{{ _("Create ISO from URL:") }}</label>
<input name="url" id="iso_url" required="" type="url">
<label for="iso_url_type">{{ _("Type:") }}</label>
<select name="type" id="iso_url_type">
<option value="HFS">
HFS
</option>
<option value="ISO-9660 Level 1">
ISO-9660 Level 1
</option>
<option value="ISO-9660 Level 2">
ISO-9660 Level 2
</option>
<option value="ISO-9660 Level 3">
ISO-9660 Level 3
</option>
<option value="Joliet">
Joliet
</option>
<option value="Rock Ridge">
Rock Ridge
</option>
</select>
<label for="iso_url_scsi_id">{{ _("ID") }}</label>
<select name="scsi_id" id="iso_url_scsi_id">
{% for id in scsi_ids["valid_ids"] %}
<option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
{{ id }}
</option>
{% endfor %}
</select>
<input type="submit" value="{{ _("Create and Attach") }}" onclick="processNotify('{{ _("Downloading file and generating CD-ROM image...") }}')">
</form>
</div>
<div>
<form action="/files/create_iso" method="post" class="iso-attach">
<label for="iso_file">{{ _("Create ISO from local file:") }}</label>
<select name="file" id="iso_file">
{% for f in files|sort(attribute='name') %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endfor %}
</select>
<label for="iso_file_type">{{ _("Type:") }}</label>
<select name="type" id="iso_file_type">
<option value="HFS">
HFS
</option>
<option value="ISO-9660 Level 1">
ISO-9660 Level 1
</option>
<option value="ISO-9660 Level 2">
ISO-9660 Level 2
</option>
<option value="ISO-9660 Level 3">
ISO-9660 Level 3
</option>
<option value="Joliet">
Joliet
</option>
<option value="Rock Ridge">
Rock Ridge
</option>
</select>
<label for="iso_file_scsi_id">{{ _("ID") }}</label>
<select name="scsi_id" id="iso_file_scsi_id">
{% for id in scsi_ids["valid_ids"] %}
<option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
{{ id }}
</option>
{% endfor %}
</select>
<input type="submit" value="{{ _("Create and Attach") }}" onclick="processNotify('{{ _("Generating CD-ROM image...") }}')">
</form>
</div>
</section>
<hr/>
<section id="create-image">
<details>
<summary class="heading">
{{ _("Create Empty Disk Image File") }}
</summary>
<ul>
<li>{{ _("Please refer to <a href=\"%(url)s\" target=\"_blank\">wiki documentation</a> to learn more about the supported image file types.", url="https://github.com/PiSCSI/piscsi/wiki/Supported-Device-Types#image-types") }}</li>
<li>{{ _("It is not recommended to use the Lido hard disk driver with the Macintosh Plus.") }}</li>
</ul>
</details>
<form action="/files/create" method="post">
<label for="image_create_file_name">{{ _("File Name:") }}</label>
<input name="file_name" id="image_create_file_name" required="" type="text">
<label for="image_create_type">{{ _("Type:") }}</label>
<select name="type" id="image_create_type">
{% for key, value in image_suffixes_to_create.items() %}
<option value="{{ key }}">
{{ value }} [.{{ key }}]
</option>
{% endfor %}
</select>
<label for="image_create_size">{{ _("Size:") }}</label>
<input name="size" id="image_create_size" type="number" placeholder="{{ _("MiB") }}" min="1" max="262144" required>
<label for="image_create_drive_name">{{ _("Masquerade as:") }}</label>
<select name="drive_name" id="image_create_drive_name">
<option value="">
{{ _("None") }}
</option>
{% for drive in drive_properties["hd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
</select>
<label for="drive_format">{{ _("Format as:") }}</label>
<select name="drive_format" id="drive_format">
<option value="">
{{ _("None") }}
</option>
<option value="Lido 7.56">
HFS + Lido
</option>
<option value="SpeedTools 3.6">
HFS + SpeedTools
</option>
<option value="FAT16">
FAT16
</option>
<option value="FAT32">
FAT32
</option>
</select>
<input type="submit" value="{{ _("Create") }}">
</form>
</section>
<section id="create-drive">
<a href="/drive/list"><p>{{ _("Create Disk Image With Properties") }}</p></a>
</section>
<hr/>
<section id="logging">
<section id="attach-devices">
<details>
<summary class="heading">
@ -482,120 +637,6 @@
<hr/>
<section id="download-to-iso">
<details>
<summary class="heading">
{{ _("Download File and Create CD-ROM Image") }}
</summary>
<ul>
<li>{{ _("Create an ISO file system CD-ROM image with the downloaded file, and mount it on the given SCSI ID.") }}</li>
<li>{{ _("HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX.") }}</li>
<li>{{ _("If the downloaded file is a zip archive, we will attempt to unzip it and store the resulting files.") }}</li>
</ul>
</details>
<form action="/files/download_to_iso" method="post">
<label for="iso_url">{{ _("Download file from URL:") }}</label>
<input name="url" id="iso_url" required="" type="url">
<label for="iso_type">{{ _("Type:") }}</label>
<select name="type" id="iso_type">
<option value="HFS">
HFS
</option>
<option value="ISO-9660 Level 1">
ISO-9660 Level 1
</option>
<option value="ISO-9660 Level 2">
ISO-9660 Level 2
</option>
<option value="ISO-9660 Level 3">
ISO-9660 Level 3
</option>
<option value="Joliet">
Joliet
</option>
<option value="Rock Ridge">
Rock Ridge
</option>
</select>
<label for="iso_scsi_id">{{ _("ID") }}</label>
<select name="scsi_id" id="iso_scsi_id">
{% for id in scsi_ids["valid_ids"] %}
<option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
{{ id }}
</option>
{% endfor %}
</select>
<input type="submit" value="{{ _("Download and Mount CD-ROM image") }}" onclick="processNotify('{{ _("Downloading File and generating CD-ROM image...") }}')">
</form>
</section>
<hr/>
<section id="create-image">
<details>
<summary class="heading">
{{ _("Create Empty Disk Image File") }}
</summary>
<ul>
<li>{{ _("Please refer to <a href=\"%(url)s\" target=\"_blank\">wiki documentation</a> to learn more about the supported image file types.", url="https://github.com/PiSCSI/piscsi/wiki/Supported-Device-Types#image-types") }}</li>
<li>{{ _("It is not recommended to use the Lido hard disk driver with the Macintosh Plus.") }}</li>
</ul>
</details>
<form action="/files/create" method="post">
<label for="image_create_file_name">{{ _("File Name:") }}</label>
<input name="file_name" id="image_create_file_name" required="" type="text">
<label for="image_create_type">{{ _("Type:") }}</label>
<select name="type" id="image_create_type">
{% for key, value in image_suffixes_to_create.items() %}
<option value="{{ key }}">
{{ value }} [.{{ key }}]
</option>
{% endfor %}
</select>
<label for="image_create_size">{{ _("Size:") }}</label>
<input name="size" id="image_create_size" type="number" placeholder="{{ _("MiB") }}" min="1" max="262144" required>
<label for="image_create_drive_name">{{ _("Masquerade as:") }}</label>
<select name="drive_name" id="image_create_drive_name">
<option value="">
{{ _("None") }}
</option>
{% for drive in drive_properties["hd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
</select>
<label for="drive_format">{{ _("Format as:") }}</label>
<select name="drive_format" id="drive_format">
<option value="">
{{ _("None") }}
</option>
<option value="Lido 7.56">
HFS + Lido
</option>
<option value="SpeedTools 3.6">
HFS + SpeedTools
</option>
<option value="FAT16">
FAT16
</option>
<option value="FAT32">
FAT32
</option>
</select>
<input type="submit" value="{{ _("Create") }}">
</form>
</section>
<section id="create-drive">
<a href="/drive/list"><p>{{ _("Create Disk Image With Properties") }}</p></a>
</section>
<hr/>
<section id="logging">
<details>
<summary class="heading">
{{ _("Logging") }}

View File

@ -879,7 +879,7 @@ def shutdown():
return response(error=True, message=message)
@APP.route("/files/download_to_iso", methods=["POST"])
@APP.route("/files/create_iso", methods=["POST"])
@login_required
def download_to_iso():
"""
@ -888,6 +888,7 @@ def download_to_iso():
scsi_id = request.form.get("scsi_id")
url = request.form.get("url")
iso_type = request.form.get("type")
local_file = request.form.get("file")
if iso_type == "HFS":
iso_args = ["-hfs"]
@ -907,7 +908,14 @@ def download_to_iso():
message=_("%(iso_type)s is not a valid CD-ROM format.", iso_type=iso_type),
)
process = file_cmd.download_file_to_iso(url, *iso_args)
if url:
process = file_cmd.download_file_to_iso(url, *iso_args)
elif local_file:
server_info = piscsi_cmd.get_server_info()
file_path = Path(server_info["image_dir"]) / local_file
iso_path = Path(str(file_path) + ".iso")
process = file_cmd.generate_iso(iso_path, file_path, *iso_args)
process = ReturnCodeMapper.add_msg(process)
if not process["status"]:
return response(

View File

@ -328,9 +328,8 @@ def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_fi
delete_file(file_name)
# route("/files/download_to_iso", methods=["POST"])
def test_download_url_to_iso(
env,
# route("/files/create_iso", methods=["POST"])
def test_create_iso_from_url(
httpserver,
http_client,
list_files,
@ -354,7 +353,7 @@ def test_download_url_to_iso(
)
response = http_client.post(
"/files/download_to_iso",
"/files/create_iso",
data={
"scsi_id": SCSI_ID,
"type": ISO_TYPE,
@ -380,6 +379,47 @@ def test_download_url_to_iso(
delete_file(iso_file_name)
# route("/files/create_iso", methods=["POST"])
def test_create_iso_from_local_file(
http_client,
create_test_image,
list_files,
list_attached_images,
detach_devices,
delete_file,
):
test_file_name = create_test_image()
iso_file_name = f"{test_file_name}.iso"
ISO_TYPE = "HFS"
response = http_client.post(
"/files/create_iso",
data={
"scsi_id": SCSI_ID,
"type": ISO_TYPE,
"file": test_file_name,
},
)
response_data = response.json()
assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS
assert iso_file_name in list_files()
assert iso_file_name in list_attached_images()
assert (
response_data["messages"][0]["message"]
== f"CD-ROM image {iso_file_name} with type {ISO_TYPE} was created "
f"and attached to SCSI ID {SCSI_ID}"
)
# Cleanup
detach_devices()
delete_file(iso_file_name)
# route("/files/diskinfo", methods=["POST"])
def test_show_diskinfo(http_client, create_test_image):
test_image = create_test_image()