├── .github └── workflows │ ├── lint.yml │ └── tests.yml ├── .gitignore ├── .obs ├── main.yaml └── workflows.yml ├── .pre-commit-config.yaml ├── .prettierignore ├── .prettierrc ├── .pylintrc ├── .readthedocs.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── SECURITY.md ├── cli ├── orthos-client.spec ├── orthos2 ├── orthosrc └── tests │ ├── __init__.py │ ├── test_add.py │ ├── test_alias.py │ ├── test_auth.py │ ├── test_config.py │ ├── test_delete.py │ ├── test_exit.py │ ├── test_help.py │ ├── test_info.py │ ├── test_power.py │ ├── test_query.py │ ├── test_regenerate.py │ ├── test_release.py │ ├── test_rescan.py │ ├── test_reservationhistory.py │ ├── test_reserve.py │ ├── test_serverconfig.py │ └── test_setup.py ├── compose.yaml ├── docker ├── README.md ├── bmc.dockerfile ├── bmc.py ├── cobbler-startup.sh ├── cobbler.dockerfile ├── devel-server.sh ├── develop-leap156.dockerfile ├── develop-tw.dockerfile ├── django-generate-admin-token ├── serial-console-startup.sh └── serial-console.dockerfile ├── docs ├── Makefile ├── adminguide.rst ├── adminguide │ ├── architectures.rst │ ├── domains.rst │ ├── groups_and_users.rst │ ├── machine.rst │ ├── machine_groups.rst │ ├── serial_console_type.rst │ ├── server_configuration.rst │ ├── setup.rst │ ├── setup_devel.rst │ ├── systems_and_enclosures.rst │ ├── tokens.rst │ ├── vendors_and_platforms.rst │ └── websocket.rst ├── commandline.rst ├── conf.py ├── img │ └── userguide │ │ ├── 00_architecture.png │ │ ├── 01_login_screen.jpg │ │ ├── 02_landingpage.jpg │ │ ├── 03_top_menu_overviews.jpg │ │ ├── 04_arch_quickfilter.jpg │ │ ├── 05_machine_page.jpg │ │ ├── 06_machine_infos.jpg │ │ ├── 07_machine_status.jpg │ │ ├── 08_machine_annotations.jpg │ │ ├── 09_machine_actions.jpg │ │ ├── 10_machine_release.jpg │ │ ├── 11_machine_virtual.jpg │ │ └── 12_machine_virtual_gast.jpg ├── index.rst ├── make.bat ├── requirements.docs.txt └── userguide.rst ├── manage.py ├── mypy.ini ├── orthos2.rpmlintrc ├── orthos2.spec ├── orthos2 ├── __init__.py ├── api │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── commands │ │ ├── __init__.py │ │ ├── add.py │ │ ├── base.py │ │ ├── delete.py │ │ ├── info.py │ │ ├── power.py │ │ ├── query.py │ │ ├── regenerate.py │ │ ├── release.py │ │ ├── rescan.py │ │ ├── reservationhistory.py │ │ ├── reserve.py │ │ ├── serverconfig.py │ │ └── setup.py │ ├── fixtures │ │ ├── forms │ │ │ └── delete_remote_power_device_api_form.json │ │ └── serializers │ │ │ └── machines.json │ ├── forms.py │ ├── lookups.py │ ├── models.py │ ├── serializers │ │ ├── __init__.py │ │ ├── annotation.py │ │ ├── bmc.py │ │ ├── installation.py │ │ ├── machine.py │ │ ├── misc.py │ │ └── networkinterface.py │ ├── tests │ │ ├── __init__.py │ │ ├── commands │ │ │ ├── __init__.py │ │ │ ├── test_add.py │ │ │ └── test_info.py │ │ ├── serializers │ │ │ ├── __init__.py │ │ │ └── test_machine.py │ │ └── test_forms.py │ ├── urls.py │ └── views.py ├── data │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── exceptions.py │ ├── fixtures │ │ ├── architectures.json │ │ ├── domains.json │ │ ├── platforms.json │ │ ├── serialconsoletypes.json │ │ ├── serverconfigs.json │ │ ├── systems.json │ │ ├── tests │ │ │ ├── test_machines.json │ │ │ └── test_serverconfig_domainending.json │ │ └── vendors.json │ ├── management │ │ └── commands │ │ │ └── dump_db.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20210311_1216.py │ │ ├── 0003_auto_20210406_1905.py │ │ ├── 0004_auto_20210505_1650.py │ │ ├── 0005_auto_20210510_1913.py │ │ ├── 0006_auto_20210511_0917.py │ │ ├── 0007_auto_20210511_1910.py │ │ ├── 0008_auto_20210522_1102.py │ │ ├── 0009_remove_domain_setup_architectures.py │ │ ├── 0010_auto_20210522_1123.py │ │ ├── 0011_auto_20210522_1537.py │ │ ├── 0012_auto_20210531_1630.py │ │ ├── 0013_auto_20210610_0804.py │ │ ├── 0014_auto_20210610_1246.py │ │ ├── 0015_auto_20210615_1500.py │ │ ├── 0016_auto_20210616_1122.py │ │ ├── 0017_auto_20210622_1848.py │ │ ├── 0019_auto_20210624_1910.py │ │ ├── 0020_auto_20210625_1935.py │ │ ├── 0021_auto_20210628_1207.py │ │ ├── 0022_auto_20210628_1713.py │ │ ├── 0023_auto_20210713_0532.py │ │ ├── 0024_auto_20210713_0538.py │ │ ├── 0025_auto_20210713_0755.py │ │ ├── 0026_auto_20210713_1242.py │ │ ├── 0027_auto_20210916_1201.py │ │ ├── 0028_auto_20210916_1216.py │ │ ├── 0029_auto_20210916_1225.py │ │ ├── 0030_auto_20210916_1228.py │ │ ├── 0031_auto_20211006_1005.py │ │ ├── 0032_alter_domain_cobbler_server.py │ │ ├── 0033_alter_domain_cobbler_server.py │ │ ├── 0034_alter_domain_cobbler_server.py │ │ ├── 0035_auto_20211103_1405.py │ │ ├── 0036_alter_serialconsole_kernel_device.py │ │ ├── 0037_remotepowerdevice_url.py │ │ ├── 0038_auto_20220221_1626.py │ │ ├── 0039_auto_20220221_1649.py │ │ ├── 0040_alter_bmc_fence_name_remove_domain_cobbler_server_and_more.py │ │ ├── 0041_domain_cobbler_server_password_and_more.py │ │ ├── 0042_alter_machine_last_check.py │ │ ├── 0043_add_network_model.py │ │ ├── 0044_initial_required_data.py │ │ ├── 0045_alter_domain_cscreen_server.py │ │ └── __init__.py │ ├── models │ │ ├── __init__.py │ │ ├── annotation.py │ │ ├── architecture.py │ │ ├── bmc.py │ │ ├── component.py │ │ ├── components │ │ │ ├── __init__.py │ │ │ └── pci.py │ │ ├── domain.py │ │ ├── enclosure.py │ │ ├── installation.py │ │ ├── machine.py │ │ ├── machinegroup.py │ │ ├── networkinterface.py │ │ ├── platform.py │ │ ├── remotepower.py │ │ ├── remotepowerdevice.py │ │ ├── remotepowertype.py │ │ ├── reservationhistory.py │ │ ├── serialconsole.py │ │ ├── serialconsoletype.py │ │ ├── serverconfig.py │ │ ├── system.py │ │ ├── vendor.py │ │ └── virtualizationapi.py │ ├── scripts │ │ ├── README │ │ ├── __init__.py │ │ ├── dump_test_db.py │ │ ├── show_ansible_info.py │ │ ├── show_machine_info.py │ │ └── store_machine_info.py │ ├── signals.py │ ├── static │ │ └── js │ │ │ └── machine_admin.js │ ├── tests │ │ ├── __init__.py │ │ └── test_serverconfig.py │ └── validators.py ├── frontend │ ├── __init__.py │ ├── apps.py │ ├── decorators.py │ ├── forms │ │ ├── __init__.py │ │ ├── newuser.py │ │ ├── passwordrestore.py │ │ ├── preferences.py │ │ ├── reservemachine.py │ │ ├── search.py │ │ ├── setupmachine.py │ │ └── virtualmachine.py │ ├── management │ │ └── commands │ │ │ └── serialconsole-ws.py │ ├── static │ │ └── frontend │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.min.css │ │ │ ├── custom.css │ │ │ ├── fa │ │ │ ├── font-awesome-4.7.0 │ │ │ ├── css │ │ │ │ ├── font-awesome.css │ │ │ │ └── font-awesome.min.css │ │ │ └── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── images │ │ │ ├── add_vm.png │ │ │ ├── amd.png │ │ │ ├── back.png │ │ │ ├── ibm.png │ │ │ ├── intel.png │ │ │ ├── logout.png │ │ │ ├── no.png │ │ │ ├── orthos.png │ │ │ ├── powerswitch.png │ │ │ ├── serialconsole.png │ │ │ ├── serviceprocessor.png │ │ │ ├── spinner.gif │ │ │ ├── ui-icons_444444_256x240.png │ │ │ ├── ui-icons_555555_256x240.png │ │ │ ├── unknown.png │ │ │ └── yes.png │ │ │ ├── jquery-ui.css │ │ │ ├── jquery-ui.min.css │ │ │ ├── js │ │ │ ├── Chart.min.js │ │ │ ├── bootstrap.min.js │ │ │ ├── chart.js │ │ │ ├── jquery-1.12.4.min.js │ │ │ ├── jquery-ui-1.12.1.min.js │ │ │ ├── jquery-ui.js │ │ │ ├── jquery.js │ │ │ ├── popper.min.js │ │ │ └── xterm │ │ │ │ ├── dist │ │ │ │ └── addons │ │ │ │ │ └── terminado │ │ │ │ │ └── terminado.js │ │ │ │ └── xterm.js │ │ │ └── xterm.css │ ├── templates │ │ └── frontend │ │ │ ├── base.html │ │ │ ├── machines │ │ │ ├── detail │ │ │ │ ├── console.html │ │ │ │ ├── cpu.html │ │ │ │ ├── history.html │ │ │ │ ├── installations.html │ │ │ │ ├── miscellaneous.html │ │ │ │ ├── networkinterfaces.html │ │ │ │ ├── overview.html │ │ │ │ ├── pci.html │ │ │ │ ├── scsi.html │ │ │ │ ├── snippets │ │ │ │ │ ├── annotations.html │ │ │ │ │ ├── general.html │ │ │ │ │ ├── machinetitle.html │ │ │ │ │ ├── platform.html │ │ │ │ │ ├── reservation.html │ │ │ │ │ ├── sidebar.html │ │ │ │ │ ├── sidebar_virtualization.html │ │ │ │ │ ├── status.html │ │ │ │ │ └── system.html │ │ │ │ ├── usb.html │ │ │ │ ├── virtualization.html │ │ │ │ └── virtualization_add.html │ │ │ ├── history.html │ │ │ ├── list.html │ │ │ ├── machine.html │ │ │ ├── reserve.html │ │ │ ├── search.html │ │ │ ├── setup.html │ │ │ └── statistics.html │ │ │ ├── registration │ │ │ ├── login.html │ │ │ ├── new.html │ │ │ ├── password_reset.html │ │ │ └── preferences.html │ │ │ ├── snippet.filterbar.html │ │ │ ├── snippet.navlinks.html │ │ │ ├── snippet.paginator.html │ │ │ ├── snippet.search.html │ │ │ └── snippet.userbar.html │ ├── templatetags │ │ ├── __init__.py │ │ ├── filters.py │ │ └── tags.py │ ├── tests │ │ ├── __init__.py │ │ ├── admin │ │ │ ├── __init__.py │ │ │ ├── test_machine.py │ │ │ └── test_machine_views.py │ │ ├── fixtures │ │ │ └── serverconfigs.json │ │ └── user │ │ │ ├── __init__.py │ │ │ ├── fixtures │ │ │ └── users.json │ │ │ ├── test_change_password.py │ │ │ ├── test_create_account.py │ │ │ ├── test_ldap.py │ │ │ ├── test_login.py │ │ │ ├── test_logout.py │ │ │ ├── test_restore_password.py │ │ │ └── test_statistics.py │ ├── urls.py │ └── views │ │ ├── __init__.py │ │ ├── ajax.py │ │ ├── auth.py │ │ ├── machine.py │ │ ├── machines.py │ │ ├── regenerate.py │ │ ├── statistics.py │ │ └── user.py ├── meta │ ├── __init__.py │ ├── apps.py │ ├── data │ │ ├── __init__.py │ │ ├── ansible │ │ │ ├── ansible.cfg │ │ │ ├── inventory.template │ │ │ ├── roles │ │ │ │ ├── add_custom_facts │ │ │ │ │ ├── files │ │ │ │ │ │ └── facts.d │ │ │ │ │ │ │ ├── dmesg.fact │ │ │ │ │ │ │ ├── dmidecode.fact │ │ │ │ │ │ │ ├── hwinfo.fact │ │ │ │ │ │ │ ├── last.fact │ │ │ │ │ │ │ ├── lsmod.fact │ │ │ │ │ │ │ ├── lspci.fact │ │ │ │ │ │ │ ├── lsscsi.fact │ │ │ │ │ │ │ └── lsusb.fact │ │ │ │ │ └── tasks │ │ │ │ │ │ └── main.yml │ │ │ │ └── get_machine_information │ │ │ │ │ └── tasks │ │ │ │ │ └── main.yml │ │ │ └── site.yml │ │ ├── bin │ │ │ └── orthos-admin │ │ ├── config │ │ │ └── settings │ │ ├── logrotate │ │ │ └── orthos2 │ │ ├── nginx │ │ │ └── orthos2_nginx.conf │ │ ├── scripts │ │ │ ├── README │ │ │ ├── create_bridge.sh │ │ │ ├── dhcp_check_and_restart.sh │ │ │ ├── machine_get_cpu_flags.sh │ │ │ ├── machine_get_cpu_id.sh │ │ │ ├── machine_get_cpu_number.sh │ │ │ ├── machine_get_cpu_speed.sh │ │ │ ├── machine_get_cpu_type.sh │ │ │ ├── machine_get_firmware.sh │ │ │ ├── machine_get_installations.sh │ │ │ ├── machine_sync_motd.sh │ │ │ ├── server_1011_power.pl │ │ │ └── virt-create-rootfs-orthos │ │ ├── service │ │ │ ├── orthos2.service │ │ │ └── orthos2_taskmanager.service │ │ └── tmpfiles.d │ │ │ └── orthos2.conf │ └── management │ │ ├── __init__.py │ │ └── commands │ │ ├── __init__.py │ │ └── setup.py ├── settings.py ├── taskmanager │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── executer.py │ ├── fixtures │ │ └── dailytasks.json │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── taskmanager.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tasks │ │ ├── __init__.py │ │ ├── ansible.py │ │ ├── cobbler.py │ │ ├── daily.py │ │ ├── machinetasks.py │ │ ├── notifications.py │ │ ├── sconsole.py │ │ └── setup.py │ └── tests.py ├── urls.py ├── utils │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── cobbler.py │ ├── ipmf.py │ ├── machinechecks.py │ ├── misc.py │ ├── remote.py │ ├── remotepowertype.py │ ├── ssh.py │ └── tests │ │ ├── __init__.py │ │ ├── fixtures │ │ └── machines.json │ │ ├── test_cobbler.py │ │ └── test_misc.py └── wsgi.py ├── pyproject.toml ├── requirements-devel.txt ├── requirements.txt ├── setup.py ├── system-user-orthos.conf └── tox.ini /.obs/main.yaml: -------------------------------------------------------------------------------- 1 | workflow: 2 | steps: 3 | - trigger_services: 4 | project: systemsmanagement:orthos2:master 5 | package: orthos2 6 | - trigger_services: 7 | project: systemsmanagement:orthos2:master 8 | package: orthos-client 9 | filters: 10 | event: push 11 | branches: 12 | only: 13 | - master 14 | -------------------------------------------------------------------------------- /.obs/workflows.yml: -------------------------------------------------------------------------------- 1 | workflow: 2 | steps: 3 | - branch_package: 4 | source_project: systemsmanagement:orthos2:github_master_ci 5 | source_package: orthos2-master 6 | target_project: home:trenn 7 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/psf/black 3 | rev: 22.3.0 4 | hooks: 5 | - id: black 6 | args: ["--verbose", "--safe"] 7 | # It is recommended to specify the latest version of Python 8 | # supported by your project here, or alternatively use 9 | # pre-commit's default_language_version, see 10 | # https://pre-commit.com/#top_level-default_language_version 11 | language_version: python3.10 12 | additional_dependencies: ['click==8.0.4'] 13 | - repo: https://github.com/pycqa/isort 14 | # Configuration for isort is in "pyproject.toml" 15 | rev: 5.12.0 16 | hooks: 17 | - id: isort 18 | args: ["--filter-files"] 19 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/.prettierignore -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/.prettierrc -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # Add files or directories matching the regex patterns to the blacklist. The 4 | # regex matches against base names, not paths. 5 | ignore-patterns=^\d{4}_.*.py 6 | 7 | [MESSAGES CONTROL] 8 | 9 | disable=all, 10 | maybe-no-member, 11 | missing-function-docstring, 12 | missing-class-docstring, 13 | missing-module-docstring, 14 | 15 | enable=c-extension-no-member, 16 | unused-import, 17 | unused-variable, 18 | undefined-variable, 19 | raw-checker-failed, 20 | bad-inline-option, 21 | locally-disabled, 22 | file-ignored, 23 | suppressed-message, 24 | useless-suppression, 25 | deprecated-pragma, 26 | use-symbolic-message-instead, 27 | logging-format-interpolation, 28 | wrong-import-order, 29 | line-too-long, 30 | superfluous-parens, 31 | logging-not-lazy, 32 | missing-format-argument-key, 33 | bad-indentation, 34 | 35 | [FORMAT] 36 | 37 | # Maximum number of characters on a single line. 38 | max-line-length=120 39 | 40 | [SIMILARITIES] 41 | 42 | # Ignore imports when computing similarities. 43 | ignore-imports=no 44 | 45 | [IMPORTS] 46 | 47 | # Deprecated modules which should not be used, separated by a comma. 48 | deprecated-modules=optparse,tkinter.tix 49 | 50 | [CLASSES] 51 | 52 | # List of valid names for the first argument in a metaclass class method. 53 | valid-metaclass-classmethod-first-arg=cls 54 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml - Read the Docs configuration file 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | version: 2 5 | build: 6 | os: ubuntu-22.04 7 | tools: 8 | python: "3.11" 9 | 10 | # Build documentation in the docs/ directory with Sphinx 11 | sphinx: 12 | configuration: docs/conf.py 13 | 14 | # Optionally build your docs in additional formats such as PDF 15 | formats: 16 | - pdf 17 | 18 | # Optionally set the version of Python and requirements required to build your docs 19 | python: 20 | install: 21 | - requirements: docs/requirements.docs.txt -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution guide for Orthos 2 2 | 3 | Please try to create issues for any feature that you need or issue that you encounter. 4 | 5 | Please be aware that this project is in maintenance mode. We still welcome contributions to stabilize this project! For more details, see: 6 | 7 | For security issues and the Code of Conduct please refer to the dedicated documents. 8 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include orthos2/frontend/static * 2 | recursive-include orthos2/frontend/templates * 3 | recursive-include orthos2/data/static * 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gitter](https://badges.gitter.im/orthos2/community.svg)](https://gitter.im/orthos2/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 2 | [![Documentation Status](https://readthedocs.org/projects/orthos2/badge/?version=latest)](https://orthos2.readthedocs.io/en/latest/?badge=latest) 3 | [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) 4 | ![Pre-Commit Badge](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white) 5 | 6 | # Orthos - Machine Administration Tool 7 | 8 | ## Introduction 9 | 10 | Orthos is the machine administration tool of the development network at SUSE. It is used for following tasks: 11 | 12 | * getting the state of the machine 13 | * overview about the hardware 14 | * overview about the installed software (installations) 15 | * reservation of the machines 16 | * generating the DHCP configuration (via Cobbler) 17 | * reboot the machines remotely 18 | * managing remote (serial) consoles 19 | 20 | ## Installation/Setup 21 | 22 | Click [here](https://orthos2.readthedocs.io/en/latest/adminguide/setup.html) for the installation/setup instructions. 23 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Orthos 2 is a rolling release. Only the latest tag is supported. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | To report a security vulnerability please write a mail to [security@suse.com](mailto:security@suse.com). For more details and options how to 10 | contact SUSE's security department please refer to . 11 | -------------------------------------------------------------------------------- /cli/orthosrc: -------------------------------------------------------------------------------- 1 | [global] 2 | username = 3 | server = orthos2.arch.suse.de 4 | port = 443 5 | protocol = https 6 | token = 7 | 8 | [alias] 9 | reserved = query name, res_by, reserved_until where res_by 10 | not_reserved = query name where !res_by 11 | not_reserved_avail = query name where !res_by and status_login 12 | sles_15_sp4 = query name, inst_dist where !res_by and status_login and inst_dist =~ "sles%2015%20sp4" 13 | older_as_2015 = query name, bios_date where bios_date < 2015-01-01 14 | freemachines = query name, cpu_cores, ram, sconsole, rpower_type where !res_by and status_login 15 | mymachines = query name, cpu_cores, ram, res_by, rpower_type, sconsole, status_ping, status_login where res_by = $USERNAME 16 | lspci = query pci_slot, pci_vendorid, pci_vendor, pci_deviceid, pci_device, pci_svendorid, pci_svendorname, pci_sdeviceid, pci_sdevicename, pci_revision, pci_driver where name = 17 | sles_15 = query name, inst_dist where !res_by and status_login and inst_dist =~ "sles%2015" 18 | sles = query name, inst_dist where !res_by and status_login and inst_dist =~ "sle" 19 | opensuse = query name, inst_dist where !res_by and status_login and inst_dist =~ "openSUSE" 20 | -------------------------------------------------------------------------------- /cli/tests/test_alias.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "alias" command is working. 3 | """ 4 | 5 | import pathlib 6 | import unittest 7 | 8 | from . import OrthosCliTestCase 9 | 10 | 11 | class AliasTests(OrthosCliTestCase): 12 | def test_add(self) -> None: 13 | """ 14 | Test to verify that adding aliases via CLI is working. 15 | """ 16 | # Arrange 17 | expected = "test = query name" 18 | self.start_cli() 19 | # Uncomment the following line to debug the test 20 | # self.process.logfile = sys.stdout.buffer 21 | 22 | # Act 23 | if self.process is None: 24 | self.fail("CLI process not successfully spawned!") 25 | self.process.sendline("alias test query name") 26 | self.process.expect("(orthos 2.3.0:Anonymous)") 27 | 28 | # Assert 29 | self.stop_cli() 30 | config_file = pathlib.Path("~orthos/.config/orthosrc").expanduser() 31 | config_file_content = config_file.read_text().split("\n") 32 | # The file is not present 33 | self.assertEqual(config_file_content[1], expected) 34 | 35 | 36 | if __name__ == "__main__": 37 | unittest.main() 38 | -------------------------------------------------------------------------------- /cli/tests/test_auth.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "auth" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class AuthTests(OrthosCliTestCase): 11 | def test_login(self) -> None: 12 | """ 13 | Test to verify that a login to the Orthos server can work. Assumes "admin"/"admin" is present. 14 | """ 15 | # Arrange 16 | self.start_cli(username="admin") 17 | 18 | # Act 19 | if self.process is None: 20 | self.fail("CLI process not successfully spawned!") 21 | self.process.sendline("auth") 22 | self.process.expect("Orthos password for admin:") 23 | self.process.sendline("admin") 24 | 25 | # Assert 26 | self.process.expect("(orthos 2.3.0:admin)") 27 | self.stop_cli() 28 | 29 | 30 | if __name__ == "__main__": 31 | unittest.main() 32 | -------------------------------------------------------------------------------- /cli/tests/test_config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "config" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class ConfigTests(OrthosCliTestCase): 11 | def test_config(self) -> None: 12 | """ 13 | Test to verify that outputting the config of the CLI works as expected. 14 | """ 15 | # Arrange 16 | self.start_cli() 17 | # Uncomment the following line to debug the test 18 | # self.process.logfile = sys.stdout.buffer 19 | 20 | # Act 21 | if self.process is None: 22 | self.fail("CLI process not successfully spawned!") 23 | self.process.sendline("config") 24 | self.process.expect("(orthos 2.3.0:Anonymous)") 25 | if self.process.before is None: 26 | self.fail("CLI didn't communicate back properly!") 27 | output = self.process_output(self.process.before) 28 | 29 | # Assert 30 | self.stop_cli() 31 | self.assertEqual(len(output), 8) 32 | configuration = {} 33 | for _, item in enumerate(output[1:6]): 34 | tmp = item.strip("\r").split(":") 35 | configuration[tmp[0]] = tmp[1].strip("\t") 36 | self.assertEqual(len(configuration.keys()), 5) 37 | self.assertEqual(configuration.get("Port"), "8000") 38 | self.assertEqual(configuration.get("Server"), "localhost") 39 | self.assertEqual(configuration.get("User"), "Anonymous") 40 | self.assertEqual(configuration.get("Protocol"), "http") 41 | 42 | 43 | if __name__ == "__main__": 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /cli/tests/test_delete.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "delete" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class DeleteTests(OrthosCliTestCase): 11 | @unittest.skip("Too much setup at the moment") 12 | def test_delete(self) -> None: 13 | self.assertTrue(False) 14 | -------------------------------------------------------------------------------- /cli/tests/test_exit.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "exit" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class ExitTests(OrthosCliTestCase): 11 | def test_exit(self) -> None: 12 | """ 13 | Test to verify that the CLI can be exited. 14 | """ 15 | # Arrange 16 | self.start_cli() 17 | 18 | # Act 19 | if self.process is None: 20 | self.fail("CLI process not successfully spawned!") 21 | self.process.sendline("quit") 22 | 23 | # Assert 24 | self.process.expect("Good bye, have a lot of fun...") 25 | self.process.wait() 26 | self.assertEqual(self.process.exitstatus, 0) 27 | 28 | 29 | if __name__ == "__main__": 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /cli/tests/test_info.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "exit" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class InfoTests(OrthosCliTestCase): 11 | @unittest.skip("Too much setup at the moment") 12 | def test_info(self) -> None: 13 | # Arrange 14 | self.start_cli(username="admin") 15 | 16 | # Act 17 | if self.process is None: 18 | self.fail("CLI process not successfully spawned!") 19 | self.process.sendline("info ") 20 | 21 | # Cleanup 22 | self.stop_cli() 23 | 24 | # Assert 25 | self.assertTrue(False) 26 | 27 | 28 | if __name__ == "__main__": 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /cli/tests/test_query.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "query" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class QueryTests(OrthosCliTestCase): 11 | @unittest.skip("Too much setup at the moment") 12 | def test_query_fields(self) -> None: 13 | # Arrange 14 | self.start_cli(username="admin") 15 | 16 | # Act 17 | if self.process is None: 18 | self.fail("CLI process not successfully spawned!") 19 | self.process.sendline("query fqdn") 20 | 21 | # Cleanup 22 | self.stop_cli() 23 | 24 | # Assert 25 | self.assertTrue(False) 26 | 27 | @unittest.skip("Too much setup at the moment") 28 | def test_query_fields_where(self) -> None: 29 | # Arrange 30 | self.start_cli(username="admin") 31 | 32 | # Act 33 | if self.process is None: 34 | self.fail("CLI process not successfully spawned!") 35 | self.process.sendline("query fqdn where cpu_model =~ Intel") 36 | 37 | # Cleanup 38 | self.stop_cli() 39 | 40 | # Assert 41 | self.assertTrue(False) 42 | 43 | 44 | if __name__ == "__main__": 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /cli/tests/test_regenerate.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "regenerate" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class RegenerateTests(OrthosCliTestCase): 11 | @unittest.skip("Too much setup at the moment") 12 | def test_regenerate(self) -> None: 13 | self.assertTrue(False) 14 | 15 | 16 | if __name__ == "__main__": 17 | unittest.main() 18 | -------------------------------------------------------------------------------- /cli/tests/test_release.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "release" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class ReleaseTests(OrthosCliTestCase): 11 | @unittest.skip("Too much setup at the moment") 12 | def test_fqdn(self) -> None: 13 | # Assert 14 | self.assertTrue(False) 15 | 16 | @unittest.skip("Too much setup at the moment") 17 | def test_hostname(self) -> None: 18 | # Assert 19 | self.assertTrue(False) 20 | 21 | 22 | if __name__ == "__main__": 23 | unittest.main() 24 | -------------------------------------------------------------------------------- /cli/tests/test_reservationhistory.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "reservationhistory" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class ReservationhistoryTests(OrthosCliTestCase): 11 | @unittest.skip("Too much setup at the moment") 12 | def test_reserverationhistory(self) -> None: 13 | # Arrange 14 | self.start_cli() 15 | 16 | # Act 17 | if self.process is None: 18 | self.fail("CLI process not successfully spawned!") 19 | self.process.sendline("reservationhistory ") 20 | 21 | # Cleanup 22 | self.stop_cli() 23 | 24 | # Assert 25 | self.assertTrue(False) 26 | 27 | 28 | if __name__ == "__main__": 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /cli/tests/test_reserve.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "reserve" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class ReserveTests(OrthosCliTestCase): 11 | @unittest.skip("Too much setup at the moment") 12 | def test_reserve(self) -> None: 13 | # Arrange 14 | self.start_cli() 15 | 16 | # Act 17 | if self.process is None: 18 | self.fail("CLI process not successfully spawned!") 19 | self.process.sendline("reserve ") 20 | 21 | # Cleanup 22 | self.stop_cli() 23 | 24 | # Assert 25 | self.assertTrue(False) 26 | 27 | 28 | if __name__ == "__main__": 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /cli/tests/test_serverconfig.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "serverconfig" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class ServerconfigTests(OrthosCliTestCase): 11 | @unittest.skip("Too much setup at the moment") 12 | def test_serverconfig(self) -> None: 13 | # Arrange 14 | self.start_cli(username="admin") 15 | self.login_cli() 16 | # Uncomment the following line to debug the test 17 | # self.process.logfile = sys.stdout.buffer 18 | 19 | # Act 20 | if self.process is None: 21 | raise RuntimeError("CLI process not successfully spawned!") 22 | self.process.sendline("serverconfig") 23 | self.process.expect("") 24 | 25 | # Assert 26 | self.stop_cli() 27 | self.assertTrue(False) 28 | 29 | 30 | if __name__ == "__main__": 31 | unittest.main() 32 | -------------------------------------------------------------------------------- /cli/tests/test_setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests that are verifying that all functionality that is related to the "setup" command is working. 3 | """ 4 | 5 | import unittest 6 | 7 | from . import OrthosCliTestCase 8 | 9 | 10 | class SetupTests(OrthosCliTestCase): 11 | @unittest.skip("Too much setup at the moment") 12 | def test_list(self) -> None: 13 | # Arrange 14 | self.start_cli() 15 | 16 | # Act 17 | if self.process is None: 18 | self.fail("CLI process not successfully spawned!") 19 | self.process.sendline("setup list") 20 | 21 | # Cleanup 22 | self.stop_cli() 23 | 24 | # Assert 25 | self.assertTrue(False) 26 | 27 | @unittest.skip("Too much setup at the moment") 28 | def test_specific(self) -> None: 29 | # Arrange 30 | self.start_cli() 31 | 32 | # Act 33 | if self.process is None: 34 | self.fail("CLI process not successfully spawned!") 35 | self.process.sendline("setup ") 36 | 37 | # Cleanup 38 | self.stop_cli() 39 | 40 | # Assert 41 | self.assertTrue(False) 42 | 43 | 44 | if __name__ == "__main__": 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | orthos2: 3 | build: 4 | context: . 5 | dockerfile: docker/develop-tw.dockerfile 6 | hostname: orthos2.orthos2.test 7 | env_file: 8 | - "orthos2.env" 9 | - "db.env" 10 | cap_add: 11 | - NET_RAW 12 | volumes: 13 | - ./:/code 14 | ports: 15 | - "8000:8000" 16 | depends_on: 17 | database: 18 | condition: service_healthy 19 | healthcheck: 20 | test: [ "CMD", "curl", "-f", "http://localhost:8000" ] 21 | interval: 30s 22 | timeout: 10s 23 | retries: 5 24 | orthos2_taskmanager: 25 | build: 26 | context: . 27 | dockerfile: docker/develop-tw.dockerfile 28 | environment: 29 | ORTHOS2_MODE: "taskmanager" 30 | env_file: 31 | - "orthos2.env" 32 | - "db.env" 33 | cap_add: 34 | - NET_RAW 35 | volumes: 36 | - ./:/code 37 | depends_on: 38 | database: 39 | condition: service_healthy 40 | orthos2: 41 | condition: service_healthy 42 | database: 43 | image: postgres 44 | restart: always 45 | hostname: database.orthos2.test 46 | environment: 47 | POSTGRES_USER: orthos 48 | POSTGRES_PASSWORD: orthos2 49 | healthcheck: 50 | test: [ "CMD-SHELL", "pg_isready -h 127.0.0.1 -d $${POSTGRES_USER} -U $${POSTGRES_USER}" ] 51 | interval: 1s 52 | timeout: 5s 53 | retries: 10 54 | cobbler: 55 | hostname: cobbler.orthos2.test 56 | build: 57 | context: . 58 | dockerfile: docker/cobbler.dockerfile 59 | ports: 60 | # - "22:22" 61 | - "80:80" 62 | - "443:443" 63 | serial_console: 64 | hostname: sconsole.orthos2.test 65 | build: 66 | context: . 67 | dockerfile: docker/serial-console.dockerfile 68 | #ports: 69 | # - 22:22 70 | machine_bmc: 71 | hostname: bmc.orthos2.test 72 | build: 73 | context: docker 74 | dockerfile: bmc.dockerfile 75 | ports: 76 | - "9001:9001" 77 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Development Container 2 | 3 | The workflow is as follows (from the project root folder): 4 | 5 | ```shell 6 | podman build -f docker/develop.dockerfile -t orthos2-dev:latest . 7 | podman run -it --entrypoint="/bin/bash" -v $PWD:/code --rm -p 8000:8000 localhost/orthos2-dev:latest 8 | ``` 9 | 10 | If you are inside the container the usage is as follows: 11 | 12 | ```shell 13 | python3 manage.py test 14 | ``` 15 | 16 | Now if you want to serve the webinterface you need to do something a little weird: 17 | 18 | ```shell 19 | /code/docker/devel-server.sh 20 | ``` 21 | 22 | If you messed something up just hit "Ctrl + C" and "Ctrl + D" and use the `podman run ...` command to spawn a new 23 | container. 24 | 25 | What might be useful is if you load some default fixtures for testing: 26 | 27 | ```shell 28 | python3 manage.py loaddata orthos2/data/fixtures/architectures.json 29 | python3 manage.py loaddata orthos2/data/fixtures/platforms.json 30 | python3 manage.py loaddata orthos2/data/fixtures/serialconsoletypes.json 31 | python3 manage.py loaddata orthos2/data/fixtures/systems.json 32 | python3 manage.py loaddata orthos2/data/fixtures/vendors.json 33 | ``` 34 | -------------------------------------------------------------------------------- /docker/bmc.dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.opensuse.org/opensuse/leap:15.5 2 | 3 | RUN zypper --gpg-auto-import-keys ref && \ 4 | zypper in -y python3-devel python3-pip gcc 5 | # https://github.com/shapeblue/ipmisim 6 | RUN pip3 install ipmisim 7 | 8 | WORKDIR /code 9 | COPY bmc.py /code 10 | 11 | ENTRYPOINT ["python3", "bmc.py"] 12 | -------------------------------------------------------------------------------- /docker/bmc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import signal 4 | import socketserver 5 | import threading 6 | from typing import Optional 7 | 8 | from ipmisim.ipmisim import IpmiServer 9 | 10 | done_event = threading.Event() 11 | server: Optional[socketserver.UDPServer] = None 12 | 13 | 14 | def shutdownHandler(msg, evt): 15 | global server 16 | 17 | if server is not None: 18 | print( 19 | "shutdown handler called. shutting down on thread id:%x" 20 | % (id(threading.current_thread())) 21 | ) 22 | server.shutdown() 23 | server.server_close() 24 | print("shutdown complete") 25 | else: 26 | print("No server to shutdown") 27 | evt.set() 28 | return 29 | 30 | 31 | def terminate(signal, frame): 32 | print("terminate handle on thread id:%x" % (id(threading.current_thread()))) 33 | t = threading.Thread(target=shutdownHandler, args=("SIGTERM received", done_event)) 34 | t.start() 35 | 36 | 37 | def main(address: str = "0.0.0.0", port: int = 9001): 38 | global server 39 | print("main thread id:%x" % (id(threading.current_thread()))) 40 | signal.signal(signal.SIGTERM, terminate) 41 | server = socketserver.UDPServer((address, port), IpmiServer) 42 | server.serve_forever() 43 | done_event.wait() 44 | 45 | 46 | if __name__ == "__main__": 47 | main() 48 | -------------------------------------------------------------------------------- /docker/cobbler-startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /usr/bin/ssh-keygen -A 4 | /usr/sbin/sshd && cobblerd && apachectl -D FOREGROUND 5 | -------------------------------------------------------------------------------- /docker/cobbler.dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.opensuse.org/systemsmanagement/cobbler/github-ci/containers/cobbler-test-github:release33 2 | 3 | RUN zypper --gpg-auto-import-keys ref \ 4 | && zypper in -y cobbler lynx w3m 5 | 6 | COPY ./docker/cobbler-startup.sh / 7 | 8 | # Set entrypoint for development 9 | CMD ["/cobbler-startup.sh"] 10 | -------------------------------------------------------------------------------- /docker/devel-server.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | 4 | server_start() { 5 | git config --global --add safe.directory /code 6 | OLD_BRANCH=$(git branch --show-current) 7 | git stash -u 8 | git switch master 9 | python3 manage.py migrate 10 | if [ -f "dump.json" ]; then 11 | python3 manage.py flush --noinput 12 | python3 manage.py loaddata dump.json 13 | fi 14 | git switch "$OLD_BRANCH" 15 | git stash pop 16 | python3 manage.py migrate 17 | DJANGO_SUPERUSER_PASSWORD='admin' python3 manage.py createsuperuser --noinput --username admin --email admin@example.com 18 | python3 manage.py shell < /code/docker/django-generate-admin-token 19 | python3 manage.py runserver 0.0.0.0:8000 20 | } 21 | 22 | taskmanager_start() { 23 | # Wait for it 24 | until curl --output /dev/null --silent --head --fail http://orthos2.orthos2.test:8000; do 25 | echo "Waiting for main application to become available" 26 | sleep 5 27 | done 28 | # Moves files into place 29 | python3 manage.py setup ansible --buildroot="/" 30 | # Start server 31 | python3 manage.py taskmanager --start 32 | } 33 | 34 | if [ "$ORTHOS2_MODE" == "taskmanager" ]; then 35 | taskmanager_start 36 | else 37 | server_start 38 | fi 39 | -------------------------------------------------------------------------------- /docker/develop-leap156.dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.opensuse.org/opensuse/leap:15.6 2 | 3 | # Install system dependencies 4 | RUN zypper in -y \ 5 | shadow \ 6 | python3 \ 7 | python3-devel \ 8 | python3-pip \ 9 | python3-setuptools \ 10 | gcc \ 11 | openldap2-devel \ 12 | cyrus-sasl-devel \ 13 | jq \ 14 | sudo \ 15 | git \ 16 | openssh \ 17 | ansible 18 | 19 | # Install requirements via zypper 20 | RUN zypper in -y \ 21 | python311-Django \ 22 | python311-django-extensions \ 23 | python311-paramiko \ 24 | python311-djangorestframework \ 25 | python311-validators \ 26 | python311-netaddr \ 27 | python311-psycopg2 \ 28 | python311-pytz \ 29 | python311-django-auth-ldap 30 | 31 | # Test dependencies 32 | RUN zypper in -y \ 33 | python311-flake8 \ 34 | python311-coverage \ 35 | python311-isort \ 36 | python311-pytest \ 37 | python311-django-webtest \ 38 | python311-pexpect \ 39 | iputils 40 | 41 | # Create required user 42 | RUN groupadd -r orthos 43 | RUN useradd -r -g orthos -d /var/lib/orthos2 -s /bin/bash -c "orthos account" orthos 44 | 45 | # Create required directories 46 | RUN mkdir -p /etc/nginx/conf.d /var/lib/orthos2 /var/log/orthos2 /var/lib/orthos2/database /usr/lib/orthos2/ansible /run/orthos2/ansible /run/orthos2/ansible_lastrun /run/orthos2/ansible_archive 47 | RUN touch /var/log/orthos2/default.log 48 | RUN chmod o+w /var/log/orthos2/default.log 49 | RUN chown -R orthos:orthos /var/log/orthos2 /var/lib/orthos2 /usr/lib/orthos2 /usr/lib/orthos2/ansible /run/orthos2 /run/orthos2/ansible /run/orthos2/ansible_lastrun /run/orthos2/ansible_archive 50 | 51 | # Setup container for work 52 | WORKDIR /code 53 | VOLUME /code 54 | EXPOSE 8000 55 | USER orthos 56 | 57 | # Set entrypoint for development 58 | CMD ["/code/docker/devel-server.sh"] 59 | -------------------------------------------------------------------------------- /docker/develop-tw.dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.opensuse.org/opensuse/tumbleweed:latest 2 | 3 | # Install system dependencies 4 | RUN zypper in -y \ 5 | shadow \ 6 | python3 \ 7 | python3-devel \ 8 | python3-pip \ 9 | python3-setuptools \ 10 | gcc \ 11 | openldap2-devel \ 12 | cyrus-sasl-devel \ 13 | jq \ 14 | sudo \ 15 | git \ 16 | openssh \ 17 | ansible 18 | 19 | # Install requirements via zypper 20 | RUN zypper in -y \ 21 | python3-Django \ 22 | python3-django-extensions \ 23 | python3-paramiko \ 24 | python3-djangorestframework \ 25 | python3-validators \ 26 | python3-netaddr \ 27 | python3-psycopg2 \ 28 | python3-pytz \ 29 | python3-django-auth-ldap 30 | 31 | # Test dependencies 32 | RUN zypper in -y \ 33 | python3-flake8 \ 34 | python3-coverage \ 35 | python3-isort \ 36 | python3-pytest \ 37 | python3-django-webtest \ 38 | python3-pexpect \ 39 | iputils 40 | 41 | # Create required user 42 | ARG USER=1000 43 | RUN groupadd -r orthos 44 | RUN useradd -r -u $USER -g orthos -d /var/lib/orthos2 -s /bin/bash -c "orthos account" orthos 45 | 46 | # Create required directories 47 | RUN mkdir -p /etc/nginx/conf.d /var/lib/orthos2 /var/log/orthos2 /var/lib/orthos2/database /usr/lib/orthos2/ansible /run/orthos2/ansible /run/orthos2/ansible_lastrun /run/orthos2/ansible_archive 48 | RUN touch /var/log/orthos2/default.log 49 | RUN chmod o+w /var/log/orthos2/default.log 50 | RUN chown -R orthos:orthos /var/log/orthos2 /var/lib/orthos2 /usr/lib/orthos2 /usr/lib/orthos2/ansible /run/orthos2 /run/orthos2/ansible /run/orthos2/ansible_lastrun /run/orthos2/ansible_archive 51 | 52 | # Setup container for work 53 | WORKDIR /code 54 | VOLUME /code 55 | EXPOSE 8000 56 | USER orthos 57 | 58 | # Set entrypoint for development 59 | CMD ["/code/docker/devel-server.sh"] 60 | -------------------------------------------------------------------------------- /docker/django-generate-admin-token: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import pathlib 4 | 5 | from django.contrib.auth.models import User 6 | from rest_framework.authtoken.models import Token 7 | 8 | token = Token.objects.create(user=User.objects.get(username="admin")) 9 | pathlib.Path("/var/lib/orthos2/admin-token").write_text(token.key) 10 | -------------------------------------------------------------------------------- /docker/serial-console-startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /usr/bin/ssh-keygen -A 4 | /usr/sbin/sshd -D 5 | -------------------------------------------------------------------------------- /docker/serial-console.dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.opensuse.org/opensuse/tumbleweed:latest 2 | 3 | # https://github.com/openSUSE/cscreen 4 | RUN zypper in -y cscreen openssh-server 5 | 6 | COPY ./docker/serial-console-startup.sh / 7 | 8 | # Set entrypoint for development 9 | CMD ["/serial-console-startup.sh"] 10 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/adminguide.rst: -------------------------------------------------------------------------------- 1 | .. _admin-guide: 2 | 3 | ********************* 4 | Administrator`s Guide 5 | ********************* 6 | 7 | These pages are partly very technical. You should read the User Guide before to understand the basic concept and the goals of Orthos. 8 | This document describes how to create and administrate groups and users, architectures, domains, 9 | machines, machine groups, serial console types, systems and enclosures, vendors and platforms. Furthermore, the 10 | administration of the Orthos server will be discussed. 11 | 12 | .. toctree:: 13 | :maxdepth: 1 14 | 15 | Setup 16 | Server Configuration 17 | WebSockets 18 | Tokens 19 | Groups and Users 20 | Machines 21 | Architectures 22 | Domains 23 | Systems and Enclosures 24 | Vendors and Platforms 25 | Serial Console Type 26 | Machine Groups 27 | -------------------------------------------------------------------------------- /docs/adminguide/architectures.rst: -------------------------------------------------------------------------------- 1 | ************* 2 | Architectures 3 | ************* 4 | 5 | Machine architectures can be set up, the most common ones are already preconfigured. Machine objects have an architecture that can then be mapped to the machine. 6 | 7 | .. code-block:: 8 | 9 | ----------- 10 | | Machine | 11 | ----------- 12 | | 13 | | 14 | ---------------- 15 | | Architecture | 16 | ---------------- 17 | 18 | Architecture fields description 19 | ############################### 20 | 21 | Name (required) 22 | =============== 23 | 24 | Name of the architecture. 25 | 26 | Example: s390x, x86_64, ppc64le etc. 27 | 28 | DHCP filename 29 | ============= 30 | 31 | Path to the boot files on the TFTP server. 32 | 33 | Contact email 34 | ============= 35 | 36 | Email address to the person who is the contact person for an architecture. 37 | 38 | Write DHCPv4 39 | ============ 40 | 41 | Enable/disable writing a DHCPv4 file for this architecture. 42 | 43 | Write DHCPv6 44 | ============ 45 | 46 | Enable/disable writing a DHCPv6 file for this architecture. 47 | -------------------------------------------------------------------------------- /docs/adminguide/domains.rst: -------------------------------------------------------------------------------- 1 | ******* 2 | Domains 3 | ******* 4 | 5 | Here you can set up a domain under Orthos, a domain must also be set up on a DNS server. That Orthos can install 6 | machines, it is absolutely necessary to specify the DHCP server, TFTP server and the architectures. Orthos is able to 7 | write the DHCP file for these domains.The FQDN of a machine object is bound to a domain. 8 | 9 | .. code-block:: 10 | 11 | ----------- 12 | | Machine | 13 | | | 14 | ---------- ----------- 15 | | Domain |-----| FQDN | 16 | ---------- ----------- 17 | 18 | Domain fields description 19 | ######################### 20 | 21 | Name (required) 22 | =============== 23 | 24 | The domain name must be a valid domain, this has to be stored on the DNS and the domain must be valid according to the valid domain endings configured in Server Configurations (key domain.validendings). 25 | 26 | Example: Key: suse.de, suse.cz 27 | 28 | DHCP Server 29 | =========== 30 | 31 | Responsible DHCP server for the domain. 32 | 33 | Example: music.arch.suse.de 34 | 35 | TFTP Server 36 | =========== 37 | 38 | TFTP server from which you are installing your machines. Here, for example, are: Grub2 files, Installation Scripts, Installation Images, Autoyast files and so on. 39 | 40 | Example: music.arch.suse.de 41 | 42 | Setup architectures 43 | =================== 44 | 45 | A domain can contain several architectures, for these a corresponding DHCP file is then written and the appropriate installation files and scripts are offered. 46 | 47 | Example: x86_64, ppc64le, s390x 48 | 49 | Setup machine groups 50 | ==================== 51 | 52 | Here it is possible to add machine groups. 53 | 54 | -------------------------------------------------------------------------------- /docs/adminguide/machine_groups.rst: -------------------------------------------------------------------------------- 1 | ************** 2 | Machine Groups 3 | ************** 4 | 5 | In Orthos it is possible to group machines. Users can be assigned to one or more machine groups. Only users assigned to 6 | a group of machines can reserve and work with this set of machines. It is possible to assign different administrative 7 | rights. 8 | 9 | .. code-block:: 10 | 11 | -------------------- 12 | | Privileged Users | 13 | -------------------- 14 | | Users | 15 | -------------------- ----------- 16 | | | Machine | 17 | | /----------- 18 | ----------------- / 19 | | Machine Group |----< 20 | ----------------- \ 21 | \----------- 22 | | Machine | 23 | ----------- 24 | 25 | Machine fields description 26 | ########################## 27 | 28 | Name (required) 29 | =============== 30 | 31 | Name of the machine group. 32 | 33 | Comment 34 | ======= 35 | 36 | Comment about the machine group. 37 | 38 | DHCP filename 39 | ============= 40 | 41 | Path to the boot files on the TFTP server. 42 | 43 | Write DHCPv4 44 | ============ 45 | 46 | Enable/disable writing a DHCPv4 file for this machine group. 47 | 48 | Write DHCPv6 49 | ============ 50 | 51 | Enable/disable writing a DHCPv6 file for this machine group. 52 | 53 | Use machines architecture for setup 54 | =================================== 55 | 56 | It is possible to set the setup settings of the associated machine architecture for the machine group. 57 | 58 | USER 59 | ==== 60 | 61 | Add/delete users to machine group and assign administrative rights. 62 | -------------------------------------------------------------------------------- /docs/adminguide/serial_console_type.rst: -------------------------------------------------------------------------------- 1 | ******************* 2 | Serial Console Type 3 | ******************* 4 | 5 | Here you can set up the different ways to access a serial console. The best known ones like IPMI, ILO2, Telnet, libvirt etc. are already predefined. The serial console type can then be converted into a machine object under: Machine > SERIAL CONSOLE > Type can be set. 6 | 7 | .. code-block:: 8 | 9 | ----------- ----------------------- 10 | | Machine |-----| Serial Console Type | 11 | ----------- ----------------------- 12 | 13 | In the following are the fields for the Serial Console Type with explanations. 14 | 15 | Name (required) 16 | ############### 17 | 18 | Name of the Serial Console Type. 19 | 20 | Command 21 | ####### 22 | 23 | Command which is required to access the serial console. In Orthos you use variables/objects to create a serial console 24 | type. These are written in double curly brackets ``{{ ... }}``. 25 | 26 | Example: ``virsh -c lxc+ssh://root@{{ machine.hypervisor.fqdn }}/system console {{ machine.hostname }}`` 27 | 28 | The objects are taken from the machine object or the respective entries at the machine. Such as, for example. 29 | 30 | - Hostname = machine.Hostname 31 | - BMC FQDN = machine.bmc.fqdn 32 | - Hypervisor FQDN = machine.hypervisor.fqdn 33 | - Console Server FQDN = console_server.fqdn 34 | - IPMI User = ipmi.user 35 | - IPMI Password = ipmi.password 36 | 37 | Comment 38 | ####### 39 | 40 | Additional information to the Serial Console Type. 41 | 42 | -------------------------------------------------------------------------------- /docs/adminguide/systems_and_enclosures.rst: -------------------------------------------------------------------------------- 1 | ********************** 2 | Systems and Enclosures 3 | ********************** 4 | 5 | Systems 6 | ####### 7 | 8 | Systems are assigned to the machines accordingly, here the type of machine is determined. Most known systems are already stored in Orthos. 9 | 10 | .. code-block:: 11 | 12 | ------------- 13 | | Enclosure | 14 | ------------- 15 | | 16 | | 17 | ------------- ------------ 18 | | Machine |-----| System | 19 | ------------- ------------ 20 | 21 | System fields description: 22 | 23 | Name (required) 24 | =============== 25 | 26 | Exact name of the system. 27 | 28 | Example: BMC, Desktop, LPAR PowerPC etc. 29 | 30 | Virtual 31 | ======= 32 | 33 | Use this if the system a virtual system (VM Gast). 34 | 35 | Administrative 36 | ============== 37 | 38 | Machines that are declared as administrative are machines that belong to the Orthos system and network management. These machines cannot be reserved by the user. 39 | 40 | Example: RemotePower 41 | 42 | Enclosures 43 | ########## 44 | 45 | Under Enclosures the case (unit) is defined. A parent name for a unit with several devices can be defined. Several machines can then be assigned to the enclosure. If you create a machine object and you do not specify an enclosure, the name of the enclosure is formed from the FQDN of the machine. 46 | 47 | .. code-block:: 48 | 49 | ------------- 50 | | Platform | 51 | ------------- 52 | | 53 | | 54 | ------------- ----------- 55 | | Enclosure |-----| Machine | 56 | ------------- ----------- 57 | 58 | System fields description: 59 | 60 | Name (required) 61 | =============== 62 | 63 | Name of the Enclosure. 64 | 65 | Platform 66 | ======== 67 | 68 | Here the machine platform is specified, this usually comes via the vendor. 69 | 70 | Description 71 | =========== 72 | 73 | Since Enclosure is the generic term of a complete system, this should be as precise as possible. 74 | -------------------------------------------------------------------------------- /docs/adminguide/tokens.rst: -------------------------------------------------------------------------------- 1 | ****** 2 | Tokens 3 | ****** 4 | 5 | Usually no administration is required for tokens. When users use the command line client for the first time, they must 6 | authenticate themselves with the user / password, after which a token is written for the user. After the process, it is 7 | no longer necessary to log on via CLI, since this is then done via the token. 8 | 9 | .. code-block:: 10 | 11 | ----------------- 12 | | CLI | ----------------- 13 | | User/Password |---->----| | 14 | ----------------- | Orthos Server | 15 | ----------------- | User -> Token | 16 | | CLI |----<----| | 17 | | User / Token | ----------------- 18 | ----------------- -------------------------------------------------------------------------------- /docs/adminguide/vendors_and_platforms.rst: -------------------------------------------------------------------------------- 1 | ********************* 2 | Vendors and Platforms 3 | ********************* 4 | 5 | Under Vendors you can enter the machine manufacturer, This serves together with the platforms to recognize a machine or 6 | machines of the same type. Most manufacturers are already stored in the Orthos system. With platforms you can set the 7 | machine family name (unit name). It goes hand in hand with the vendor, the platform name is determined by the vendor. 8 | 9 | .. code-block:: 10 | 11 | ------------- 12 | | Vendor | 13 | ------------- 14 | | 15 | | 16 | ------------- 17 | | Platform | 18 | ------------- 19 | | 20 | | 21 | ------------- ----------- 22 | | Enclosure |-----| Machine | 23 | ------------- ----------- 24 | 25 | Vendor fields description 26 | ######################### 27 | 28 | Name (required) 29 | =============== 30 | 31 | Name of the vendor. 32 | 33 | Example: AMD, IBM, Raritan, Dell, Intel, SGI etc. 34 | 35 | Platform fields description 36 | ########################### 37 | 38 | Name (required) 39 | =============== 40 | 41 | Name of the platform specified by the manufacturer. 42 | On x86 systems you often get an idea via dmidecode command, e.g.: 43 | ` 44 | dmidecode -s system-product-name 45 | Latitude E7470 46 | ` 47 | 48 | But this info is often empty or wrong, especially on early developement machines. 49 | Therefore it has to be filled manually. 50 | It should be a meaningful name, by which people who are familiar with products 51 | of the vendor have an idea what kind of machine this is (how old, features, etc.). 52 | 53 | Vendor (required) 54 | ================= 55 | 56 | Here a vendor is specified and the connection between platform and vendor created. 57 | 58 | Example: AMD, IBM, Raritan, Dell, Intel, SGI etc. 59 | 60 | Cartridge/Blade 61 | =============== 62 | 63 | If the platform is a cartridge/blade system, a hook must be set here. 64 | 65 | Description 66 | =========== 67 | 68 | More information about the platform. 69 | -------------------------------------------------------------------------------- /docs/adminguide/websocket.rst: -------------------------------------------------------------------------------- 1 | .. `websockets`_ 2 | 3 | ********** 4 | WebSockets 5 | ********** 6 | 7 | Serial console in the web browser 8 | ################################# 9 | 10 | Orthos provides the ability to use an interactive serial console in the web browser for each machine that has an entry 11 | for a serial console. The WebSocket communication protocol is used for this. To start the WebSocket service, the 12 | following command line must be executed (no HTTPS/TLS): 13 | 14 | .. code-block:: 15 | 16 | $ orthos-admin serialconsole-ws --start 17 | Start websocket daemon on port 8010... 18 | 19 | 20 | With HTTPS/TLS: 21 | 22 | .. code-block:: 23 | 24 | $ orthos-admin serialconsole-ws --start --wss\ 25 | --crt \ 26 | --key 27 | Start websocket daemon on port 8010... 28 | 29 | 30 | Example: 31 | 32 | .. code-block:: 33 | 34 | $ orthos-admin serialconsole-ws --start --wss\ 35 | --crt /etc/apache2/ssl.crt/orthos.network.tld.crt\ 36 | --key /etc/apache2/ssl.key/orthos.network.tld.pem 37 | Start websocket daemon on port 8010... 38 | 39 | 40 | Further configuraton information can be found in the :ref:`admin-guide` (``websocket.*``). 41 | 42 | When the service establishes a SSH connection to invoke a command on a remote server, a SSH key without password is 43 | recommended. The key path just needs to be added to the SSH command (``-i ``). 44 | 45 | The output is available in the machine view under the tab ``Serial Console``. 46 | 47 | This feature was tested with Chromium Version 62.0.3202.89 (64-bit) and Firefox 52.5.0 (64-bit). 48 | -------------------------------------------------------------------------------- /docs/img/userguide/00_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/00_architecture.png -------------------------------------------------------------------------------- /docs/img/userguide/01_login_screen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/01_login_screen.jpg -------------------------------------------------------------------------------- /docs/img/userguide/02_landingpage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/02_landingpage.jpg -------------------------------------------------------------------------------- /docs/img/userguide/03_top_menu_overviews.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/03_top_menu_overviews.jpg -------------------------------------------------------------------------------- /docs/img/userguide/04_arch_quickfilter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/04_arch_quickfilter.jpg -------------------------------------------------------------------------------- /docs/img/userguide/05_machine_page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/05_machine_page.jpg -------------------------------------------------------------------------------- /docs/img/userguide/06_machine_infos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/06_machine_infos.jpg -------------------------------------------------------------------------------- /docs/img/userguide/07_machine_status.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/07_machine_status.jpg -------------------------------------------------------------------------------- /docs/img/userguide/08_machine_annotations.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/08_machine_annotations.jpg -------------------------------------------------------------------------------- /docs/img/userguide/09_machine_actions.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/09_machine_actions.jpg -------------------------------------------------------------------------------- /docs/img/userguide/10_machine_release.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/10_machine_release.jpg -------------------------------------------------------------------------------- /docs/img/userguide/11_machine_virtual.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/11_machine_virtual.jpg -------------------------------------------------------------------------------- /docs/img/userguide/12_machine_virtual_gast.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/docs/img/userguide/12_machine_virtual_gast.jpg -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Orthos2 documentation master file, created by 2 | sphinx-quickstart on Mon Nov 9 16:57:00 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Orthos2's documentation! 7 | =================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 1 11 | :caption: Contents: 12 | 13 | Adminstrator Guide 14 | User Guide 15 | Command Line Client 16 | 17 | 18 | 19 | Indices and tables 20 | ================== 21 | 22 | * :ref:`genindex` 23 | * :ref:`modindex` 24 | * :ref:`search` 25 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.docs.txt: -------------------------------------------------------------------------------- 1 | # Requirements for rtd 2 | django>=3.1 3 | django-extensions 4 | paramiko 5 | djangorestframework 6 | validators 7 | netaddr 8 | sphinx_rtd_theme 9 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orthos2.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | plugins = 3 | mypy_django_plugin.main, 4 | mypy_drf_plugin.main 5 | 6 | [mypy.plugins.django-stubs] 7 | django_settings_module = "orthos2.settings" 8 | -------------------------------------------------------------------------------- /orthos2.rpmlintrc: -------------------------------------------------------------------------------- 1 | # On Deb-based distros the deduplication would fail 2 | addFilter("orthos2.noarch: W: files-duplicate /usr/lib/python3.*/site-packages/orthos2") 3 | 4 | # Ansible scripts are executed remotely 5 | addFilter("orthos2.noarch: E: non-executable-script /usr/lib/orthos2/ansible/roles/add_custom_facts/files/facts.d/") 6 | # django scripts are executed via manage python script 7 | addFilter("orthos2.noarch: E: non-executable-script /usr/lib/python3.8/site-packages/orthos2/data/scripts/") 8 | 9 | # Need until we may get an official orthos2 user/group 10 | addFilter("orthos2.noarch: W: non-standard-uid *") 11 | addFilter("orthos2.noarch: W: non-standard-gid *") 12 | 13 | addFilter("orthos2.noarch: W: hidden-file-or-dir /var/lib/orthos2/.ssh") 14 | -------------------------------------------------------------------------------- /orthos2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/__init__.py -------------------------------------------------------------------------------- /orthos2/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/api/__init__.py -------------------------------------------------------------------------------- /orthos2/api/admin.py: -------------------------------------------------------------------------------- 1 | # Register your models here. 2 | -------------------------------------------------------------------------------- /orthos2/api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class APIConfig(AppConfig): 5 | name = "orthos2.api" 6 | -------------------------------------------------------------------------------- /orthos2/api/commands/__init__.py: -------------------------------------------------------------------------------- 1 | from orthos2.api.commands.add import ( 2 | AddAnnotationCommand, 3 | AddBMCCommand, 4 | AddCommand, 5 | AddMachineCommand, 6 | AddRemotePowerCommand, 7 | AddRemotePowerDeviceCommand, 8 | AddSerialConsoleCommand, 9 | AddVMCommand, 10 | ) 11 | from orthos2.api.commands.delete import ( 12 | DeleteCommand, 13 | DeleteMachineCommand, 14 | DeleteRemotePowerCommand, 15 | DeleteRemotePowerDeviceCommand, 16 | DeleteSerialConsoleCommand, 17 | ) 18 | from orthos2.api.commands.info import InfoCommand 19 | from orthos2.api.commands.power import PowerCommand 20 | from orthos2.api.commands.query import QueryCommand 21 | from orthos2.api.commands.regenerate import RegenerateCommand 22 | from orthos2.api.commands.release import ReleaseCommand 23 | from orthos2.api.commands.rescan import RescanCommand 24 | from orthos2.api.commands.reservationhistory import ReservationHistoryCommand 25 | from orthos2.api.commands.reserve import ReserveCommand 26 | from orthos2.api.commands.serverconfig import ServerConfigCommand 27 | from orthos2.api.commands.setup import SetupCommand 28 | 29 | __all__ = [ 30 | "InfoCommand", 31 | "QueryCommand", 32 | "ReserveCommand", 33 | "ReleaseCommand", 34 | "ReservationHistoryCommand", 35 | "RescanCommand", 36 | "RegenerateCommand", 37 | "ServerConfigCommand", 38 | "SetupCommand", 39 | "PowerCommand", 40 | "AddCommand", 41 | "AddVMCommand", 42 | "AddMachineCommand", 43 | "AddSerialConsoleCommand", 44 | "AddAnnotationCommand", 45 | "AddRemotePowerCommand", 46 | "DeleteCommand", 47 | "DeleteMachineCommand", 48 | "DeleteSerialConsoleCommand", 49 | "DeleteRemotePowerCommand", 50 | "DeleteRemotePowerDeviceCommand", 51 | "AddBMCCommand", 52 | "AddRemotePowerDeviceCommand", 53 | ] 54 | -------------------------------------------------------------------------------- /orthos2/api/commands/serverconfig.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, List 2 | 3 | from django.contrib.auth.models import AnonymousUser 4 | from django.http import JsonResponse 5 | from django.urls import URLPattern, re_path 6 | from rest_framework.request import Request 7 | 8 | from orthos2.api.commands.base import BaseAPIView 9 | from orthos2.api.serializers.misc import ( 10 | AuthRequiredSerializer, 11 | ErrorMessage, 12 | InfoMessage, 13 | ) 14 | from orthos2.data.models import ServerConfig 15 | 16 | 17 | class ServerConfigCommand(BaseAPIView): 18 | 19 | METHOD = "GET" 20 | URL = "/serverconfig" 21 | ARGUMENTS = ([],) 22 | 23 | HELP_SHORT = "Show server configuration." 24 | HELP = """Show server configuration (superusers only). 25 | 26 | Usage: 27 | CONFIG 28 | 29 | Example: 30 | CONFIG 31 | """ 32 | 33 | @staticmethod 34 | def get_urls() -> List[URLPattern]: 35 | return [ 36 | re_path( 37 | r"^serverconfig$", ServerConfigCommand.as_view(), name="serverconfig" 38 | ), 39 | ] 40 | 41 | def get(self, request: Request, *args: Any, **kwargs: Any) -> JsonResponse: 42 | """Show server configuration.""" 43 | if isinstance(request.user, AnonymousUser) or not request.auth: 44 | return AuthRequiredSerializer().as_json 45 | 46 | if not request.user.is_superuser: 47 | return ErrorMessage( 48 | "Only superusers are allowed to perform this action!" 49 | ).as_json 50 | 51 | config = ServerConfig.objects.all() 52 | 53 | if config.count() == 0: 54 | return InfoMessage("No configurations available.").as_json 55 | 56 | theader = [{"key": "Key"}, {"value": "Value"}] 57 | response: Dict[str, Any] = { 58 | "header": {"type": "TABLE", "theader": theader}, 59 | "data": [], 60 | } 61 | 62 | for item in config: 63 | response["data"].append({"key": item.key, "value": item.value}) 64 | 65 | return JsonResponse(response) 66 | -------------------------------------------------------------------------------- /orthos2/api/fixtures/forms/delete_remote_power_device_api_form.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "data.remotepowerdevice", 4 | "pk": null, 5 | "fields": { 6 | "username": "test", 7 | "password": "test", 8 | "fqdn": "rpower.foo.de", 9 | "mac": "AA:BB:CC:DD:EE", 10 | "url": "https://rpower.foo.de" 11 | } 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /orthos2/api/lookups.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING, Tuple 2 | 3 | from django.db.models import Lookup 4 | 5 | if TYPE_CHECKING: 6 | from django.db.backends.base.base import BaseDatabaseWrapper 7 | from django.db.models.sql.compiler import SQLCompiler 8 | 9 | 10 | class NotEqual(Lookup): 11 | lookup_name = "ne" 12 | 13 | def as_sql( # type: ignore 14 | self, compiler: "SQLCompiler", connection: "BaseDatabaseWrapper" 15 | ) -> Tuple[str, str]: 16 | lhs, lhs_params = self.process_lhs(compiler, connection) 17 | rhs, rhs_params = self.process_rhs(compiler, connection) 18 | params = lhs_params + rhs_params 19 | return "{} <> {}".format(lhs, rhs), params # type: ignore 20 | -------------------------------------------------------------------------------- /orthos2/api/serializers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/api/serializers/__init__.py -------------------------------------------------------------------------------- /orthos2/api/serializers/annotation.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from orthos2.data.models import Annotation 4 | 5 | 6 | class AnnotationSerializer(serializers.ModelSerializer[Annotation]): 7 | class Meta: # type: ignore 8 | model = Annotation 9 | exclude = ["machine"] 10 | -------------------------------------------------------------------------------- /orthos2/api/serializers/bmc.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from orthos2.data.models import BMC 4 | 5 | 6 | class BMCSerializer(serializers.ModelSerializer[BMC]): 7 | class Meta: # type: ignore 8 | model = BMC 9 | exclude = ["machine"] 10 | -------------------------------------------------------------------------------- /orthos2/api/serializers/installation.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from orthos2.data.models import Installation 4 | 5 | 6 | class InstallationSerializer(serializers.ModelSerializer[Installation]): 7 | class Meta: # type: ignore 8 | model = Installation 9 | exclude = ["machine"] 10 | -------------------------------------------------------------------------------- /orthos2/api/serializers/networkinterface.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from orthos2.data.models import NetworkInterface 4 | 5 | 6 | class NetworkInterfaceSerializer(serializers.ModelSerializer[NetworkInterface]): 7 | class Meta: # type: ignore 8 | model = NetworkInterface 9 | exclude = ["machine"] 10 | -------------------------------------------------------------------------------- /orthos2/api/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/api/tests/__init__.py -------------------------------------------------------------------------------- /orthos2/api/tests/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/api/tests/commands/__init__.py -------------------------------------------------------------------------------- /orthos2/api/tests/commands/test_info.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.urls import reverse 3 | from rest_framework import status 4 | from rest_framework.test import APITestCase 5 | 6 | 7 | class InfoTest(APITestCase): 8 | """ 9 | Test all routes that are related to the info endpoint. 10 | """ 11 | 12 | fixtures = [ 13 | "orthos2/data/fixtures/systems.json", 14 | "orthos2/data/fixtures/vendors.json", 15 | "orthos2/api/fixtures/serializers/machines.json", 16 | ] 17 | 18 | def setUp(self) -> None: 19 | self.user = User.objects.create_superuser( 20 | username="testuser", email="test@test.de", password="12345" 21 | ) 22 | self.client.force_authenticate(user=self.user) 23 | 24 | def test_info_get_infinite_reservation(self): 25 | """ 26 | Verify that retrieving a machine with an infinite reservation is possible. 27 | """ 28 | # Arrange 29 | url = reverse("api:machine") 30 | url += "?fqdn=test.testing.suse.de" 31 | self.maxDiff = None 32 | 33 | # Act 34 | response = self.client.get(url, format="json") 35 | json_response = response.json() 36 | 37 | # Assert 38 | self.assertEqual(response.status_code, status.HTTP_200_OK) 39 | self.assertTrue("data" in json_response) 40 | self.assertTrue("header" in json_response) 41 | self.assertTrue("type" in json_response["header"]) 42 | self.assertEqual(json_response["header"]["type"], "INFO") 43 | self.assertIn( 44 | json_response["data"]["reserved_until"]["value"], 45 | ("9999-12-31T22:59:59.999999+01:00", "9999-12-31T23:59:59.999999+01:00"), 46 | ) 47 | -------------------------------------------------------------------------------- /orthos2/api/tests/serializers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/api/tests/serializers/__init__.py -------------------------------------------------------------------------------- /orthos2/api/tests/serializers/test_machine.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | from orthos2.api.serializers.machine import MachineSerializer 4 | from orthos2.data.models import Machine 5 | 6 | 7 | class MachineSerializerTest(TestCase): 8 | """ 9 | Verify that machine serialization is working as expected. 10 | """ 11 | 12 | fixtures = [ 13 | "orthos2/data/fixtures/systems.json", 14 | "orthos2/data/fixtures/vendors.json", 15 | "orthos2/api/fixtures/serializers/machines.json", 16 | ] 17 | 18 | def test_machine_serialization_infinite_reservation(self): 19 | """ 20 | Verify that serializing machines with an infinite reservation is working as expected. 21 | """ 22 | # Arrange 23 | machine = Machine.objects.get(pk=1) 24 | serializer = MachineSerializer(machine) 25 | 26 | # Act 27 | result = serializer.data_info 28 | 29 | # Assert 30 | self.assertNotEqual(result, {}) 31 | -------------------------------------------------------------------------------- /orthos2/api/urls.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | from django.urls import re_path 4 | from rest_framework.authtoken import views as authtoken_views 5 | 6 | from orthos2.api import views 7 | from orthos2.api.commands import * # noqa: F403 8 | 9 | app_name = "api" 10 | urlpatterns = [ 11 | re_path(r"^$", views.root, name="root"), 12 | re_path(r"^login", authtoken_views.obtain_auth_token), 13 | ] 14 | 15 | urlpatterns += InfoCommand.get_urls() # noqa: F405 16 | urlpatterns += QueryCommand.get_urls() # noqa: F405 17 | urlpatterns += ReserveCommand.get_urls() # noqa: F405 18 | urlpatterns += ReleaseCommand.get_urls() # noqa: F405 19 | urlpatterns += ReservationHistoryCommand.get_urls() # noqa: F405 20 | urlpatterns += RescanCommand.get_urls() # noqa: F405 21 | urlpatterns += RegenerateCommand.get_urls() # noqa: F405 22 | urlpatterns += ServerConfigCommand.get_urls() # noqa: F405 23 | urlpatterns += SetupCommand.get_urls() # noqa: F405 24 | urlpatterns += PowerCommand.get_urls() # noqa: F405 25 | urlpatterns += AddCommand.get_urls() # noqa: F405 26 | urlpatterns += AddVMCommand.get_urls() # noqa: F405 27 | urlpatterns += AddMachineCommand.get_urls() # noqa: F405 28 | urlpatterns += AddSerialConsoleCommand.get_urls() # noqa: F405 29 | urlpatterns += AddAnnotationCommand.get_urls() # noqa: F405 30 | urlpatterns += AddBMCCommand.get_urls() 31 | urlpatterns += AddRemotePowerCommand.get_urls() # noqa: F405 32 | urlpatterns += AddRemotePowerDeviceCommand.get_urls() 33 | urlpatterns += DeleteCommand.get_urls() # noqa: F405 34 | urlpatterns += DeleteMachineCommand.get_urls() # noqa: F405 35 | urlpatterns += DeleteSerialConsoleCommand.get_urls() # noqa: F405 36 | urlpatterns += DeleteRemotePowerCommand.get_urls() # noqa: F405 37 | urlpatterns += DeleteRemotePowerDeviceCommand.get_urls() # noda: F405 38 | -------------------------------------------------------------------------------- /orthos2/api/views.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.http import HttpRequest, JsonResponse 3 | from django.urls import reverse 4 | from rest_framework.decorators import api_view 5 | 6 | from orthos2.api.serializers.misc import RootSerializer 7 | from orthos2.data.models import ServerConfig 8 | 9 | 10 | @api_view(["GET"]) 11 | def root(request: HttpRequest) -> JsonResponse: 12 | """API root.""" 13 | import orthos2.api.commands as commands 14 | 15 | data = { 16 | "version": settings.VERSION, 17 | "contact": settings.CONTACT, 18 | "user": request.user.username, 19 | "api": request.build_absolute_uri(reverse("api:root")), 20 | "web": request.build_absolute_uri(reverse("frontend:root")), 21 | "message": ServerConfig.objects.by_key( 22 | "orthos.api.welcomemessage", "Come in, reserve and play..." 23 | ), 24 | "commands": { 25 | "info": commands.InfoCommand.description(), 26 | "query": commands.QueryCommand.description(), 27 | "reserve": commands.ReserveCommand.description(), 28 | "release": commands.ReleaseCommand.description(), 29 | "reservationhistory": commands.ReservationHistoryCommand.description(), 30 | "rescan": commands.RescanCommand.description(), 31 | "regenerate": commands.RegenerateCommand.description(), 32 | "serverconfig": commands.ServerConfigCommand.description(), 33 | "setup": commands.SetupCommand.description(), 34 | "power": commands.PowerCommand.description(), 35 | "add": commands.AddCommand.description(), 36 | "delete": commands.DeleteCommand.description(), 37 | }, 38 | } 39 | root = RootSerializer(data) 40 | 41 | return root.as_json 42 | -------------------------------------------------------------------------------- /orthos2/data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/data/__init__.py -------------------------------------------------------------------------------- /orthos2/data/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DataConfig(AppConfig): 5 | name = "orthos2.data" 6 | -------------------------------------------------------------------------------- /orthos2/data/exceptions.py: -------------------------------------------------------------------------------- 1 | class HostnameNotDnsResolvable(Exception): 2 | pass 3 | 4 | 5 | class ReserveException(Exception): 6 | pass 7 | 8 | 9 | class ReleaseException(Exception): 10 | pass 11 | -------------------------------------------------------------------------------- /orthos2/data/fixtures/domains.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "data.domain", 4 | "pk": null, 5 | "fields": { 6 | "name": "example.our-org.tld", 7 | "created": "2016-01-01T10:00:00+00:00", 8 | "updated": "2016-01-01T10:00:00+00:00" 9 | } 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /orthos2/data/fixtures/platforms.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "data.platform", 4 | "pk": null, 5 | "fields": { 6 | "name": "Other", 7 | "vendor": 1 8 | } 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /orthos2/data/fixtures/systems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "data.system", 4 | "pk": 1, 5 | "fields": { 6 | "name": "BareMetal", 7 | "administrative": false, 8 | "virtual": false, 9 | "allowHypervisor": true, 10 | "created": "2016-01-01T10:00:00+00:00" 11 | } 12 | }, 13 | { 14 | "model": "data.system", 15 | "pk": 2, 16 | "fields": { 17 | "name": "VM KVM", 18 | "virtual": true, 19 | "administrative": false, 20 | "created": "2016-01-01T10:00:00+00:00" 21 | } 22 | }, 23 | { 24 | "model": "data.system", 25 | "pk": 3, 26 | "fields": { 27 | "name": "VM PowerKVM", 28 | "virtual": true, 29 | "administrative": false, 30 | "created": "2016-01-01T10:00:00+00:00" 31 | } 32 | }, 33 | { 34 | "model": "data.system", 35 | "pk": 4, 36 | "fields": { 37 | "name": "VM XEN", 38 | "virtual": true, 39 | "administrative": false, 40 | "created": "2016-01-01T10:00:00+00:00" 41 | } 42 | }, 43 | { 44 | "model": "data.system", 45 | "pk": 5, 46 | "fields": { 47 | "name": "VM zVM", 48 | "virtual": true, 49 | "administrative": false, 50 | "created": "2016-01-01T10:00:00+00:00" 51 | } 52 | }, 53 | { 54 | "model": "data.system", 55 | "pk": 6, 56 | "fields": { 57 | "name": "VM zKVM", 58 | "virtual": true, 59 | "administrative": false, 60 | "created": "2016-01-01T10:00:00+00:00" 61 | } 62 | }, 63 | { 64 | "model": "data.system", 65 | "pk": 7, 66 | "fields": { 67 | "name": "LPAR PowerPC", 68 | "virtual": true, 69 | "administrative": false, 70 | "created": "2016-01-01T10:00:00+00:00" 71 | } 72 | }, 73 | { 74 | "model": "data.system", 75 | "pk": 8, 76 | "fields": { 77 | "name": "LPAR zSeries", 78 | "virtual": true, 79 | "administrative": false, 80 | "created": "2016-01-01T10:00:00+00:00" 81 | } 82 | } 83 | ] 84 | -------------------------------------------------------------------------------- /orthos2/data/fixtures/tests/test_serverconfig_domainending.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "data.serverconfig", 4 | "pk": null, 5 | "fields": { 6 | "key": "domain.validendings", 7 | "value": "example.de, example.com, foo.de" 8 | } 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /orthos2/data/fixtures/vendors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "data.vendor", 4 | "pk": null, 5 | "fields": { 6 | "name": "Other" 7 | } 8 | } 9 | ] 10 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0003_auto_20210406_1905.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-04-06 17:05 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0002_auto_20210311_1216"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name="remotepower", 15 | name="kind", 16 | ), 17 | migrations.AddField( 18 | model_name="bmc", 19 | name="fence_name", 20 | field=models.CharField( 21 | choices=[("ipmilanplus", "ipmilanplus")], 22 | default="ipmilanplus", 23 | max_length=255, 24 | verbose_name="Fence agent", 25 | ), 26 | preserve_default=False, 27 | ), 28 | migrations.AddField( 29 | model_name="remotepower", 30 | name="fence_name", 31 | field=models.CharField( 32 | choices=[("virsh", "virsh")], 33 | default="virsh", 34 | max_length=255, 35 | verbose_name="Fence Agent", 36 | ), 37 | preserve_default=False, 38 | ), 39 | migrations.AddField( 40 | model_name="remotepowerdevice", 41 | name="fence_name", 42 | field=models.CharField( 43 | choices=[("raritan", "raritan")], 44 | default="raritan", 45 | max_length=255, 46 | verbose_name="Fence Agent", 47 | ), 48 | preserve_default=False, 49 | ), 50 | migrations.AlterField( 51 | model_name="machine", 52 | name="use_bmc", 53 | field=models.BooleanField(default=True, verbose_name="Use BMC"), 54 | ), 55 | migrations.DeleteModel( 56 | name="RemotePowerType", 57 | ), 58 | ] 59 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0004_auto_20210505_1650.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-05-05 14:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0003_auto_20210406_1905"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="remotepower", 15 | name="options", 16 | field=models.CharField( 17 | blank=True, 18 | default="", 19 | help_text='Additional command line options to be passed to the fence agent.\n E. g. "--management= for lpar', 20 | max_length=1024, 21 | ), 22 | ), 23 | migrations.AlterField( 24 | model_name="bmc", 25 | name="password", 26 | field=models.CharField(blank=True, default="", max_length=256), 27 | preserve_default=False, 28 | ), 29 | migrations.AlterField( 30 | model_name="bmc", 31 | name="username", 32 | field=models.CharField(blank=True, default="", max_length=256), 33 | preserve_default=False, 34 | ), 35 | migrations.AlterField( 36 | model_name="remotepower", 37 | name="fence_name", 38 | field=models.CharField( 39 | choices=[ 40 | ("virsh", "virsh"), 41 | ("lpar", "lpar"), 42 | ("ibmz", "ibmz"), 43 | ("redfish", "redfish"), 44 | ], 45 | max_length=255, 46 | verbose_name="Fence Agent", 47 | ), 48 | ), 49 | migrations.AlterField( 50 | model_name="remotepowerdevice", 51 | name="fence_name", 52 | field=models.CharField( 53 | choices=[("raritan", "raritan"), ("apc", "apc")], 54 | max_length=255, 55 | verbose_name="Fence Agent", 56 | ), 57 | ), 58 | ] 59 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0005_auto_20210510_1913.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-05-10 17:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0004_auto_20210505_1650"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name="remotepower", 15 | name="hypervisor", 16 | ), 17 | migrations.RemoveField( 18 | model_name="remotepower", 19 | name="management_bmc", 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0006_auto_20210511_0917.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-05-11 07:17 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0005_auto_20210510_1913"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="system", 15 | name="allowBMC", 16 | field=models.BooleanField( 17 | default=False, 18 | help_text="Can a network interface be assigned to such a system serving as BMC?", 19 | ), 20 | ), 21 | migrations.AddField( 22 | model_name="system", 23 | name="allowHypervisor", 24 | field=models.BooleanField( 25 | default=False, help_text="Can such systems host virtual machines?" 26 | ), 27 | ), 28 | migrations.AlterField( 29 | model_name="system", 30 | name="administrative", 31 | field=models.BooleanField( 32 | default=False, 33 | help_text="Are these machines administrative systems (cannot be installed or reserved)?", 34 | ), 35 | ), 36 | migrations.AlterField( 37 | model_name="system", 38 | name="name", 39 | field=models.CharField( 40 | help_text="What kind of system are these machines?", 41 | max_length=200, 42 | unique=True, 43 | ), 44 | ), 45 | migrations.AlterField( 46 | model_name="system", 47 | name="virtual", 48 | field=models.BooleanField( 49 | default=False, 50 | help_text="Are these machines virtual systems (can have a hypervisor)?", 51 | ), 52 | ), 53 | ] 54 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0008_auto_20210522_1102.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-05-22 09:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0007_auto_20210511_1910"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="bmc", 15 | name="fence_name", 16 | field=models.CharField( 17 | choices=[("redfish", "redfish"), ("ipmilanplus", "ipmilanplus")], 18 | max_length=255, 19 | verbose_name="Fence agent", 20 | ), 21 | ), 22 | migrations.AlterField( 23 | model_name="remotepower", 24 | name="fence_name", 25 | field=models.CharField( 26 | choices=[ 27 | ("virsh", "virsh"), 28 | ("pvm", "pvm"), 29 | ("lpar", "lpar"), 30 | ("ibmz", "ibmz"), 31 | ], 32 | max_length=255, 33 | verbose_name="Fence Agent", 34 | ), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0009_remove_domain_setup_architectures.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-05-22 09:20 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0008_auto_20210522_1102"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name="domain", 15 | name="setup_architectures", 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0010_auto_20210522_1123.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-05-22 09:23 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("data", "0009_remove_domain_setup_architectures"), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name="DomainAdmin", 16 | fields=[ 17 | ( 18 | "id", 19 | models.AutoField( 20 | auto_created=True, 21 | primary_key=True, 22 | serialize=False, 23 | verbose_name="ID", 24 | ), 25 | ), 26 | ("contact_email", models.EmailField(blank=True, max_length=254)), 27 | ( 28 | "arch", 29 | models.ForeignKey( 30 | on_delete=django.db.models.deletion.CASCADE, 31 | to="data.architecture", 32 | ), 33 | ), 34 | ( 35 | "domain", 36 | models.ForeignKey( 37 | on_delete=django.db.models.deletion.CASCADE, to="data.domain" 38 | ), 39 | ), 40 | ], 41 | ), 42 | migrations.AddField( 43 | model_name="domain", 44 | name="supported_architectures", 45 | field=models.ManyToManyField( 46 | blank=True, 47 | related_name="supported_domains", 48 | through="data.DomainAdmin", 49 | to="data.Architecture", 50 | verbose_name="Supported architectures", 51 | ), 52 | ), 53 | ] 54 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0011_auto_20210522_1537.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-05-22 13:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0010_auto_20210522_1123"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="domainadmin", 15 | name="contact_email", 16 | field=models.EmailField(max_length=254), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0013_auto_20210610_0804.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-06-10 06:04 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("data", "0012_auto_20210531_1630"), 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name="serialconsole", 16 | name="cscreen_server", 17 | ), 18 | migrations.AddField( 19 | model_name="domain", 20 | name="cscreen_server", 21 | field=models.ForeignKey( 22 | blank=True, 23 | limit_choices_to={"administrative": True}, 24 | null=True, 25 | on_delete=django.db.models.deletion.SET_NULL, 26 | related_name="+", 27 | to="data.machine", 28 | verbose_name="CScreen server", 29 | ), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0015_auto_20210615_1500.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-06-15 13:00 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0014_auto_20210610_1246"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="machine", 15 | name="bios_date", 16 | field=models.DateField( 17 | default="1990-10-03", 18 | editable=False, 19 | help_text="The firmware BIOS is from ... (on x86 as retrieved from dmidecode -s bios-version", 20 | ), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0016_auto_20210616_1122.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-06-16 09:22 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0015_auto_20210615_1500"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="machine", 15 | name="bios_date", 16 | field=models.DateField( 17 | blank=True, 18 | default=None, 19 | editable=False, 20 | help_text="The firmware BIOS is from ... (on x86 as retrieved from dmidecode -s bios-version", 21 | null=True, 22 | ), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0017_auto_20210622_1848.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-06-22 16:48 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0016_auto_20210616_1122"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="machine", 15 | name="unknown_mac", 16 | field=models.BooleanField( 17 | default=False, 18 | help_text="Use this to create a BMC before the mac address of the machine is known", 19 | verbose_name="MAC unknwon", 20 | ), 21 | ), 22 | migrations.AlterField( 23 | model_name="machine", 24 | name="vm_max", 25 | field=models.IntegerField( 26 | default=6, 27 | help_text="Maximum amount of virtual hosts allowed to be spawned on this virtual server (ToDo: don't use yet)", 28 | verbose_name="Max. VMs", 29 | ), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0019_auto_20210624_1910.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-06-24 17:10 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0017_auto_20210622_1848"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="remotepowerdevice", 15 | name="fqdn", 16 | field=models.CharField(max_length=256, unique=True), 17 | ), 18 | migrations.AlterField( 19 | model_name="remotepowerdevice", 20 | name="mac", 21 | field=models.CharField(max_length=17, unique=True), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0020_auto_20210625_1935.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-06-25 17:35 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("data", "0019_auto_20210624_1910"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="remotepower", 16 | name="machine", 17 | field=models.OneToOneField( 18 | on_delete=django.db.models.deletion.CASCADE, to="data.machine" 19 | ), 20 | ), 21 | migrations.AddField( 22 | model_name="remotepower", 23 | name="id", 24 | field=models.AutoField( 25 | auto_created=True, primary_key=True, serialize=False, verbose_name="ID" 26 | ), 27 | preserve_default=False, 28 | ), 29 | migrations.AlterField( 30 | model_name="remotepowerdevice", 31 | name="fence_name", 32 | field=models.CharField( 33 | choices=[("raritan", "raritan"), ("cdu", "cdu"), ("apc", "apc")], 34 | max_length=255, 35 | verbose_name="Fence Agent", 36 | ), 37 | ), 38 | ] 39 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0021_auto_20210628_1207.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-06-28 10:07 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("data", "0020_auto_20210625_1935"), 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name="remotepower", 16 | name="id", 17 | ), 18 | migrations.AlterField( 19 | model_name="remotepower", 20 | name="machine", 21 | field=models.OneToOneField( 22 | on_delete=django.db.models.deletion.CASCADE, 23 | primary_key=True, 24 | serialize=False, 25 | to="data.machine", 26 | ), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0022_auto_20210628_1713.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-06-28 15:13 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("data", "0021_auto_20210628_1207"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="machine", 16 | name="hypervisor", 17 | field=models.ForeignKey( 18 | blank=True, 19 | help_text="The physical host this virtual machine is running on", 20 | null=True, 21 | on_delete=django.db.models.deletion.SET_NULL, 22 | related_name="hypervising", 23 | to="data.machine", 24 | ), 25 | ), 26 | migrations.AlterField( 27 | model_name="serialconsole", 28 | name="kernel_device", 29 | field=models.CharField( 30 | default="ttyS", 31 | help_text="The kernel device string as passed via kernel command line, e.g. ttyS, ttyAMA, ttyUSB,...", 32 | max_length=255, 33 | verbose_name="Kernel Device", 34 | ), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0024_auto_20210713_0538.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-07-13 03:38 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0023_auto_20210713_0532"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name="platform", 15 | name="created", 16 | ), 17 | migrations.RemoveField( 18 | model_name="platform", 19 | name="updated", 20 | ), 21 | migrations.RemoveField( 22 | model_name="vendor", 23 | name="created", 24 | ), 25 | migrations.RemoveField( 26 | model_name="vendor", 27 | name="updated", 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0025_auto_20210713_0755.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-07-13 05:55 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0024_auto_20210713_0538"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name="domain", 15 | name="created", 16 | ), 17 | migrations.RemoveField( 18 | model_name="domain", 19 | name="updated", 20 | ), 21 | migrations.RemoveField( 22 | model_name="enclosure", 23 | name="created", 24 | ), 25 | migrations.RemoveField( 26 | model_name="enclosure", 27 | name="updated", 28 | ), 29 | migrations.RemoveField( 30 | model_name="machinegroup", 31 | name="created", 32 | ), 33 | migrations.RemoveField( 34 | model_name="machinegroup", 35 | name="updated", 36 | ), 37 | migrations.RemoveField( 38 | model_name="remotepower", 39 | name="created", 40 | ), 41 | migrations.RemoveField( 42 | model_name="remotepower", 43 | name="updated", 44 | ), 45 | migrations.RemoveField( 46 | model_name="serialconsole", 47 | name="created", 48 | ), 49 | migrations.RemoveField( 50 | model_name="serialconsole", 51 | name="updated", 52 | ), 53 | migrations.RemoveField( 54 | model_name="serialconsoletype", 55 | name="created", 56 | ), 57 | migrations.RemoveField( 58 | model_name="serialconsoletype", 59 | name="updated", 60 | ), 61 | ] 62 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0026_auto_20210713_1242.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-07-13 10:42 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0025_auto_20210713_0755"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | model_name="machine", 15 | old_name="virtualization_api", 16 | new_name="virt_api_int", 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0027_auto_20210916_1201.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-09-16 10:01 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0026_auto_20210713_1242"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="serialconsole", 15 | name="kernel_device", 16 | field=models.CharField( 17 | choices=[(0, "ttyS"), (1, "ttyUSB"), (2, "ttyAMA"), (3, "tty")], 18 | default="ttyS", 19 | help_text="The kernel device string as passed via kernel command line, e.g. ttyS, ttyAMA, ttyUSB,...", 20 | max_length=64, 21 | verbose_name="Kernel Device", 22 | ), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0028_auto_20210916_1216.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-09-16 10:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0027_auto_20210916_1201"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="serialconsole", 15 | name="kernel_device", 16 | field=models.CharField( 17 | choices=[ 18 | (0, "None"), 19 | (1, "ttyS"), 20 | (2, "tty"), 21 | (3, "ttyUSB"), 22 | (4, "ttyAMA"), 23 | ], 24 | default=1, 25 | help_text="The kernel device string as passed via kernel command line, e.g. ttyS, ttyAMA, ttyUSB,...", 26 | max_length=64, 27 | verbose_name="Kernel Device", 28 | ), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0029_auto_20210916_1225.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-09-16 10:25 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0028_auto_20210916_1216"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="serialconsole", 15 | name="kernel_device", 16 | field=models.CharField( 17 | choices=[(0, "ttyS"), (1, "ttyUSB"), (2, "ttyAMA"), (3, "tty")], 18 | default="ttyS", 19 | help_text="The kernel device string as passed via kernel command line, e.g. ttyS, ttyAMA, ttyUSB,...", 20 | max_length=64, 21 | verbose_name="Kernel Device", 22 | ), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0030_auto_20210916_1228.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-09-16 10:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0029_auto_20210916_1225"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="serialconsole", 15 | name="kernel_device", 16 | field=models.CharField( 17 | choices=[ 18 | ("ttyS", "ttyS"), 19 | ("ttyUSB", "ttyUSB"), 20 | ("ttyAMA", "ttyAMA"), 21 | ("tty", "tty"), 22 | ("None", "None"), 23 | ], 24 | default="ttyS", 25 | help_text="The kernel device string as passed via kernel command line, e.g. ttyS, ttyAMA, ttyUSB,...", 26 | max_length=64, 27 | verbose_name="Kernel Device", 28 | ), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0031_auto_20211006_1005.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-10-06 08:05 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0030_auto_20210916_1228"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="serialconsole", 15 | name="kernel_device", 16 | field=models.CharField( 17 | choices=[ 18 | ("ttyS", "ttyS"), 19 | ("ttyUSB", "ttyUSB"), 20 | ("ttyAMA", "ttyAMA"), 21 | ("tty", "tty"), 22 | ("None", "None"), 23 | ], 24 | default="ttyS", 25 | help_text='The kernel device string as passed via kernel command line, e.g. ttyS, ttyAMA, ttyUSB,... "None" will remove console= kernel paramter', 26 | max_length=64, 27 | verbose_name="Kernel Device", 28 | ), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0032_alter_domain_cobbler_server.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.8 on 2021-10-18 14:08 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0031_auto_20211006_1005"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="domain", 15 | name="cobbler_server", 16 | field=models.ManyToManyField( # type: ignore 17 | blank=True, 18 | limit_choices_to={"administrative": True}, 19 | null=True, 20 | related_name="cobbler_server_for", 21 | to="data.Machine", 22 | verbose_name="Cobbler server", 23 | ), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0033_alter_domain_cobbler_server.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.8 on 2021-10-18 14:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0032_alter_domain_cobbler_server"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="domain", 15 | name="cobbler_server", 16 | field=models.ManyToManyField( 17 | blank=True, 18 | limit_choices_to={"administrative": True}, 19 | related_name="cobbler_server_for", 20 | to="data.Machine", 21 | verbose_name="Cobbler server", 22 | ), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0034_alter_domain_cobbler_server.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.8 on 2021-10-18 14:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0033_alter_domain_cobbler_server"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="domain", 15 | name="cobbler_server", 16 | field=models.ManyToManyField( # type: ignore 17 | blank=True, 18 | limit_choices_to={"administrative": True}, 19 | null=True, 20 | related_name="cobbler_server_for", 21 | to="data.Machine", 22 | verbose_name="Cobbler server", 23 | ), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0036_alter_serialconsole_kernel_device.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.8 on 2021-11-04 21:14 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0035_auto_20211103_1405"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name="serialconsole", 15 | name="kernel_device", 16 | field=models.CharField( 17 | choices=[ 18 | ("ttyS", "ttyS"), 19 | ("ttyUSB", "ttyUSB"), 20 | ("ttyAMA", "ttyAMA"), 21 | ("hvc", "hvc"), 22 | ("None", "None"), 23 | ], 24 | default="ttyS", 25 | help_text='The kernel device string as passed via kernel command line, e.g. ttyS, ttyAMA, ttyUSB,... "None" will remove console= kernel paramter', 26 | max_length=64, 27 | verbose_name="Kernel Device", 28 | ), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0037_remotepowerdevice_url.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.8 on 2022-02-17 11:45 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0036_alter_serialconsole_kernel_device"), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="remotepowerdevice", 15 | name="url", 16 | field=models.URLField( 17 | blank=True, 18 | help_text="URL of the Webinterface to configure this Power Device\nPower devices should be in a separate management network only reachable via the cobbler server\nIn this case the Webinterface might be port forwarded, see example below\nSee documentation about more Details.http://cobbler.arch.suse.de:10018", 19 | ), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0039_auto_20220221_1649.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.8 on 2022-02-21 15:49 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("data", "0038_auto_20220221_1626"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name="machine", 15 | name="installed_distro", 16 | ), 17 | migrations.AlterField( 18 | model_name="machine", 19 | name="active", 20 | field=models.BooleanField( 21 | default=True, 22 | help_text="Machine vanishes from most lists. This is intendend as kind of maintenance/repair state", 23 | ), 24 | ), 25 | migrations.AlterField( 26 | model_name="machine", 27 | name="autoreinstall", 28 | field=models.BooleanField( 29 | default=True, 30 | help_text="Shall this machine be automatically re-installed when its reservation ends?
The last installation that has been triggered will be used for auto re-installation.", 31 | verbose_name="Auto re-install machine", 32 | ), 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0042_alter_machine_last_check.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.20 on 2025-04-03 13:59 2 | 3 | import datetime 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ("data", "0041_domain_cobbler_server_password_and_more"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name="machine", 17 | name="last_check", 18 | field=models.DateTimeField( 19 | default=datetime.datetime( 20 | 2016, 1, 1, 10, 0, tzinfo=datetime.timezone.utc 21 | ), 22 | editable=False, 23 | verbose_name="Checked at", 24 | ), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /orthos2/data/migrations/0045_alter_domain_cscreen_server.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.20 on 2025-05-15 06:54 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("data", "0044_initial_required_data"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="domain", 16 | name="cscreen_server", 17 | field=models.ForeignKey( 18 | blank=True, 19 | limit_choices_to={"administrative": True}, 20 | null=True, 21 | on_delete=django.db.models.deletion.SET_NULL, 22 | related_name="cscreen_server_for", 23 | to="data.machine", 24 | verbose_name="CScreen server", 25 | ), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /orthos2/data/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/data/migrations/__init__.py -------------------------------------------------------------------------------- /orthos2/data/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .annotation import Annotation 2 | from .architecture import * # noqa: F403 3 | from .bmc import * # noqa: F403 4 | from .component import Component 5 | from .components.pci import PCIDevice 6 | from .domain import * # noqa: F403 7 | from .enclosure import * # noqa: F403 8 | from .installation import * # noqa: F403 9 | from .machine import * # noqa: F403 10 | from .machinegroup import MachineGroup, MachineGroupMembership 11 | from .networkinterface import * # noqa: F403 12 | from .platform import * # noqa: F403 13 | from .remotepower import * # noqa: F403 14 | from .remotepowerdevice import * # noqa: F403 15 | from .reservationhistory import * # noqa: F403 16 | from .serialconsole import * # noqa: F403 17 | from .serverconfig import * # noqa: F403 18 | from .system import * # noqa: F403 19 | from .vendor import * # noqa: F403 20 | from .virtualizationapi import * # noqa: F403 21 | -------------------------------------------------------------------------------- /orthos2/data/models/annotation.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from typing import TYPE_CHECKING, Tuple 3 | 4 | from django.contrib.auth.models import User 5 | from django.db import models 6 | 7 | if TYPE_CHECKING: 8 | from orthos2.data.models.machine import Machine 9 | 10 | 11 | class Annotation(models.Model): 12 | class Meta: 13 | ordering = ["-created"] 14 | 15 | machine = models.ForeignKey["Machine"]( # type: ignore 16 | "data.Machine", 17 | related_name="annotations", 18 | editable=False, 19 | blank=False, 20 | on_delete=models.CASCADE, 21 | ) 22 | 23 | text = models.CharField(max_length=1024, blank=False) 24 | 25 | reporter = models.ForeignKey( 26 | User, on_delete=models.SET_NULL, null=True, blank=False, editable=False 27 | ) 28 | 29 | created = models.DateTimeField("Created at", auto_now_add=True) 30 | 31 | def natural_key(self) -> Tuple[str, datetime.date]: 32 | return self.machine.fqdn, self.created # type: ignore 33 | 34 | def __str__(self) -> str: 35 | return self.machine.fqdn # type: ignore 36 | -------------------------------------------------------------------------------- /orthos2/data/models/bmc.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | 3 | from django.db import models 4 | 5 | from orthos2.data.validators import validate_mac_address 6 | from orthos2.utils.remotepowertype import get_remote_power_type_choices 7 | 8 | if TYPE_CHECKING: 9 | from orthos2.data.models.machine import Machine 10 | 11 | 12 | class BMC(models.Model): 13 | username = models.CharField(max_length=256, blank=True) 14 | password = models.CharField(max_length=256, blank=True) 15 | fqdn = models.CharField(max_length=256, unique=True) 16 | mac = models.CharField( 17 | max_length=17, unique=True, validators=[validate_mac_address] 18 | ) 19 | machine = models.OneToOneField["Machine"]("data.Machine", on_delete=models.CASCADE) # type: ignore 20 | 21 | remotepower_type_choices = get_remote_power_type_choices("bmc") 22 | fence_name = models.CharField( 23 | choices=remotepower_type_choices, max_length=255, verbose_name="Fence agent" 24 | ) 25 | 26 | ip_address_v4 = models.GenericIPAddressField( 27 | protocol="IPv4", 28 | blank=True, 29 | unique=True, 30 | null=True, 31 | verbose_name="IPv4 address", 32 | help_text="IPv4 address", 33 | ) 34 | 35 | ip_address_v6 = models.GenericIPAddressField( 36 | protocol="IPv6", 37 | blank=True, 38 | unique=True, 39 | null=True, 40 | verbose_name="IPv6 address", 41 | help_text="IPv6 address", 42 | ) 43 | 44 | def natural_key(self) -> str: 45 | return self.fqdn 46 | 47 | def __str__(self) -> str: 48 | return self.fqdn 49 | -------------------------------------------------------------------------------- /orthos2/data/models/component.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import TYPE_CHECKING 3 | 4 | from django.db import models 5 | 6 | if TYPE_CHECKING: 7 | from orthos2.data.models.machine import Machine 8 | 9 | logger = logging.getLogger("models") 10 | 11 | 12 | class Component(models.Model): 13 | class Meta: 14 | abstract = True 15 | 16 | machine = models.ForeignKey["Machine"]( # type: ignore 17 | "data.Machine", on_delete=models.CASCADE, null=False 18 | ) 19 | -------------------------------------------------------------------------------- /orthos2/data/models/components/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/data/models/components/__init__.py -------------------------------------------------------------------------------- /orthos2/data/models/installation.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from .machine import Machine 4 | 5 | 6 | class Installation(models.Model): 7 | machine = models.ForeignKey( 8 | Machine, related_name="installations", editable=False, on_delete=models.CASCADE 9 | ) 10 | 11 | active = models.BooleanField(blank=False, default=True) 12 | 13 | architecture = models.CharField(max_length=200, blank=True) 14 | 15 | distribution = models.CharField(max_length=200, blank=True) 16 | 17 | kernelversion = models.CharField("Kernel version", max_length=100, blank=True) 18 | 19 | partition = models.CharField(max_length=100, blank=True) 20 | 21 | def __str__(self) -> str: 22 | if self.active: 23 | return "{} ({})".format(self.distribution, "active") 24 | return self.distribution 25 | -------------------------------------------------------------------------------- /orthos2/data/models/platform.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING, Tuple 2 | 3 | from django.contrib import admin 4 | from django.db import models 5 | 6 | from .vendor import Vendor 7 | 8 | if TYPE_CHECKING: 9 | from orthos2.data.models.enclosure import Enclosure 10 | 11 | 12 | class Platform(models.Model): 13 | class Meta: 14 | ordering = ["vendor", "name"] 15 | 16 | id: int 17 | 18 | name = models.CharField(max_length=200, blank=False) 19 | 20 | vendor = models.ForeignKey( 21 | Vendor, blank=False, null=False, on_delete=models.CASCADE 22 | ) 23 | 24 | is_cartridge = models.BooleanField("Cartridge/Blade", default=False) 25 | 26 | description = models.CharField(max_length=512, blank=True) 27 | 28 | enclosure_set: models.Manager["Enclosure"] 29 | 30 | def natural_key(self) -> Tuple[str]: 31 | return (self.name,) 32 | 33 | natural_key.dependencies = ["data.vendor"] # type: ignore 34 | 35 | def __str__(self) -> str: 36 | return self.name 37 | 38 | @admin.display(description="Vendor") 39 | def get_vendor(self) -> str: 40 | return self.vendor.name 41 | 42 | @admin.display(description="Enclosures") 43 | def get_enclosure_count(self) -> int: 44 | return self.enclosure_set.count() 45 | -------------------------------------------------------------------------------- /orthos2/data/models/remotepowertype.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Any, Dict 3 | 4 | 5 | class RemotePowerType: 6 | def __init__(self, options: Dict[str, Any]) -> None: 7 | self.fence = options.get("fence") 8 | logging.debug("Initialiced RemotePowerType for %s", self.fence) 9 | self.device = options.get("device") 10 | self.username = options.get("username") 11 | if "password" in options: 12 | self.password = options["password"] 13 | self.use_password = True 14 | else: 15 | self.use_password = False 16 | if "identity_file" in options: 17 | self.identity_file = options["identity_file"] 18 | self.use_identity_file = True 19 | else: 20 | self.use_identity_file = False 21 | self.use_port = options.get("port", False) 22 | logging.info("Initialiced RemotePowerType for %s", self.fence) 23 | self.use_hostname_as_port = options.get("use_hostname_as_port", False) 24 | self.options = options.get("options", None) 25 | -------------------------------------------------------------------------------- /orthos2/data/models/reservationhistory.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from .machine import Machine 4 | 5 | 6 | class ReservationHistory(models.Model): 7 | class Meta: 8 | ordering = ["-created"] 9 | 10 | machine = models.ForeignKey(Machine, editable=False, on_delete=models.CASCADE) 11 | 12 | reserved_by = models.CharField( 13 | max_length=200, 14 | blank=False, 15 | null=False, 16 | ) 17 | 18 | reserved_at = models.DateTimeField(blank=False, null=False) 19 | 20 | reserved_until = models.DateTimeField(blank=False, null=False) 21 | 22 | reserved_reason = models.CharField( 23 | "Reservation reason", max_length=512, blank=False, null=False 24 | ) 25 | 26 | updated = models.DateTimeField("Updated at", auto_now=True) 27 | 28 | created = models.DateTimeField("Created at", auto_now_add=True) 29 | 30 | def __str__(self) -> str: 31 | return "{} ({})".format(self.reserved_by, self.machine.fqdn) 32 | -------------------------------------------------------------------------------- /orthos2/data/models/serialconsoletype.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | from django.db import models 4 | 5 | 6 | class SerialConsoleType(models.Model): 7 | class Meta: 8 | verbose_name = "Serial Console Type" 9 | 10 | class Type: 11 | @classmethod 12 | def to_str(cls, index: int) -> str: 13 | """Return type as string (serial console type name) by index.""" 14 | for type_tuple in SerialConsoleType.objects.all().values_list("id", "name"): 15 | if int(index) == type_tuple[0]: 16 | return type_tuple[1] 17 | raise Exception( 18 | "Serial console type with ID '{}' doesn't exist!".format(index) 19 | ) 20 | 21 | @classmethod 22 | def to_int(cls, name: str) -> int: 23 | """Return type as integer if name matches.""" 24 | for type_tuple in SerialConsoleType.objects.all().values_list("id", "name"): 25 | if name.lower() == type_tuple[1].lower(): 26 | return type_tuple[0] 27 | raise Exception("Serial console type '{}' not found!".format(name)) 28 | 29 | name = models.CharField(max_length=100, null=False, blank=False, unique=True) 30 | 31 | command = models.CharField(max_length=512, null=True, blank=True) 32 | 33 | comment = models.CharField(max_length=200, null=True, blank=True) 34 | 35 | def natural_key(self) -> Tuple[str]: 36 | return (self.name,) 37 | 38 | def __str__(self) -> str: 39 | return self.name 40 | -------------------------------------------------------------------------------- /orthos2/data/models/system.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Tuple 2 | 3 | from django.db import models 4 | 5 | 6 | class System(models.Model): 7 | class Manager(models.Manager["System"]): 8 | def get_by_natural_key(self, name: str) -> Optional["System"]: 9 | return self.get(name=name) 10 | 11 | help_text = "Describes the system type of a machine" 12 | 13 | name = models.CharField( 14 | max_length=200, 15 | blank=False, 16 | unique=True, 17 | help_text="What kind of system are these machines?", 18 | ) 19 | 20 | virtual = models.BooleanField( 21 | default=False, 22 | null=False, 23 | blank=False, 24 | help_text="Are these machines virtual systems (can have a hypervisor)?", 25 | ) 26 | 27 | allowBMC = models.BooleanField( 28 | default=False, 29 | null=False, 30 | blank=False, 31 | help_text="Can a network interface be assigned to such a system serving as BMC?", 32 | ) 33 | 34 | allowHypervisor = models.BooleanField( 35 | default=False, 36 | null=False, 37 | blank=False, 38 | help_text="Can such systems host virtual machines?", 39 | ) 40 | 41 | administrative = models.BooleanField( 42 | default=False, 43 | null=False, 44 | blank=False, 45 | help_text="Are these machines administrative systems (cannot be installed or reserved)?", 46 | ) 47 | 48 | created = models.DateTimeField("created at", auto_now=True) 49 | 50 | objects = Manager() 51 | 52 | def natural_key(self) -> Tuple[str]: 53 | return (self.name,) 54 | 55 | def __str__(self) -> str: 56 | return self.name 57 | -------------------------------------------------------------------------------- /orthos2/data/models/vendor.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Tuple 2 | 3 | from django.db import models 4 | 5 | 6 | class Vendor(models.Model): 7 | class Manager(models.Manager["Vendor"]): 8 | def get_by_natural_key(self, name: str) -> Optional["Vendor"]: 9 | return self.get(name=name) 10 | 11 | class Meta: 12 | ordering = ["name"] 13 | 14 | name = models.CharField(max_length=100, blank=False, unique=True) 15 | 16 | objects = Manager() 17 | 18 | def natural_key(self) -> Tuple[str]: 19 | return (self.name,) 20 | 21 | def __str__(self) -> str: 22 | return self.name 23 | -------------------------------------------------------------------------------- /orthos2/data/scripts/README: -------------------------------------------------------------------------------- 1 | manage runscript show_ansible_info --script-args -------------------------------------------------------------------------------- /orthos2/data/scripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/data/scripts/__init__.py -------------------------------------------------------------------------------- /orthos2/data/scripts/show_ansible_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from typing import Any 3 | 4 | from orthos2.taskmanager.tasks.ansible import Ansible 5 | 6 | 7 | def run(*args: Any) -> None: 8 | 9 | if not args: 10 | print("Use --script-args to pass data.serverconfig.json file") 11 | exit(1) 12 | 13 | Ansible.print_ansible_info(args[0]) 14 | -------------------------------------------------------------------------------- /orthos2/data/scripts/show_machine_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from typing import Any 3 | 4 | from orthos2.taskmanager.tasks.ansible import Ansible 5 | 6 | 7 | def run(*args: Any) -> None: 8 | 9 | if not args: 10 | print("Use --script-args to pass data.serverconfig.json file") 11 | exit(1) 12 | 13 | Ansible.print_machine_info(args[0]) 14 | -------------------------------------------------------------------------------- /orthos2/data/scripts/store_machine_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from typing import Any 3 | 4 | from orthos2.taskmanager.tasks.ansible import Ansible 5 | 6 | 7 | def run(*args: Any) -> None: 8 | 9 | if not args: 10 | print("Use --script-args to pass data.serverconfig.json file") 11 | exit(1) 12 | 13 | Ansible.store_machine_info(args[0]) 14 | -------------------------------------------------------------------------------- /orthos2/data/static/js/machine_admin.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function () { 2 | (function ($) { 3 | $(function () { 4 | const architectureSystemMapping = { 5 | 's390x': ['zVM', 'zKVM', 'LPAR zSeries'], 6 | 'x86_64': ['BareMetal', 'KVM', 'XEN', 'HMC'], 7 | 8 | 'ppc64': ['LPAR PowerPC', 'PowerVM', 'KVM', 'HMC', 'BareMetal'], 9 | 'ppc64le': ['LPAR PowerPC', 'PowerVM', 'KVM', 'HMC', 'BareMetal'], 10 | 11 | 'aarch64': ['BareMetal', 'KVM'], 12 | 'embedded': ['BareMetal', 'Storage Array', 'FC Switch', 'Network Switch', 'Omni-Path Switch'] 13 | }; 14 | 15 | function filterSystems() { 16 | const architectureSelect = document.getElementById('id_architecture'); 17 | const systemSelect = document.getElementById('id_system'); 18 | const selectedSystem = systemSelect.options[systemSelect.selectedIndex].text; 19 | const selectedArchitecture = architectureSelect.options[architectureSelect.selectedIndex].text; 20 | const allowedSystems = architectureSystemMapping[selectedArchitecture] || []; 21 | 22 | if (!allowedSystems.includes(selectedSystem)) { 23 | systemSelect.selectedIndex = 0; 24 | } 25 | 26 | for (let i = 0; i < systemSelect.options.length; i++) { 27 | const systemOption = systemSelect.options[i]; 28 | if (allowedSystems.length === 0 || allowedSystems.includes(systemOption.text)) { 29 | systemOption.style.display = 'block'; 30 | systemOption.disabled = false; 31 | } else { 32 | systemOption.disabled = true; 33 | } 34 | } 35 | } 36 | 37 | document.getElementById('id_architecture').addEventListener('change', filterSystems); 38 | filterSystems(); 39 | }); 40 | })(django.jQuery); 41 | }); 42 | -------------------------------------------------------------------------------- /orthos2/data/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/data/tests/__init__.py -------------------------------------------------------------------------------- /orthos2/data/validators.py: -------------------------------------------------------------------------------- 1 | import validators 2 | from django.core.exceptions import ValidationError 3 | from django.utils.translation import gettext_lazy as _ 4 | 5 | 6 | def validate_mac_address(mac_address: str) -> None: 7 | """Validate MAC address format.""" 8 | if not validators.mac_address(mac_address): 9 | raise ValidationError("'{}' is not a valid MAC address!".format(mac_address)) 10 | -------------------------------------------------------------------------------- /orthos2/frontend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/__init__.py -------------------------------------------------------------------------------- /orthos2/frontend/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class FrontendConfig(AppConfig): 5 | name = "orthos2.frontend" 6 | -------------------------------------------------------------------------------- /orthos2/frontend/decorators.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Concatenate, ParamSpec, TypeVar 2 | 3 | from django.core.exceptions import BadRequest, PermissionDenied 4 | from django.http import Http404, HttpRequest 5 | 6 | from orthos2.data.models import Machine 7 | 8 | P = ParamSpec("P") 9 | R = TypeVar("R") 10 | 11 | 12 | def check_permissions( 13 | key: str = "id", 14 | ) -> Callable[ 15 | [Callable[Concatenate[HttpRequest, P], R]], Callable[Concatenate[HttpRequest, P], R] 16 | ]: 17 | def decorator( 18 | function: Callable[Concatenate[HttpRequest, P], R] 19 | ) -> Callable[Concatenate[HttpRequest, P], R]: 20 | def wrapper(request: HttpRequest, *args: P.args, **kwargs: P.kwargs) -> R: 21 | """ 22 | Check access permission for machine. 23 | 24 | Only superusers can access administrative machines and/or systems. 25 | Raises `PermissionDenied` exception if a user is not authorized. 26 | """ 27 | try: 28 | ident = kwargs.get(key, None) 29 | if ident is None: 30 | raise PermissionDenied("Bad key %s", key) 31 | if key == "fqdn": 32 | machine = Machine.objects.get(fqdn=ident) 33 | elif key in ("id", "machine_id"): 34 | if not isinstance(ident, (str, int)): 35 | raise PermissionDenied( 36 | 'Bad type of key "%s" must be int or str.', key 37 | ) 38 | machine = Machine.objects.get(pk=ident) 39 | else: 40 | raise BadRequest("Incorrect key given!") 41 | except Machine.DoesNotExist: 42 | raise Http404("Machine does not exist!") 43 | 44 | if machine.administrative or machine.system.administrative: 45 | if not request.user.is_superuser: 46 | raise PermissionDenied("Machine is administrative!") 47 | return function(request, *args, **kwargs) 48 | 49 | return wrapper 50 | 51 | return decorator 52 | -------------------------------------------------------------------------------- /orthos2/frontend/forms/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module groups together all Django Forms that the application needs. Forms for the API should be living inside the 3 | "orthos2.api.forms" package. 4 | """ 5 | -------------------------------------------------------------------------------- /orthos2/frontend/forms/passwordrestore.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains the logic to help restoring the password of a user. 3 | """ 4 | 5 | from typing import Any 6 | 7 | from django import forms 8 | 9 | 10 | class PasswordRestoreForm(forms.Form): 11 | """ 12 | Form to restore the password of a user with the built-in authentication provider. 13 | """ 14 | 15 | def __init__(self, *args: Any, **kwargs: Any) -> None: 16 | username = kwargs.pop("username", None) 17 | 18 | super(PasswordRestoreForm, self).__init__(*args, **kwargs) 19 | 20 | if username is not None: 21 | self.fields["login"].initial = username 22 | 23 | login = forms.CharField( 24 | max_length=100, 25 | required=True, 26 | widget=forms.TextInput(attrs={"class": "form-control"}), 27 | ) 28 | 29 | email = forms.EmailField( 30 | max_length=64, 31 | required=True, 32 | widget=forms.TextInput(attrs={"class": "form-control"}), 33 | ) 34 | -------------------------------------------------------------------------------- /orthos2/frontend/forms/preferences.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains the logic to show the preferences of a user. 3 | """ 4 | 5 | from django import forms 6 | 7 | 8 | class PreferencesForm(forms.Form): 9 | """ 10 | Form to show the preferences of a user with the built-in authentication provider. 11 | """ 12 | 13 | def clean(self) -> None: 14 | cleaned_data = super(PreferencesForm, self).clean() 15 | new_password = cleaned_data.get("new_password") # type: ignore 16 | new_password2 = cleaned_data.get("new_password2") # type: ignore 17 | 18 | if len(new_password) < 8: # type: ignore 19 | self.add_error( 20 | "new_password", 21 | "Password is too short. It must contain at least 8 characters.", 22 | ) 23 | 24 | if new_password != new_password2: 25 | self.add_error( 26 | "new_password", "Password and confirmation password do not match." 27 | ) 28 | 29 | old_password = forms.CharField( 30 | max_length=32, widget=forms.PasswordInput(attrs={"class": "form-control"}) 31 | ) 32 | 33 | new_password = forms.CharField( 34 | max_length=32, widget=forms.PasswordInput(attrs={"class": "form-control"}) 35 | ) 36 | 37 | new_password2 = forms.CharField( 38 | label="Password confirmation", 39 | max_length=32, 40 | widget=forms.PasswordInput(attrs={"class": "form-control"}), 41 | ) 42 | -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/bootstrap.css: -------------------------------------------------------------------------------- 1 | bootstrap.min.css -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/fa: -------------------------------------------------------------------------------- 1 | font-awesome-4.7.0 -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/font-awesome-4.7.0/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/font-awesome-4.7.0/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/font-awesome-4.7.0/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/font-awesome-4.7.0/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/font-awesome-4.7.0/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/font-awesome-4.7.0/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/add_vm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/add_vm.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/amd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/amd.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/back.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/ibm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/ibm.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/intel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/intel.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/logout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/logout.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/no.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/orthos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/orthos.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/powerswitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/powerswitch.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/serialconsole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/serialconsole.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/serviceprocessor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/serviceprocessor.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/spinner.gif -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/ui-icons_444444_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/ui-icons_444444_256x240.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/ui-icons_555555_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/ui-icons_555555_256x240.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/unknown.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/images/yes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/static/frontend/images/yes.png -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/jquery-ui.css: -------------------------------------------------------------------------------- 1 | jquery-ui.min.css -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/js/chart.js: -------------------------------------------------------------------------------- 1 | Chart.min.js -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/js/jquery-ui.js: -------------------------------------------------------------------------------- 1 | jquery-ui-1.12.1.min.js -------------------------------------------------------------------------------- /orthos2/frontend/static/frontend/js/jquery.js: -------------------------------------------------------------------------------- 1 | jquery-1.12.4.min.js -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/console.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/machines/machine.html' %} 2 | {% load static %} 3 | 4 | {% block tabcontent %} 5 |
6 |
7 |
8 | 9 | {% endblock %} 10 | 11 | {% block javascript_imports %} 12 | 13 | 14 | {% endblock %} 15 | 16 | {% block javascript %} 17 | 37 | {% endblock %} 38 | 39 | {% block links %} 40 | 41 | {% endblock %} 42 | 43 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/cpu.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/machines/machine.html' %} 2 | 3 | {% block tabcontent %} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
Model{{ machine.cpu_model }}
ID{{ machine.cpu_id }}
Flags{{ machine.cpu_flags }}
Physical{{ machine.cpu_physical }}
Cores{{ machine.cpu_cores }}
Threads{{ machine.cpu_threads }}
Speed{{ machine.cpu_speed }}
36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/history.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/machines/machine.html' %} 2 | 3 | {% block tabcontent %} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% for reservation in machine.reservationhistory_set.all|dictsortreversed:"created" %} 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% endfor %} 22 | 23 |
UserAtUntilReason
{{ reservation.reserved_by }}{{ reservation.reserved_at }}{{ reservation.reserved_until }}{{ reservation.reserved_reason }}
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/installations.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/machines/machine.html' %} 2 | {% load filters %} 3 | 4 | {% block tabcontent %} 5 | 6 | 7 | 13 | 14 | {% for installation in machine.installations.all %} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% endfor %} 23 | 24 |
Active 8 | Distribution 9 | Architecture 10 | Kernel Version 11 | Partition 12 |
{{ installation.active|boolean_image:15|safe }}{{ installation.distribution }}{{ installation.architecture }}{{ installation.kernelversion }}{{ installation.partition }}
25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/miscellaneous.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/machines/machine.html' %} 2 | 3 | {% block tabcontent %} 4 | 5 |
6 | hwinfo 7 | dmidecode 8 | dmesg 9 | lsmod 10 |
11 | 12 | 13 |
14 |
hwinfo Output
15 |
16 | 17 |
18 | {{ machine.hwinfo }}
19 | 
20 |
Back to top... 21 | 22 | 23 |
24 |
dmidecode Output
25 |
26 | 27 |
28 | {{ machine.dmidecode }}
29 | 
30 |
Back to top... 31 | 32 | 33 |
34 |
dmesg Output
35 |
36 | 37 |
38 | {{ machine.dmesg }}
39 | 
40 |
Back to top... 41 | 42 | 43 |
44 |
lsmod Output
45 |
46 | 47 |
48 | {{ machine.lsmod }}
49 | 
50 |
Back to top... 51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/networkinterfaces.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/machines/machine.html' %} 2 | {% load filters %} 3 | 4 | {% block tabcontent %} 5 |
6 |
Network Interfaces
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {% for network_interface in machine.networkinterfaces.all %} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {% endfor %} 27 | 28 |
MAC AddressPrimaryNameDriver ModuleEthernet Type
{{ network_interface.mac_address }}{{ network_interface.primary|boolean_image:15|safe }}{{ network_interface.name }}{{ network_interface.driver_module }}{{ network_interface.ethernet_type }}
29 | 30 |
31 |
Management Interfaces
32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 |
MAC AddressBMCComment
{{ machine.bmc.mac }} 44 | {{ machine.bmc.fqdn }} 45 | (http|https){{ machine.bmc.comment }}
51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/pci.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/machines/machine.html' %} 2 | {% load filters %} 3 | 4 | {% block tabcontent %} 5 |
6 |
PCI Devices
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {% for pci_device in machine.pcidevice_set.all %} 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% endfor %} 25 | 26 |
SlotClassVendorDevice
{{ pci_device.slot }}{{ pci_device.classname }} ({{ pci_device.class_id }}){{ pci_device.vendor }}{{ pci_device.device }}
27 | 28 |
29 |
lspci Output
lspci -vvv -nn 30 |
31 | 32 |
33 | {{ machine.lspci|pcihooks }}
34 | 
35 | {% endblock %} 36 | 37 | {% block javascript %} 38 | 45 | {% endblock %} 46 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/scsi.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/machines/machine.html' %} 2 | 3 | {% block tabcontent %} 4 |
5 |
lsscsi Output
6 |
7 | 8 |
 9 | {{ machine.lsscsi }}
10 | 
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/snippets/annotations.html: -------------------------------------------------------------------------------- 1 | {% load filters %} 2 | {% load static %} 3 |
4 |
Annotations
Machine Features, Glitches, Hardware Upgrades, Bios Updates, etc. 5 |
6 | 7 |
8 | 9 | 10 | {% for annotation in machine.annotations.all %} 11 | 12 | 18 | 19 | {% endfor %} 20 | 21 |
13 | {{ annotation.text|urlizetrunc:100 }} 14 | 15 | ({{ annotation.reporter }} {{ annotation.created|date:"SHORT_DATE_FORMAT" }}) 16 | 17 |
22 |
23 | 24 |
25 | 26 | 27 | 28 | 29 |
30 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/snippets/machinetitle.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 |
3 |

{{ machine.fqdn }}

4 |
5 | {% if machine.enclosure.location_room and machine.enclosure.location_room != "unknown" %} 6 | {{ machine.enclosure.location_room }} > 7 | {{ machine.enclosure.location_rack }} > 8 | {{ machine.enclosure.location_rack_position }} 9 | {% endif %} 10 |
11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/snippets/platform.html: -------------------------------------------------------------------------------- 1 | {% load filters %} 2 |
3 |
Platform
Enclosure > System 4 |
5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
10 | EnclosureSystem
Vendor{{ machine.enclosure.platform.vendor.name }}{{ machine.platform.vendor }}
Platform{{ machine.enclosure.platform }}{{ machine.platform|default_if_none:"" }}
27 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/snippets/reservation.html: -------------------------------------------------------------------------------- 1 | {% load filters %} 2 |
3 |
Reservation
Current Reservation Status 4 |
5 | 6 | {% if machine.reserved_by %} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Owner{{ machine.reserved_by }}
Period 16 | {{ machine.reserved_at }} 17 | {% if machine.is_reserved_infinite %} 18 | - infinite 19 | {% else %} 20 | - {{ machine.reserved_until }} 21 | {% endif %} 22 |
Reason{{ machine.reserved_reason }}
30 | {% else %} 31 |

Machine is not reserved.

32 | {% endif %} 33 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/snippets/sidebar_virtualization.html: -------------------------------------------------------------------------------- 1 | {% load filters %} 2 |
3 |
Actions
4 |
5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/snippets/status.html: -------------------------------------------------------------------------------- 1 | {% load filters %} 2 | {% load tags %} 3 |
4 |
Status
Last check: {{ machine.last_check }} 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {% status_ipv4 machine %} 19 | {% status_ipv6 machine %} 20 | {% status_ssh machine %} 21 | {% status_login machine %} 22 | 23 | 24 |
IPv4IPv6SSHLogin
25 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/detail/usb.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/machines/machine.html' %} 2 | 3 | {% block tabcontent %} 4 |
5 |
lsusb Output
6 |
7 | 8 |
 9 | {{ machine.lsusb }}
10 | 
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/history.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/base.html' %} 2 | {% load static %} 3 | 4 | {% block navbar %} 5 | {% include 'frontend/snippet.navlinks.html' %} 6 | {% endblock %} 7 | 8 | {% block content %} 9 |
10 |
11 |  Back 12 |
13 |
14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {% for reservation in machine.reservationhistory_set.all|dictsortreversed:"created" %} 28 | 29 | 30 | 31 | 32 | 33 | 34 | {% endfor %} 35 | 36 |
Reservation History of {{ machine.fqdn }}
UserAtUntilReason
{{ reservation.reserved_by }}{{ reservation.reserved_at }}{{ reservation.reserved_until }}{{ reservation.reserved_reason }}
37 |
38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/reserve.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/base.html' %} 2 | {% load static %} 3 | 4 | {% block javascript %} 5 | 16 | {% endblock %} 17 | 18 | {% block navbar %} 19 | {% include 'frontend/snippet.navlinks.html' %} 20 | {% endblock %} 21 | 22 | {% block content %} 23 |
24 |
25 |
26 | {% include 'frontend/machines/detail/snippets/machinetitle.html' %} 27 |
28 |
29 |
30 | 31 |
32 |
33 |
34 |
35 | {% csrf_token %} 36 | 37 | {{ form.reason.errors }} 38 |
39 |
40 | 41 |
42 |
43 | {{ form.reason }} 44 | {{ form.reason.help_text }} 45 |
46 |
47 | 48 | {{ form.until.errors }} 49 |
50 |
51 | 52 |
53 |
54 | {{ form.until }} 55 | {{ form.until.help_text }} 56 |
57 | 58 | 59 | 60 |
61 |
62 |
63 | {% endblock %} 64 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/machines/setup.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/base.html' %} 2 | {% load static %} 3 | 4 | {% block navbar %} 5 | {% include 'frontend/snippet.navlinks.html' %} 6 | {% endblock %} 7 | 8 | {% block content %} 9 |
10 |
11 |
12 | {% include 'frontend/machines/detail/snippets/machinetitle.html' %} 13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | {% csrf_token %} 22 | 23 | {{ form.setup.errors }} 24 |
25 |
26 | 27 |
28 |
29 | {{ form.setup }} 30 | {{ form.setup.help_text }} 31 |
32 | 33 | 34 | 35 |
36 |
37 |
38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/registration/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/base.html' %} 2 | {% load tags %} 3 | 4 | {% block navbar %} 5 | 18 | {% endblock %} 19 | 20 | {% block content %} 21 |
22 |
23 |
24 | 36 |
37 |
38 |
39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/registration/password_reset.html: -------------------------------------------------------------------------------- 1 | {% extends 'frontend/base.html' %} 2 | {% load tags %} 3 | 4 | {% block navbar %} 5 | 16 | {% endblock %} 17 | 18 | {% block content %} 19 |
20 |
21 |
22 | {{ form.non_field_errors }} 23 |
24 | {% csrf_token %} 25 | 26 | {{ form.login.errors }} 27 |
28 |
29 | 30 |
31 |
32 | {{ form.login }} 33 |
34 |
35 | 36 | {{ form.email.errors }} 37 |
38 |
39 | 40 |
41 |
42 | {{ form.email }} 43 |
44 |
45 | 46 | 47 |
48 |
49 |
50 |
51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/snippet.navlinks.html: -------------------------------------------------------------------------------- 1 | {% load tags %} 2 | 22 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/snippet.paginator.html: -------------------------------------------------------------------------------- 1 | {% load tags %} 2 | 3 | {% if is_paginated %} 4 |
5 |
6 |
7 |
    8 | 9 | {% if page_obj.has_previous %} 10 |
  • 11 | Previous 12 |
  • 13 | {% else %} 14 |
  • 15 | 16 | Previous 17 | 18 |
  • 19 | {% endif %} 20 | 21 | {% for number in page_obj.paginator.page_range %} 22 |
  • 23 | {{ number }} 24 |
  • 25 | {% endfor %} 26 | 27 | {% if page_obj.has_next %} 28 |
  • 29 | Next 30 |
  • 31 | {% else %} 32 |
  • 33 | Next 34 |
  • 35 | {% endif %} 36 | 37 |
38 |
39 |
40 |
41 | {% endif %} 42 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/snippet.search.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /orthos2/frontend/templates/frontend/snippet.userbar.html: -------------------------------------------------------------------------------- 1 | {% load tags %} 2 | {% load static %} 3 |
4 |
5 | {% if user.is_authenticated %} 6 | {% if user.is_superuser %} 7 | {{ user.username }} 8 | {% else %} 9 | {{ user.username }} 10 | {% endif %} 11 | 12 | Preferences 13 | {% endif %} 14 | 15 | 16 | {% if user.is_superuser or not user.is_authenticated %} 17 | Administration 18 | {% endif %} 19 | 20 | Bugreport 21 | 22 | Download CLI 23 | 24 | Documentation 25 | 26 | {% if user.is_authenticated %} 27 | 28 |
29 | {% csrf_token %} 30 | 31 |
32 | {% endif %} 33 |
34 |
35 | -------------------------------------------------------------------------------- /orthos2/frontend/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/templatetags/__init__.py -------------------------------------------------------------------------------- /orthos2/frontend/templatetags/filters.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import Optional 3 | 4 | from django import template 5 | from django.templatetags.static import static 6 | from django.utils.html import escape 7 | from django.utils.safestring import SafeString, mark_safe 8 | 9 | register = template.Library() 10 | 11 | 12 | @register.filter 13 | def divide(value: float, arg: int) -> Optional[str]: 14 | """Divide ``value`` and return float value rounded to one decimal place.""" 15 | try: 16 | return format(float(value) / arg, ".1f") 17 | except (ValueError, ZeroDivisionError): 18 | return None 19 | 20 | 21 | @register.filter 22 | def boolean_image(value: bool, size: int = 25) -> str: 23 | """Return an HTML image tag with ``size`` (width in px) if ``value`` is true.""" 24 | if value: 25 | return "✅" 26 | else: 27 | return "✕" 28 | 29 | 30 | @register.filter 31 | def vendor_image(cpu_model: str, size: int = 25) -> SafeString: 32 | """Return an HTML image tag with ``size`` (width in px) and the repective vendor logo.""" 33 | cpu_model = cpu_model.lower() 34 | 35 | img = ''.format(size) 36 | 37 | if "intel" in cpu_model: 38 | return mark_safe(img.format(static("frontend/images/intel.png"))) 39 | 40 | if "amd" in cpu_model: 41 | return mark_safe(img.format(static("frontend/images/amd.png"))) 42 | 43 | if "power" in cpu_model: 44 | return mark_safe(img.format(static("frontend/images/ibm.png"))) 45 | 46 | return mark_safe('') 47 | 48 | 49 | @register.filter 50 | def pcihooks(lspci: str) -> SafeString: 51 | """ 52 | Escapes the lspci string and set HTML hooks for every PCI slot ID. 53 | 54 | Example: 55 | "00:00.0 ..." > "00:00.0 ..." 56 | """ 57 | result = re.sub( 58 | r"([a-fA-F0-9]+:[a-fA-F0-9]+.[a-fA-F0-9]+) (.*)", 59 | r'\1 \2', 60 | str(escape(lspci)), 61 | ) 62 | return mark_safe(result) 63 | -------------------------------------------------------------------------------- /orthos2/frontend/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/tests/__init__.py -------------------------------------------------------------------------------- /orthos2/frontend/tests/admin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/tests/admin/__init__.py -------------------------------------------------------------------------------- /orthos2/frontend/tests/admin/test_machine.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django_webtest import WebTest # type: ignore 3 | 4 | 5 | class AddMachine(WebTest): 6 | def test_add_new_machine(self) -> None: 7 | pass 8 | 9 | 10 | class EditMachine(TestCase): 11 | 12 | pass 13 | 14 | 15 | class DeleteMachine(TestCase): 16 | 17 | pass 18 | -------------------------------------------------------------------------------- /orthos2/frontend/tests/fixtures/serverconfigs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "data.serverconfig", 4 | "pk": null, 5 | "fields": { 6 | "key": "orthos.web.welcomemessage", 7 | "value": "Hello to the web frontend!" 8 | } 9 | }, 10 | { 11 | "model": "data.serverconfig", 12 | "pk": null, 13 | "fields": { 14 | "key": "auth.account.creation", 15 | "value": "bool:true" 16 | } 17 | }, 18 | { 19 | "model": "data.serverconfig", 20 | "pk": null, 21 | "fields": { 22 | "key": "mail.validdomains", 23 | "value": "example.de, example.com, foo.bar" 24 | } 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /orthos2/frontend/tests/user/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/frontend/tests/user/__init__.py -------------------------------------------------------------------------------- /orthos2/frontend/tests/user/fixtures/users.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "auth.user", 4 | "pk": null, 5 | "fields": { 6 | "username": "superuser", 7 | "password": "pbkdf2_sha256$36000$FFhkWCuL1AyE$QFlRwInY3INsxQ0Y+N5rWGpp/3MSTsU72PRkkEnZkvs=", 8 | "email": "superuser@foo.bar", 9 | "is_superuser": true, 10 | "is_staff": true 11 | } 12 | }, 13 | { 14 | "model": "auth.user", 15 | "pk": null, 16 | "fields": { 17 | "username": "user", 18 | "password": "pbkdf2_sha256$36000$FFhkWCuL1AyE$QFlRwInY3INsxQ0Y+N5rWGpp/3MSTsU72PRkkEnZkvs=", 19 | "email": "user@foo.bar", 20 | "is_superuser": false, 21 | "is_staff": false 22 | } 23 | }, 24 | { 25 | "model": "auth.user", 26 | "pk": null, 27 | "fields": { 28 | "username": "user-nopassword", 29 | "email": "user-nopassword@foo.bar", 30 | "is_superuser": false, 31 | "is_staff": false 32 | } 33 | }, 34 | { 35 | "model": "auth.user", 36 | "pk": null, 37 | "fields": { 38 | "username": "user-nopassword-multiple1", 39 | "email": "user-nopassword-multiple@foo.bar", 40 | "is_superuser": false, 41 | "is_staff": false 42 | } 43 | }, 44 | { 45 | "model": "auth.user", 46 | "pk": null, 47 | "fields": { 48 | "username": "user-nopassword-multiple2", 49 | "email": "user-nopassword-multiple@foo.baz", 50 | "is_superuser": false, 51 | "is_staff": false 52 | } 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /orthos2/frontend/tests/user/test_ldap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Author Jan Löser 5 | # Published under the GNU Public Licence 2 6 | 7 | from django.urls import reverse 8 | from django_webtest import WebTest # type: ignore 9 | 10 | from orthos2.data.models.serverconfig import ServerConfig 11 | 12 | 13 | class LDAP(WebTest): 14 | 15 | csrf_checks = True 16 | 17 | fixtures = [ 18 | "orthos2/frontend/tests/fixtures/serverconfigs.json", 19 | "orthos2/frontend/tests/user/fixtures/users.json", 20 | ] 21 | 22 | def test_enabled_user_creation(self) -> None: 23 | """ 24 | Tests if user creation is enabled. 25 | """ 26 | with self.settings(AUTH_ALLOW_USER_CREATION=True): 27 | page = self.app.get(reverse("frontend:login")) 28 | self.assertContains(page, "Create Account") 29 | self.assertContains(page, "Restore Password") 30 | 31 | def test_disabled_user_creation(self) -> None: 32 | """ 33 | Tests if user creation is disabled. 34 | """ 35 | with self.settings(AUTH_ALLOW_USER_CREATION=False): 36 | creation = ServerConfig.objects.get(key="auth.account.creation") 37 | creation.value = "bool:false" 38 | creation.save() 39 | 40 | page = self.app.get(reverse("frontend:login")) 41 | self.assertNotContains(page, "Create Account") 42 | self.assertNotContains(page, "Restore Password") 43 | 44 | form = self.app.get(reverse("frontend:create_user")).form 45 | form["login"] = "new-user" 46 | form["email"] = "new-user@example.com" 47 | form["password"] = "linux1234" 48 | form["password2"] = "linux1234" 49 | page = form.submit().maybe_follow() 50 | 51 | self.assertNotEqual(page.context["user"].username, "new-user") 52 | self.assertContains(page, "Account creation is disabled!") 53 | -------------------------------------------------------------------------------- /orthos2/frontend/tests/user/test_logout.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | from django_webtest import WebTest # type: ignore 3 | 4 | 5 | class Logout(WebTest): 6 | 7 | csrf_checks = True 8 | 9 | fixtures = [ 10 | "orthos2/frontend/tests/fixtures/serverconfigs.json", 11 | "orthos2/frontend/tests/user/fixtures/users.json", 12 | ] 13 | 14 | def test_successful_logout(self) -> None: 15 | """Test if a user can log out successfully.""" 16 | # Arrange 17 | page = self.app.get(reverse("frontend:free_machines"), user="user") 18 | 19 | # Assert user is logged in 20 | self.assertEqual(page.context["user"].username, "user") 21 | 22 | # Act 23 | logout_form = page.forms[0] 24 | res = logout_form.submit("Logout").maybe_follow() 25 | 26 | # Assert 27 | self.assertContains(res, "Login") 28 | -------------------------------------------------------------------------------- /orthos2/frontend/tests/user/test_statistics.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | from django_webtest import WebTest # type: ignore 3 | 4 | 5 | class Statistics(WebTest): 6 | 7 | csrf_checks = True 8 | 9 | fixtures = [] # type: ignore 10 | 11 | def test_statistics_view(self) -> None: 12 | """Test if statistics view comes up.""" 13 | page = self.app.get(reverse("frontend:free_machines"), user="user") 14 | 15 | self.assertEqual(page.context["user"].username, "user") 16 | 17 | page = page.click("Statistics").maybe_follow() 18 | 19 | self.assertContains(page, "Numbers") 20 | -------------------------------------------------------------------------------- /orthos2/frontend/views/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains all Django frontend views. For convenience, they are all reexported. 3 | """ 4 | 5 | from .ajax import * # noqa: F401 6 | from .auth import * # noqa: F401 7 | from .machine import * # noqa: F401 8 | from .machines import * # noqa: F401 9 | from .regenerate import * # noqa: F401 10 | from .statistics import * # noqa: F401 11 | from .user import * # noqa: F401 12 | -------------------------------------------------------------------------------- /orthos2/meta/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/meta/__init__.py -------------------------------------------------------------------------------- /orthos2/meta/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MetaConfig(AppConfig): 5 | name = "orthos2.meta" 6 | -------------------------------------------------------------------------------- /orthos2/meta/data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/meta/data/__init__.py -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | 3 | # disable default cowsay usage for ansible output 4 | nocows = 1 5 | inventory = inventory.yml 6 | interpreter_python = auto 7 | strategy = free 8 | private_key_file = /home/orthos/.ssh/master 9 | 10 | [ssh_connection] 11 | 12 | # improve speed by using less ssh connections. 13 | # See https://docs.ansible.com/ansible/2.10/reference_appendices/config.html#ansible-pipelining 14 | pipelining = True 15 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/inventory.template: -------------------------------------------------------------------------------- 1 | all: 2 | hosts: 3 | {% for machine in machine_list %} 4 | {{ machine }}: 5 | {% endfor %} 6 | vars: 7 | ansible_connection: ssh 8 | ansible_user: root 9 | ansible_ssh_extra_args: -o ConnectionAttempts=1 -o ConnectTimeout=5 -o StrictHostKeyChecking=no 10 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/roles/add_custom_facts/files/facts.d/dmesg.fact: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | import sys 6 | 7 | data = dict() 8 | 9 | command = "if [ -e /var/log/boot.msg ]; then cat /var/log/boot.msg; else journalctl -xl | head -n200; fi" 10 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) 11 | data.update({"-xl":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 12 | 13 | json_string = json.dumps(data) 14 | sys.stdout.write(json_string) 15 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/roles/add_custom_facts/files/facts.d/dmidecode.fact: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import platform 5 | import subprocess 6 | import sys 7 | 8 | data = dict() 9 | 10 | if platform.machine() in ("i386", "i486", "i586", "i686", "x86_64", "aarch64", "aarch64_be", "arm64"): 11 | command = ["dmidecode"] 12 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 13 | data.update({"noargs":{"stdout":process.stdout.read(), "stderr":process.stderr.read()}}) 14 | 15 | json_string = json.dumps(data) 16 | sys.stdout.write(json_string) 17 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/roles/add_custom_facts/files/facts.d/hwinfo.fact: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | import sys 6 | 7 | data = dict() 8 | 9 | command = ["hwinfo"] 10 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 11 | data.update({"noargs":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 12 | 13 | command = ["hwinfo", "--disk"] 14 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 15 | data.update({"--disk":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 16 | 17 | command = ["hwinfo", "--network"] 18 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 19 | data.update({"--network":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 20 | 21 | command = ["hwinfo", "--bios", "--block", "--bridge", "--cdrom", "--cpu", 22 | "--disk", "--floppy", "--framebuffer", "--gfxcard" , "--hub", "--ide", 23 | "--isapnp", "--isdn", "--keyboard", "--memory", "--monitor", "--mouse", 24 | "--netcard", "--network", "--partition", "--pci", "--pcmcia", "--scsi", 25 | "--smp", "--sound", "--sys", "--tape", "--tv", "--usb", "--usb-ctrl", 26 | "--wlan"] 27 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 28 | data.update({"full":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 29 | 30 | json_string = json.dumps(data) 31 | sys.stdout.write(json_string) 32 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/roles/add_custom_facts/files/facts.d/last.fact: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | import sys 6 | 7 | data = dict() 8 | 9 | command = "last | grep -v reboot | head -n 1" 10 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) 11 | data.update({"latest":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 12 | 13 | json_string = json.dumps(data) 14 | sys.stdout.write(json_string) 15 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/roles/add_custom_facts/files/facts.d/lsmod.fact: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | import sys 6 | 7 | data = dict() 8 | 9 | command = ["lsmod"] 10 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 11 | data.update({"noargs":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 12 | 13 | json_string = json.dumps(data) 14 | sys.stdout.write(json_string) 15 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/roles/add_custom_facts/files/facts.d/lspci.fact: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | import sys 6 | 7 | data = dict() 8 | 9 | command = ["lspci"] 10 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 11 | data.update({"noargs":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 12 | 13 | command = ["lspci", "-vvv", "-nn"] 14 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 15 | data.update({"-vvv -nn":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 16 | 17 | command = ["lspci", "-mmvn"] 18 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 19 | data.update({"-mmvn":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 20 | 21 | json_string = json.dumps(data) 22 | sys.stdout.write(json_string) 23 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/roles/add_custom_facts/files/facts.d/lsscsi.fact: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | import sys 6 | 7 | data = dict() 8 | 9 | command = ["lsscsi", "-s"] 10 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 11 | data.update({"-s":{"stdout": process.stdout.read(), "stderr":process.stderr.read()}}) 12 | 13 | json_string = json.dumps(data) 14 | sys.stdout.write(json_string) 15 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/roles/add_custom_facts/files/facts.d/lsusb.fact: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import subprocess 5 | import sys 6 | 7 | data = dict() 8 | 9 | command = ["lsusb"] 10 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 11 | data.update({"noargs": {"stdout" : process.stdout.read(), "stderr" : process.stderr.read()}}) 12 | 13 | json_string = json.dumps(data) 14 | sys.stdout.write(json_string) 15 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/roles/add_custom_facts/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Create ansible directory 2 | ansible.builtin.file: 3 | path: /etc/ansible 4 | recurse: yes 5 | state: directory 6 | 7 | - name: Copy custom fact scripts to remote host 8 | ansible.builtin.copy: 9 | src: files/facts.d/ 10 | dest: /etc/ansible/facts.d 11 | mode: 0755 12 | 13 | - name: Re-read facts after adding custom fact 14 | ansible.builtin.setup: 15 | filter: ansible_local 16 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/roles/get_machine_information/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: saving gathered facts to local file 2 | local_action: 3 | module: copy 4 | content: "{{ ansible_facts }}" 5 | dest: "/run/orthos2/ansible/{{ ansible_facts.fqdn }}.json" 6 | 7 | - name: saving gathered facts to remote machine 8 | copy: 9 | content: "{{ ansible_facts }}" 10 | dest: "/tmp/ansible_facts.json" 11 | 12 | -------------------------------------------------------------------------------- /orthos2/meta/data/ansible/site.yml: -------------------------------------------------------------------------------- 1 | - hosts: all 2 | any_errors_fatal: true 3 | roles: 4 | - add_custom_facts 5 | - get_machine_information 6 | -------------------------------------------------------------------------------- /orthos2/meta/data/bin/orthos-admin: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Django wrapper script for orthos server managmenent 4 | 5 | sudo -i -u orthos DJANGO_SETTINGS_MODULE=orthos2.settings django-admin $@ 6 | -------------------------------------------------------------------------------- /orthos2/meta/data/logrotate/orthos2: -------------------------------------------------------------------------------- 1 | /var/log/orthos2/*.log { 2 | compress 3 | dateext 4 | maxage 365 5 | rotate 99 6 | size=+16M 7 | notifempty 8 | missingok 9 | su orthos orthos 10 | create 644 orthos orthos 11 | # sharedscripts 12 | # prerotate 13 | # systemctl stop orthos2 14 | # endscript 15 | # postrotate 16 | # systemctl start orthos2 17 | # endscript 18 | } 19 | -------------------------------------------------------------------------------- /orthos2/meta/data/nginx/orthos2_nginx.conf: -------------------------------------------------------------------------------- 1 | # orthos2_nginx.conf 2 | 3 | # the upstream component nginx needs to connect to 4 | upstream django { 5 | server 127.0.0.1:8000; 6 | } 7 | 8 | # configuration of the server 9 | server { 10 | # the port your site will be served on 11 | listen 80; 12 | # the domain name it will serve for 13 | server_name 127.0.0.1; # substitute your machine's IP address or FQDN 14 | charset utf-8; 15 | 16 | # max upload size 17 | client_max_body_size 75M; # adjust to taste 18 | 19 | 20 | 21 | location /static { 22 | alias /srv/www/orthos2/static; # your Django project's static files - amend as required 23 | } 24 | 25 | # Finally, send all non-media requests to the Django server. 26 | location / { 27 | proxy_pass http://django; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /orthos2/meta/data/scripts/README: -------------------------------------------------------------------------------- 1 | Scripts in that directory: 2 | 3 | machine_*: 4 | Gets executed on the managed host for hardware scanning. 5 | 6 | server_*: 7 | Gets executed on the Orthos server directly. 8 | 9 | -------------------------------------------------------------------------------- /orthos2/meta/data/scripts/create_bridge.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright by Vanessa Wallfahrer , SUSE GmBH 4 | 5 | #Check if default route is already br0 6 | defaultroute=$(ip route show |grep "^default" |awk '{print $5}') 7 | dir="/sys/class/net/$defaultroute/bridge" 8 | if [ -d "$dir" ]; then 9 | exit 0 10 | fi 11 | 12 | #Create br0 file 13 | echo "BOOTPROTO='dhcp' 14 | BRIDGE='yes' 15 | BRIDGE_FORWARDDELAY='0' 16 | BRIDGE_PORTS='$defaultroute' 17 | BRIDGE_STP='off' 18 | STARTMODE='auto'" > /etc/sysconfig/network/ifcfg-br0 19 | 20 | #Change BOOTPROTO from 'dhcp' to 'none' in eth file 21 | ethfile="/etc/sysconfig/network/ifcfg-$defaultroute" 22 | [ ! -w $ethfile ] && exit 1 23 | sed -i "s/BOOTPROTO='dhcp'/BOOTPROTO='none'/" $ethfile 24 | wicked ifreload all 25 | -------------------------------------------------------------------------------- /orthos2/meta/data/scripts/machine_get_cpu_flags.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Gets the CPU flags. 4 | # 5 | 6 | function cpu_flags_x86() 7 | { 8 | local FLAGS=$(grep '^flags' /proc/cpuinfo | head -1 | 9 | sed -e 's/.*: //g' | sed -e 's/ \+/ /g') 10 | echo "$FLAGS" 11 | } 12 | 13 | function cpu_flags_other() 14 | { 15 | echo "Unknown" 16 | } 17 | 18 | 19 | case $(uname -i) in 20 | i386|x86_64) cpu_flags_x86 ;; 21 | *) cpu_flags_other ;; 22 | esac 23 | 24 | # vim: set sw=4 ts=4 et: :collapseFolds=1: 25 | -------------------------------------------------------------------------------- /orthos2/meta/data/scripts/machine_get_cpu_id.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | STEPPING_URL=ftp://ftphost.network.tld/orthos/stepping 4 | 5 | if wget -q "$STEPPING_URL" &>/dev/null ; then 6 | chmod 755 ./stepping 7 | CPUID=$(./stepping -s | cut -d : -f 2) 8 | echo -n "$CPUID" 9 | rm ./stepping 10 | exit 0 11 | else 12 | exit 1 13 | fi 14 | -------------------------------------------------------------------------------- /orthos2/meta/data/scripts/machine_get_cpu_number.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Gets the CPU number. 4 | # 5 | 6 | SOCKETS=0 7 | CORES=0 8 | THREADS=0 9 | 10 | function cpu_number_default() 11 | { 12 | THREADS=$(grep -c ^processor /proc/cpuinfo) 13 | SOCKETS=$(grep ^physical /proc/cpuinfo | cut -d : -f 2 | sort | uniq | wc -l) 14 | local cores=$(grep '^cpu cores' /proc/cpuinfo | head -1 | cut -d : -f 2) 15 | if [ -z "$cores" ] || [ "$cores" -eq 0 ] ; then 16 | CORES=$THREADS 17 | else 18 | CORES=$[ $cores * $SOCKETS ] 19 | fi 20 | 21 | if [ -z "$SOCKETS" ] || [ "$SOCKETS" -eq 0 ] ; then 22 | SOCKETS=$THREADS 23 | fi 24 | } 25 | 26 | function cpu_number_ia64() 27 | { 28 | THREADS=$(grep -c ^processor /proc/cpuinfo) 29 | SOCKETS=$(grep ^physical /proc/cpuinfo | cut -d : -f 2 | sort | uniq | wc -l) 30 | local cores=$(grep '^cpu regs' /proc/cpuinfo | head -1 | cut -d : -f 2) 31 | if [ -z "$cores" ] || [ "$cores" -eq 0 ] ; then 32 | CORES=$THREADS 33 | else 34 | CORES=$[ $cores * $SOCKETS ] 35 | fi 36 | 37 | if [ -z "$SOCKETS" ] || [ "$SOCKETS" -eq 0 ] ; then 38 | SOCKETS=$THREADS 39 | fi 40 | } 41 | 42 | function cpu_number_ppc() 43 | { 44 | THREADS=$(grep -c ^processor /proc/cpuinfo) 45 | SOCKETS=$THREADS 46 | CORES=$THREADS 47 | } 48 | 49 | 50 | case $(uname -i) in 51 | ia|ia64) 52 | cpu_number_ia64 53 | ;; 54 | ppc|ppc64) 55 | cpu_number_ppc 56 | ;; 57 | *) 58 | cpu_number_default 59 | ;; 60 | esac 61 | 62 | echo "SOCKETS=${SOCKETS}" 63 | echo "CORES=${CORES}" 64 | echo "THREADS=${THREADS}" 65 | 66 | # vim: set sw=4 ts=4 et: :collapseFolds=1: 67 | -------------------------------------------------------------------------------- /orthos2/meta/data/scripts/machine_get_cpu_speed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Gets the CPU speed 4 | # 5 | 6 | function cpu_speed_default() 7 | { 8 | local mhz=$(grep -m 1 '^cpu MHz' /proc/cpuinfo | sed -e 's/cpu MHz.*: //g') 9 | echo "$mhz * 1000000" | bc -l | sed -e 's/\..*//g' 10 | } 11 | 12 | function cpu_speed_ppc() 13 | { 14 | local mhz=$(grep -m 1 '^clock' /proc/cpuinfo | sed -e 's/clock.*: //g' -e 's/MHz//g') 15 | echo "$mhz * 1000000" | bc -l | sed -e 's/\..*//g' 16 | } 17 | 18 | 19 | 20 | case $(uname -i) in 21 | ppc|ppc64) 22 | cpu_speed_ppc 23 | ;; 24 | *) 25 | cpu_speed_default 26 | ;; 27 | esac 28 | 29 | # vim: set sw=4 ts=4 et: :collapseFolds=1: 30 | -------------------------------------------------------------------------------- /orthos2/meta/data/scripts/machine_get_cpu_type.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Gets the CPU type. 4 | # 5 | 6 | STEPPING_URL=ftp://ftphost.network.tld/orthos/stepping 7 | 8 | ADD_X86_FLAGS=true 9 | 10 | function cpu_type_x86() 11 | { 12 | local MODELNAME=$(grep '^model name' /proc/cpuinfo | head -1 | 13 | sed -e 's/.*: //g' | sed -e 's/ \+/ /g') 14 | 15 | local FLAGS=$(grep '^flags' /proc/cpuinfo | head -1 | 16 | sed -e 's/.*: //g' | sed -e 's/ \+/ /g') 17 | 18 | if wget -q $STEPPING_URL &>/dev/null ; then 19 | chmod 755 ./stepping 20 | NAME=$(./stepping 1| head -1 | cut -c 4-) 21 | echo -n "$MODELNAME" 22 | else 23 | echo -n "$MODELNAME" 24 | fi 25 | # cpu_flags get scanned in a seperat script now 26 | # if $ADD_X86_FLAGS;then 27 | # echo " : $FLAGS" 28 | # else 29 | echo 30 | # fi 31 | 32 | rm -f ./stepping 33 | } 34 | 35 | function cpu_type_ia64() 36 | { 37 | local VENDOR=$(grep ^vendor /proc/cpuinfo | head -1 | sed -e 's/.*: //g') 38 | local FAMILY=$(grep ^family /proc/cpuinfo | head -1 | sed -e 's/.*: //g') 39 | 40 | echo "$FAMILY: $VENDOR" 41 | } 42 | 43 | function cpu_type_ppc() 44 | { 45 | local CPU=$(grep ^cpu /proc/cpuinfo | head -1 | sed -e 's/.*: //g') 46 | 47 | echo "$CPU" 48 | } 49 | 50 | function cpu_type_other() 51 | { 52 | echo "Unknown" 53 | } 54 | 55 | 56 | case $(uname -i) in 57 | ia64) cpu_type_ia64 ;; 58 | i386|x86_64) cpu_type_x86 ;; 59 | ppc|ppc64|ppc64le) cpu_type_ppc ;; 60 | *) cpu_type_other ;; 61 | esac 62 | 63 | # vim: set sw=4 ts=4 et: :collapseFolds=1: 64 | -------------------------------------------------------------------------------- /orthos2/meta/data/scripts/machine_get_firmware.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Gets the firmware version. 4 | # 5 | 6 | DMIDECODE=/usr/sbin/dmidecode 7 | 8 | function firmware_default() 9 | { 10 | local in_bios=0 11 | local oldifs="$IFS" 12 | IFS=" 13 | " 14 | $DMIDECODE | while read line ; do 15 | if (( $in_bios )) ; then 16 | if echo "$line" | grep -q '^[[:space:]]\+Version: ' ; then 17 | version=$(echo "$line" | sed -e 's/^\s\+Version: \(.*\)/\1/g') 18 | echo -n "$version " 19 | elif echo "$line" | grep -q '^[[:space:]]\+Release Date: ' ; then 20 | release=$(echo "$line" | sed -e 's/^\s\+Release Date: \(.*\)/\1/g') 21 | echo "$release" 22 | elif echo "$line" | grep -q -v '^[[:space:]]' ; then 23 | break 24 | fi 25 | else 26 | if echo "$line" | grep -q "^BIOS Information" ; then 27 | in_bios=1 28 | fi 29 | fi 30 | done 31 | IFS="$oldifs" 32 | } 33 | 34 | function firmware_ppc() 35 | { 36 | if [ -f /proc/device-tree/openprom/model ] ; then 37 | strings /proc/device-tree/openprom/model 38 | fi 39 | } 40 | 41 | case $(uname -i) in 42 | ppc|ppc64) 43 | firmware_ppc 44 | ;; 45 | *) 46 | firmware_default 47 | ;; 48 | esac 49 | 50 | # vim: set sw=4 ts=4 et: :collapseFolds=1: 51 | -------------------------------------------------------------------------------- /orthos2/meta/data/scripts/machine_sync_motd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This scripts syncs the motd of the current installation with 4 | # all motds. 5 | # 6 | 7 | if [ ! -f "/etc/motd.orthos" ] ; then 8 | exit 1 9 | fi 10 | 11 | function replace_motd() 12 | { 13 | local motd=$1 14 | 15 | repl=0 16 | in_orthos=0 17 | while read line ; do 18 | if [ "$in_orthos" -eq 0 ] ; then 19 | if echo "$line" | grep '^-\+ Orthos{ --$' &>/dev/null ; then 20 | cat /etc/motd.orthos >> ${motd}.new 21 | in_orthos=1 22 | repl=1 23 | else 24 | echo "$line" >> ${motd}.new 25 | fi 26 | else 27 | if echo "$line" | grep '^-\+ Orthos} --$' &>/dev/null ; then 28 | in_orthos=0 29 | fi 30 | fi 31 | done < ${motd} 32 | 33 | if [ "$repl" -eq 0 ] ; then 34 | cat /etc/motd.orthos >> ${motd}.new 35 | fi 36 | 37 | mv ${motd}.new ${motd} 38 | } 39 | 40 | 41 | replace_motd /etc/motd 42 | 43 | DISKS=$(cat /proc/partitions | awk '{print $4}' | \ 44 | grep -v name | grep -v '^$' | grep -v loop | grep '[0-9]') 45 | ROOT=$(mount | grep ' / ' | cut -d ' ' -f 1) 46 | for partition in ${DISKS} ; do 47 | partition=/dev/${partition} 48 | # Only try filesystems which had been default for the 49 | # corresponding partitions 50 | if [ ! -b ${partition} ] || [ ${partition} = ${ROOT} ] ; then 51 | continue; 52 | fi 53 | FS="" 54 | case "${partition}" in 55 | *5) 56 | FS="ext3" 57 | ;; 58 | *6) 59 | FS="ext3" 60 | ;; 61 | *7) 62 | FS="ext4 ext3 btrfs" 63 | ;; 64 | *8) 65 | FS="ext3" 66 | ;; 67 | *) 68 | continue 69 | ;; 70 | esac 71 | for fs in $FS;do 72 | mount -t "$fs" ${partition} /mnt &> /dev/null 73 | if [ $? -ne 0 ];then 74 | continue; 75 | fi 76 | if [ -f /etc/SuSE-release ] && [ -f /mnt/etc/motd ] ; then 77 | replace_motd /mnt/etc/motd 78 | fi 79 | umount /mnt &> /dev/null 80 | done 81 | done 82 | 83 | # vim: set sw=4 ts=4 et: :collapseFolds=1: 84 | -------------------------------------------------------------------------------- /orthos2/meta/data/service/orthos2.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Orthos server 3 | Requires=nginx.service 4 | Wants=logrotate.service 5 | 6 | StartLimitIntervalSec=500 7 | StartLimitBurst=2 8 | 9 | [Service] 10 | User=orthos 11 | Group=orthos 12 | ExecStart=/usr/bin/gunicorn orthos2.wsgi:application 13 | 14 | Restart=on-failure 15 | RestartSec=5s 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /orthos2/meta/data/service/orthos2_taskmanager.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Orthos taskmanager 3 | Requires=nginx.service 4 | Wants=logrotate.service 5 | 6 | StartLimitIntervalSec=500 7 | StartLimitBurst=5 8 | 9 | [Service] 10 | User=orthos 11 | Group=orthos 12 | Environment=DJANGO_SETTINGS_MODULE=orthos2.settings 13 | ExecStart=/usr/bin/django-admin taskmanager --start 14 | Restart=on-failure 15 | RestartSec=5s 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /orthos2/meta/data/tmpfiles.d/orthos2.conf: -------------------------------------------------------------------------------- 1 | d /run/orthos2 0755 orthos orthos - 2 | d /run/orthos2/ansible 0755 orthos orthos - 3 | d /run/orthos2/ansible_lastrun 0755 orthos orthos - 4 | d /run/orthos2/ansible_archive 0755 orthos orthos - 5 | d /run/orthos2/tmp 0755 orthos orthos - 6 | -------------------------------------------------------------------------------- /orthos2/meta/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/meta/management/__init__.py -------------------------------------------------------------------------------- /orthos2/meta/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/meta/management/commands/__init__.py -------------------------------------------------------------------------------- /orthos2/taskmanager/__init__.py: -------------------------------------------------------------------------------- 1 | class Priority: 2 | HIGH = 0 3 | NORMAL = 10 4 | 5 | CHOICES = ((HIGH, "high"), (NORMAL, "normal")) 6 | 7 | 8 | class TaskType: 9 | SINGLE = 0 10 | DAILY = 1 11 | 12 | CHOICES = ((SINGLE, "single"), (DAILY, "daily")) 13 | -------------------------------------------------------------------------------- /orthos2/taskmanager/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class TaskManagerConfig(AppConfig): 5 | name = "orthos2.taskmanager" 6 | -------------------------------------------------------------------------------- /orthos2/taskmanager/fixtures/dailytasks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "orthos2.taskmanager.dailytask", 4 | "pk": null, 5 | "fields": { 6 | "name": "DailyMachineChecks", 7 | "module": "taskmanager.tasks.daily", 8 | "arguments": "[[], {}]", 9 | "hash": "abae11b32e42a544bd321d4091de25a8bd90b953", 10 | "priority": 10, 11 | "running": false, 12 | "enabled": false, 13 | "updated": "2016-01-01T10:00:00+00:00", 14 | "created": "2016-01-01T10:00:00+00:00", 15 | "executed_at": "2016-01-01T10:00:00+00:00" 16 | } 17 | }, 18 | { 19 | "model": "orthos2.taskmanager.dailytask", 20 | "pk": null, 21 | "fields": { 22 | "name": "DailyCheckReservationExpirations", 23 | "module": "taskmanager.tasks.daily", 24 | "arguments": "[[], {}]", 25 | "hash": "bd27c312e62e38bb64618a21d368dc758195803e", 26 | "priority": 10, 27 | "running": false, 28 | "enabled": false, 29 | "updated": "2016-01-01T10:00:00+00:00", 30 | "created": "2016-01-01T10:00:00+00:00", 31 | "executed_at": "2016-01-01T10:00:00+00:00" 32 | } 33 | }, 34 | { 35 | "model": "taskmanager.dailytask", 36 | "pk": null, 37 | "fields": { 38 | "name": "DailyCheckForPrimaryNetwork", 39 | "module": "orthos2.taskmanager.tasks.daily", 40 | "arguments": "[[], {}]", 41 | "hash": "6e91ec1bc8ee9a0022d79a6fd9e85c79ce6a6521", 42 | "priority": 10, 43 | "running": false, 44 | "updated": "2021-06-23T22:11:14.523Z", 45 | "created": "2021-06-21T12:25:40.031Z", 46 | "executed_at": "2021-06-23T22:01:10.967Z", 47 | "enabled": true 48 | } 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /orthos2/taskmanager/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/taskmanager/management/__init__.py -------------------------------------------------------------------------------- /orthos2/taskmanager/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/taskmanager/management/commands/__init__.py -------------------------------------------------------------------------------- /orthos2/taskmanager/management/commands/taskmanager.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import signal 3 | from typing import Any 4 | 5 | from django.core.management.base import BaseCommand, CommandParser 6 | 7 | from orthos2.taskmanager.executer import TaskExecuter 8 | 9 | logger = logging.getLogger("tasks") 10 | taskexecuter = TaskExecuter() 11 | 12 | 13 | def handler(signal, frame): 14 | taskexecuter.finish() 15 | 16 | 17 | class Command(BaseCommand): 18 | help = "Run Orthos TaskManager" 19 | 20 | OPTIONS = ( 21 | (("--start",), {"action": "store_true", "help": "Start Orthos TaskManager"}), 22 | ) 23 | 24 | def add_arguments(self, parser: CommandParser) -> None: 25 | for (args, kwargs) in self.OPTIONS: 26 | parser.add_argument(*args, **kwargs) # type: ignore 27 | 28 | def __init__(self, *args: Any, **kwargs: Any) -> None: 29 | super(Command, self).__init__(*args, **kwargs) 30 | 31 | def handle(self, *args: Any, **options: Any) -> None: 32 | if options["start"]: 33 | signal.signal(signal.SIGTERM, handler) 34 | signal.signal(signal.SIGUSR1, handler) 35 | 36 | logger.info("Start TaskManager...") 37 | try: 38 | taskexecuter.start() 39 | signal.pause() 40 | except KeyboardInterrupt: 41 | pass 42 | 43 | logger.info("Stop TaskManager...") 44 | taskexecuter.finish() 45 | taskexecuter.join() 46 | logger.info("TaskManager stopped; Exit") 47 | -------------------------------------------------------------------------------- /orthos2/taskmanager/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/taskmanager/migrations/__init__.py -------------------------------------------------------------------------------- /orthos2/taskmanager/tasks/__init__.py: -------------------------------------------------------------------------------- 1 | from .cobbler import RegenerateCobbler, SyncCobblerDHCP, UpdateCobblerMachine 2 | from .daily import ( 3 | DailyCheckForPrimaryNetwork, 4 | DailyCheckReservationExpirations, 5 | DailyMachineChecks, 6 | ) 7 | from .machinetasks import MachineCheck, RegenerateMOTD 8 | from .notifications import ( 9 | CheckForPrimaryNetwork, 10 | CheckMultipleAccounts, 11 | CheckReservationExpiration, 12 | SendReservationInformation, 13 | SendRestoredPassword, 14 | ) 15 | from .sconsole import RegenerateSerialConsole 16 | from .setup import SetupMachine 17 | -------------------------------------------------------------------------------- /orthos2/taskmanager/tests.py: -------------------------------------------------------------------------------- 1 | # Create your tests here. 2 | -------------------------------------------------------------------------------- /orthos2/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | orthos2 URL Configuration. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/1.10/topics/http/urls/ 6 | 7 | Examples: 8 | Function views 9 | 1. Add an import: from my_app import views 10 | 2. Add a URL to urlpatterns: re_path(r'^$', views.home, name='home') 11 | Class-based views 12 | 1. Add an import: from other_app.views import Home 13 | 2. Add a URL to urlpatterns: re_path(r'^$', Home.as_view(), name='home') 14 | Including another URLconf 15 | 1. Import the include() function: from django.urls import url, include 16 | 2. Add a URL to urlpatterns: re_path(r'^blog/', include('blog.urls')) 17 | """ 18 | from django.contrib import admin 19 | from django.urls import include, re_path 20 | 21 | urlpatterns = [ 22 | re_path(r"^", include("orthos2.frontend.urls", namespace="frontend")), 23 | re_path(r"^admin/", admin.site.urls), 24 | re_path(r"^api/", include("orthos2.api.urls", namespace="api")), 25 | ] 26 | -------------------------------------------------------------------------------- /orthos2/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/utils/__init__.py -------------------------------------------------------------------------------- /orthos2/utils/admin.py: -------------------------------------------------------------------------------- 1 | # Register your models here. 2 | -------------------------------------------------------------------------------- /orthos2/utils/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UtilsConfig(AppConfig): 5 | name = "orthos2.utils" 6 | -------------------------------------------------------------------------------- /orthos2/utils/remotepowertype.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Dict, List, Optional, Tuple, Union 3 | 4 | from django.conf import settings 5 | 6 | 7 | def get_remote_power_type_choices( 8 | device: str = "", 9 | ) -> List[Tuple[Union[bool, List[str], str], Union[bool, List[str], str]]]: 10 | if device: 11 | remotepower_type_choices = [ 12 | (fence["fence"], fence["fence"]) # type: ignore 13 | for fence in settings.REMOTEPOWER_TYPES 14 | if fence["device"].lower() == device.lower() # type: ignore 15 | ] 16 | else: 17 | remotepower_type_choices = [ 18 | (fence["fence"], fence["fence"]) for fence in settings.REMOTEPOWER_TYPES # type: ignore 19 | ] 20 | return remotepower_type_choices 21 | 22 | 23 | class RemotePowerType: 24 | @classmethod 25 | def from_fence(cls, fence: str) -> Optional["RemotePowerType"]: 26 | if not fence: 27 | raise ValueError("Empty Argument") 28 | objs = [cls(x) for x in settings.REMOTEPOWER_TYPES if x["fence"] == fence] # type: ignore 29 | if not objs: 30 | logging.error("no fence agent named %s found", fence) 31 | return None 32 | return objs[0] 33 | 34 | def __init__(self, options: Dict[str, str]) -> None: 35 | self.fence = options.get("fence") 36 | logging.debug("Initialiced RemotePowerType for %s", self.fence) 37 | self.device = options.get("device") 38 | self.username = options.get("username") 39 | if "password" in options: 40 | self.password = options["password"] 41 | self.use_password = True 42 | else: 43 | self.use_password = False 44 | if "identity_file" in options: 45 | self.identity_file = options["identity_file"] 46 | self.use_identity_file = True 47 | else: 48 | self.use_identity_file = False 49 | self.use_port = options.get("port", False) 50 | self.use_hostname_as_port = options.get("use_hostname_as_port", False) 51 | self.use_options = options.get("options", False) 52 | -------------------------------------------------------------------------------- /orthos2/utils/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openSUSE/orthos2/46f9c0006b437154b94c07e33e1331d5531ec7ff/orthos2/utils/tests/__init__.py -------------------------------------------------------------------------------- /orthos2/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for orthos2 project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | import pwd 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | 15 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orthos2.settings") 16 | os.environ.setdefault("HOME", pwd.getpwuid(os.getuid()).pw_dir) 17 | 18 | application = get_wsgi_application() 19 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.isort] 2 | profile = "black" 3 | known_cobbler="orthos2" 4 | known_tests="tests" 5 | sections="FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER,COBBLER,TESTS" 6 | 7 | [tool.pyright] 8 | pythonVersion = "3.11" 9 | typeCheckingMode = "strict" 10 | useLibraryCodeForTypes = true 11 | exclude = ["build", "venv", ".venv", "orthos2/data/migrations"] 12 | -------------------------------------------------------------------------------- /requirements-devel.txt: -------------------------------------------------------------------------------- 1 | # Requirements for development 2 | # pip install -r requirements-devel.txt 3 | # 4 | 5 | -r requirements.txt 6 | 7 | flake8 8 | coverage 9 | isort==5.12.0 # See .pre-commit-config.yaml 10 | pytest 11 | mock 12 | django-webtest 13 | djangorestframework-stubs[compatible-mypy] 14 | django-stubs[compatible-mypy] 15 | types-netaddr 16 | types-pytz 17 | types-pexpect 18 | types-paramiko 19 | pexpect 20 | black==22.3.0 # See .pre-commit-config.yaml 21 | 22 | # generate UML diagram: 23 | # pygraphviz 24 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements 2 | # pip install -r requirements.txt 3 | # 4 | 5 | django~=4.2.9 6 | django-extensions 7 | paramiko 8 | djangorestframework 9 | validators 10 | netaddr 11 | django-auth-ldap 12 | terminado 13 | tornado 14 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import find_packages, setup 4 | 5 | 6 | def requires(filename: str = "requirements.txt"): 7 | """Returns a list of all pip requirements 8 | 9 | :param filename: the Pip requirement file 10 | (usually 'requirements.txt') 11 | :return: list of modules 12 | :rtype: list 13 | """ 14 | with open(filename, "r+t") as pipreq: 15 | for line in pipreq: 16 | line = line.strip() 17 | if not line or line.startswith("#") or line.startswith("-r"): 18 | continue 19 | yield line 20 | 21 | 22 | if __name__ == "__main__": 23 | setup( 24 | name="orthos2", 25 | version="0.1", 26 | description="Machine administration server", 27 | long_description=""" 28 | Orthos is the machine administration tool of the development network at SUSE. 29 | It is used for following tasks: 30 | 31 | getting the state of the machine 32 | overview about the hardware 33 | overview about the installed software (installations) 34 | reservation of the machines 35 | generating the DHCP configuration (via Cobbler) 36 | reboot the machines remotely 37 | managing remote (serial) consoles""", 38 | author="orthos team", 39 | url="https://github.com/openSUSE/orthos2", 40 | license="GPLv2+", 41 | setup_requires=[], 42 | install_requires=list(requires()), 43 | packages=find_packages(exclude=["*tests*"]), 44 | include_package_data=True, 45 | scripts=[ 46 | "cli/orthos2", 47 | ], 48 | ) 49 | -------------------------------------------------------------------------------- /system-user-orthos.conf: -------------------------------------------------------------------------------- 1 | #Type Name ID GECOS Home directory Shell 2 | g orthos - 3 | u orthos - "Orthos Machine Administration" /var/lib/orthos2 - 4 | m orthos orthos 5 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [pycodestyle] 2 | max-line-length = 120 3 | # Make pycodestyle & black interoperable 4 | # https://github.com/psf/black/issues/354#issuecomment-397685631 5 | ignore = E203,W503 6 | statistics = True 7 | exclude = 8 | orthos2/data/migrations/*, 9 | orthos2/taskmanager/migrations/* 10 | 11 | [flake8] 12 | max-line-length = 100 13 | exclude = 14 | orthos2/data/migrations/*, 15 | orthos2/taskmanager/migrations/*, 16 | --------------------------------------------------------------------------------