Compare commits

9 Commits
16.0 ... 14.0

Author SHA1 Message Date
clementthomas
ef3e32071d [IMP] maintenancer_server_monitoring :
* simplify log management in launch_test function
* set ping True when ping very slow
* bugfix on ssh
2024-04-12 09:13:32 +02:00
clementthomas
fa21e7a2ac [IMP] maintenance_server_monitoring:
add views in submodules
2024-04-09 16:05:27 +02:00
clementthomas
648e5f038a [NEW] refactoring of maintenance_server_monitoring in several modules 2024-04-09 15:51:10 +02:00
clementthomas
661a5b597a [IMP] maintenance_server_monitoring_maintenance_equipment_status:
* bugfix
2024-04-09 11:24:52 +02:00
clementthomas
3df4e83711 [IMP] maintenance_server_monitoring:
* bugfix
2024-04-09 11:21:16 +02:00
clementthomas
b8172d5f96 [NEW] maintenance_server_monitoring_equipment_status 2024-04-09 11:14:25 +02:00
clementthomas
ce57a34fe2 [IMP] maintenance_server_monitoring :
create request only if monitoring enabled
2024-04-09 10:07:02 +02:00
clementthomas
784895c416 [NEW] maintenance_server_ssh 2024-04-05 14:06:54 +02:00
clementthomas
d95b95e56c [NEW] maintenance_server_monitoring 2024-04-05 14:06:37 +02:00
60 changed files with 830 additions and 1520 deletions

View File

@@ -1,20 +0,0 @@
# Configuration for known file extensions
[*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.{json,yml,yaml,rst,md}]
indent_size = 2
# Do not configure editor for libs and autogenerated content
[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}]
charset = unset
end_of_line = unset
indent_size = unset
indent_style = unset
insert_final_newline = false
trim_trailing_whitespace = false

View File

@@ -1,188 +0,0 @@
env:
browser: true
es6: true
# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449
parserOptions:
ecmaVersion: 2019
overrides:
- files:
- "**/*.esm.js"
parserOptions:
sourceType: module
# Globals available in Odoo that shouldn't produce errorings
globals:
_: readonly
$: readonly
fuzzy: readonly
jQuery: readonly
moment: readonly
odoo: readonly
openerp: readonly
owl: readonly
luxon: readonly
# Styling is handled by Prettier, so we only need to enable AST rules;
# see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890
rules:
accessor-pairs: warn
array-callback-return: warn
callback-return: warn
capitalized-comments:
- warn
- always
- ignoreConsecutiveComments: true
ignoreInlineComments: true
complexity:
- warn
- 15
constructor-super: warn
dot-notation: warn
eqeqeq: warn
global-require: warn
handle-callback-err: warn
id-blacklist: warn
id-match: warn
init-declarations: error
max-depth: warn
max-nested-callbacks: warn
max-statements-per-line: warn
no-alert: warn
no-array-constructor: warn
no-caller: warn
no-case-declarations: warn
no-class-assign: warn
no-cond-assign: error
no-const-assign: error
no-constant-condition: warn
no-control-regex: warn
no-debugger: error
no-delete-var: warn
no-div-regex: warn
no-dupe-args: error
no-dupe-class-members: error
no-dupe-keys: error
no-duplicate-case: error
no-duplicate-imports: error
no-else-return: warn
no-empty-character-class: warn
no-empty-function: error
no-empty-pattern: error
no-empty: warn
no-eq-null: error
no-eval: error
no-ex-assign: error
no-extend-native: warn
no-extra-bind: warn
no-extra-boolean-cast: warn
no-extra-label: warn
no-fallthrough: warn
no-func-assign: error
no-global-assign: error
no-implicit-coercion:
- warn
- allow: ["~"]
no-implicit-globals: warn
no-implied-eval: warn
no-inline-comments: warn
no-inner-declarations: warn
no-invalid-regexp: warn
no-irregular-whitespace: warn
no-iterator: warn
no-label-var: warn
no-labels: warn
no-lone-blocks: warn
no-lonely-if: error
no-mixed-requires: error
no-multi-str: warn
no-native-reassign: error
no-negated-condition: warn
no-negated-in-lhs: error
no-new-func: warn
no-new-object: warn
no-new-require: warn
no-new-symbol: warn
no-new-wrappers: warn
no-new: warn
no-obj-calls: warn
no-octal-escape: warn
no-octal: warn
no-param-reassign: warn
no-path-concat: warn
no-process-env: warn
no-process-exit: warn
no-proto: warn
no-prototype-builtins: warn
no-redeclare: warn
no-regex-spaces: warn
no-restricted-globals: warn
no-restricted-imports: warn
no-restricted-modules: warn
no-restricted-syntax: warn
no-return-assign: error
no-script-url: warn
no-self-assign: warn
no-self-compare: warn
no-sequences: warn
no-shadow-restricted-names: warn
no-shadow: warn
no-sparse-arrays: warn
no-sync: warn
no-this-before-super: warn
no-throw-literal: warn
no-undef-init: warn
no-undef: error
no-unmodified-loop-condition: warn
no-unneeded-ternary: error
no-unreachable: error
no-unsafe-finally: error
no-unused-expressions: error
no-unused-labels: error
no-unused-vars: error
no-use-before-define: error
no-useless-call: warn
no-useless-computed-key: warn
no-useless-concat: warn
no-useless-constructor: warn
no-useless-escape: warn
no-useless-rename: warn
no-void: warn
no-with: warn
operator-assignment: [error, always]
prefer-const: warn
radix: warn
require-yield: warn
sort-imports: warn
spaced-comment: [error, always]
strict: [error, function]
use-isnan: error
valid-jsdoc:
- warn
- prefer:
arg: param
argument: param
augments: extends
constructor: class
exception: throws
func: function
method: function
prop: property
return: returns
virtual: abstract
yield: yields
preferType:
array: Array
bool: Boolean
boolean: Boolean
number: Number
object: Object
str: String
string: String
requireParamDescription: false
requireReturn: false
requireReturnDescription: false
requireReturnType: false
valid-typeof: warn
yoda: warn

View File

@@ -1,42 +0,0 @@
name: pre-commit
on:
pull_request:
branches:
- "16.0*"
jobs:
pre-commit:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Get python version
run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV
# - uses: actions/cache@v4
# with:
# path: ~/.cache/pre-commit
# key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: Install pre-commit
run: pip install pre-commit
- name: Run pre-commit
run: pre-commit run --all-files --show-diff-on-failure --color=always
env:
# Consider valid a PR that changes README fragments but doesn't
# change the README.rst file itself. It's not really a problem
# because the bot will update it anyway after merge. This way, we
# lower the barrier for functional contributors that want to fix the
# readme fragments, while still letting developers get README
# auto-generated (which also helps functionals when using runboat).
# DOCS https://pre-commit.com/#temporarily-disabling-hooks
SKIP: oca-gen-addon-readme
- name: Check that all files generated by pre-commit are in git
run: |
newfiles="$(git ls-files --others --exclude-from=.gitignore)"
if [ "$newfiles" != "" ] ; then
echo "Please check-in the following files:"
echo "$newfiles"
exit 1
fi

View File

@@ -1,150 +0,0 @@
exclude: |
(?x)
# NOT INSTALLABLE ADDONS
# END NOT INSTALLABLE ADDONS
# Files and folders generated by bots, to avoid loops
^setup/|/static/description/index\.html$|
# We don't want to mess with tool-generated files
.svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|^eslint.config.cjs|^prettier.config.cjs|
# Maybe reactivate this when all README files include prettier ignore tags?
^README\.md$|
# Library files can have extraneous formatting (even minimized)
/static/(src/)?lib/|
# Repos using Sphinx to generate docs don't need prettying
^docs/_templates/.*\.html$|
# Don't bother non-technical authors with formatting issues in docs
readme/.*\.(rst|md)$|
# Ignore build and dist directories in addons
/build/|/dist/|
# Ignore test files in addons
/tests/samples/.*|
# You don't usually want a bot to modify your legal texts
(LICENSE.*|COPYING.*)
default_language_version:
python: python3
node: "16.17.0"
repos:
- repo: local
hooks:
# These files are most likely copier diff rejection junks; if found,
# review them manually, fix the problem (if needed) and remove them
- id: forbidden-files
name: forbidden files
entry: found forbidden files; remove them
language: fail
files: "\\.rej$"
- id: en-po-files
name: en.po files cannot exist
entry: found a en.po file
language: fail
files: '[a-zA-Z0-9_]*/i18n/en\.po$'
- repo: https://github.com/oca/maintainer-tools
rev: f9b919b9868143135a9c9cb03021089cabba8223
hooks:
# update the NOT INSTALLABLE ADDONS section above
- id: oca-update-pre-commit-excluded-addons
- id: oca-fix-manifest-website
entry:
bash -c 'oca-fix-manifest-website "https://git.elabore.coop/elabore/$(basename
$(git rev-parse --show-toplevel))"'
- id: oca-gen-addon-readme
entry:
bash -c 'oca-gen-addon-readme
--addons-dir=.
--branch=$(git symbolic-ref
refs/remotes/origin/HEAD | sed "s@^refs/remotes/origin/@@")
--repo-name=$(basename $(git rev-parse --show-toplevel))
--org-name="Elabore"
--if-source-changed --keep-source-digest'
- repo: https://github.com/OCA/odoo-pre-commit-hooks
rev: v0.1.4
hooks:
- id: oca-checks-odoo-module
- id: oca-checks-po
args:
- --disable=po-pretty-format
- repo: local
hooks:
- id: prettier
name: prettier (with plugin-xml)
entry: prettier
args:
- --write
- --list-different
- --ignore-unknown
types: [text]
files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$
language: node
additional_dependencies:
- "prettier@2.7.1"
- "@prettier/plugin-xml@2.2.0"
- repo: local
hooks:
- id: eslint
name: eslint
entry: eslint
args:
- --color
- --fix
verbose: true
types: [javascript]
language: node
additional_dependencies:
- "eslint@8.24.0"
- "eslint-plugin-jsdoc@"
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
# exclude autogenerated files
exclude: /README\.rst$|\.pot?$
- id: end-of-file-fixer
# exclude autogenerated files
exclude: /README\.rst$|\.pot?$
- id: debug-statements
- id: fix-encoding-pragma
args: ["--remove"]
- id: check-case-conflict
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-merge-conflict
# exclude files where underlines are not distinguishable from merge conflicts
exclude: /README\.rst$|^docs/.*\.rst$
- id: check-symlinks
- id: check-xml
- id: mixed-line-ending
args: ["--fix=lf"]
- repo: https://github.com/PyCQA/docformatter
rev: v1.7.7
hooks:
- id: docformatter
args: [
"--in-place", # modify the files
"--recursive", # run on all the files
"--wrap-summaries",
"88", # max length of 1st line
"--wrap-descriptions",
"88", # max length of other lines
"--pre-summary-newline", # new line before a long summary
"--make-summary-multi-line", # force summary on multilines
]
additional_dependencies: ["tomli"] # if Python <3.11
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.0
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/OCA/pylint-odoo
rev: v9.1.3
hooks:
- id: pylint_odoo
name: pylint with optional checks
args:
- --rcfile=.pylintrc
- --exit-zero
verbose: true
- id: pylint_odoo
args:
- --rcfile=.pylintrc-mandatory

View File

@@ -1,8 +0,0 @@
# Defaults for all prettier-supported languages.
# Prettier will complete this with settings from .editorconfig file.
bracketSpacing: false
printWidth: 88
proseWrap: always
semi: true
trailingComma: "es5"
xmlWhitespaceSensitivity: "strict"

123
.pylintrc
View File

@@ -1,123 +0,0 @@
[MASTER]
load-plugins=pylint_odoo
score=n
[ODOOLINT]
readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
manifest-required-authors=Elabore
manifest-required-keys=license
manifest-deprecated-keys=description,active
license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
valid-odoo-versions=16.0
[MESSAGES CONTROL]
disable=all
# This .pylintrc contains optional AND mandatory checks and is meant to be
# loaded in an IDE to have it check everything, in the hope this will make
# optional checks more visible to contributors who otherwise never look at a
# green travis to see optional checks that failed.
# .pylintrc-mandatory containing only mandatory checks is used the pre-commit
# config as a blocking check.
enable=anomalous-backslash-in-string,
api-one-deprecated,
api-one-multi-together,
assignment-from-none,
attribute-deprecated,
class-camelcase,
dangerous-default-value,
dangerous-view-replace-wo-priority,
development-status-allowed,
duplicate-id-csv,
duplicate-key,
duplicate-xml-fields,
duplicate-xml-record-id,
eval-referenced,
eval-used,
incoherent-interpreter-exec-perm,
license-allowed,
manifest-author-string,
manifest-deprecated-key,
manifest-required-author,
manifest-required-key,
manifest-version-format,
method-compute,
method-inverse,
method-required-super,
method-search,
openerp-exception-warning,
pointless-statement,
pointless-string-statement,
print-used,
redundant-keyword-arg,
redundant-modulename-xml,
reimported,
relative-import,
return-in-init,
rst-syntax-error,
sql-injection,
too-few-format-args,
translation-field,
translation-required,
unreachable,
use-vim-comment,
wrong-tabs-instead-of-spaces,
xml-syntax-error,
attribute-string-redundant,
character-not-valid-in-resource-link,
consider-merging-classes-inherited,
context-overridden,
create-user-wo-reset-password,
dangerous-filter-wo-user,
dangerous-qweb-replace-wo-priority,
deprecated-data-xml-node,
deprecated-openerp-xml-node,
duplicate-po-message-definition,
except-pass,
file-not-used,
invalid-commit,
manifest-maintainers-list,
missing-newline-extrafiles,
missing-readme,
missing-return,
odoo-addons-relative-import,
old-api7-method-defined,
po-msgstr-variables,
po-syntax-error,
renamed-field-parameter,
resource-not-exist,
str-format-used,
test-folder-imported,
translation-contains-variable,
translation-positional-used,
unnecessary-utf8-coding-comment,
website-manifest-key-not-valid-uri,
xml-attribute-translatable,
xml-deprecated-qweb-directive,
xml-deprecated-tree-attribute,
external-request-timeout,
# messages that do not cause the lint step to fail
consider-merging-classes-inherited,
create-user-wo-reset-password,
dangerous-filter-wo-user,
deprecated-module,
file-not-used,
invalid-commit,
missing-manifest-dependency,
missing-newline-extrafiles,
missing-readme,
no-utf8-coding-comment,
odoo-addons-relative-import,
old-api7-method-defined,
redefined-builtin,
too-complex,
unnecessary-utf8-coding-comment
[REPORTS]
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
output-format=colorized
reports=no

View File

@@ -1,98 +0,0 @@
[MASTER]
load-plugins=pylint_odoo
score=n
[ODOOLINT]
readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
manifest-required-authors=Elabore
manifest-required-keys=license
manifest-deprecated-keys=description,active
license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
valid-odoo-versions=16.0
[MESSAGES CONTROL]
disable=all
enable=anomalous-backslash-in-string,
api-one-deprecated,
api-one-multi-together,
assignment-from-none,
attribute-deprecated,
class-camelcase,
dangerous-default-value,
dangerous-view-replace-wo-priority,
development-status-allowed,
duplicate-id-csv,
duplicate-key,
duplicate-xml-fields,
duplicate-xml-record-id,
eval-referenced,
eval-used,
incoherent-interpreter-exec-perm,
license-allowed,
manifest-author-string,
manifest-deprecated-key,
manifest-required-author,
manifest-required-key,
manifest-version-format,
method-compute,
method-inverse,
method-required-super,
method-search,
openerp-exception-warning,
pointless-statement,
pointless-string-statement,
print-used,
redundant-keyword-arg,
redundant-modulename-xml,
reimported,
relative-import,
return-in-init,
rst-syntax-error,
sql-injection,
too-few-format-args,
translation-field,
translation-required,
unreachable,
use-vim-comment,
wrong-tabs-instead-of-spaces,
xml-syntax-error,
attribute-string-redundant,
character-not-valid-in-resource-link,
consider-merging-classes-inherited,
context-overridden,
create-user-wo-reset-password,
dangerous-filter-wo-user,
dangerous-qweb-replace-wo-priority,
deprecated-data-xml-node,
deprecated-openerp-xml-node,
duplicate-po-message-definition,
except-pass,
file-not-used,
invalid-commit,
manifest-maintainers-list,
missing-newline-extrafiles,
missing-readme,
missing-return,
odoo-addons-relative-import,
old-api7-method-defined,
po-msgstr-variables,
po-syntax-error,
renamed-field-parameter,
resource-not-exist,
str-format-used,
test-folder-imported,
translation-contains-variable,
translation-positional-used,
unnecessary-utf8-coding-comment,
website-manifest-key-not-valid-uri,
xml-attribute-translatable,
xml-deprecated-qweb-directive,
xml-deprecated-tree-attribute,
external-request-timeout
[REPORTS]
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
output-format=colorized
reports=no

View File

@@ -1,31 +0,0 @@
target-version = "py310"
fix = true
[lint]
extend-select = [
"B",
"C90",
"E501", # line too long (default 88)
"I", # isort
"UP", # pyupgrade
]
extend-safe-fixes = ["UP008"]
exclude = ["setup/*"]
[format]
exclude = ["setup/*"]
[lint.per-file-ignores]
"__init__.py" = ["F401", "I001"] # ignore unused and unsorted imports in __init__.py
"__manifest__.py" = ["B018"] # useless expression
[lint.isort]
section-order = ["future", "standard-library", "third-party", "odoo", "odoo-addons", "first-party", "local-folder"]
[lint.isort.sections]
"odoo" = ["odoo"]
"odoo-addons" = ["odoo.addons"]
[lint.mccabe]
max-complexity = 16

View File

@@ -1,182 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * maintenance_create_requests_from_project_task
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-06-02 22:52+0000\n"
"PO-Revision-Date: 2025-06-03 00:58+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: maintenance_create_requests_from_project_task
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.create_maintenance_requests_wizard_view_form
msgid "Cancel"
msgstr "Annuler"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model,name:maintenance_create_requests_from_project_task.model_create_maintenance_requests_wizard
msgid "Configure the maintenance requests to create from the current task."
msgstr "Définir les données des demandes de maintenance à créer depuis la tâche actuelle."
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__maintenance_type__corrective
msgid "Corrective"
msgstr "Corrective"
#. module: maintenance_create_requests_from_project_task
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.create_maintenance_requests_wizard_view_form
msgid "Create"
msgstr "Créer"
#. module: maintenance_create_requests_from_project_task
#: model:ir.actions.act_window,name:maintenance_create_requests_from_project_task.action_create_maintenance_requests_wizard
msgid "Create Maintenance Requests"
msgstr "Créer des demandes de maintenance"
#. module: maintenance_create_requests_from_project_task
#. odoo-python
#: code:addons/maintenance_create_requests_from_project_task/wizard/create_maintenance_requests_wizard.py:0
#: model:ir.actions.server,name:maintenance_create_requests_from_project_task.task_wizard_action_create_maintenance_requests
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.create_maintenance_requests_wizard_view_form
#, python-format
msgid "Create maintenance requests"
msgstr "Créer des demandes de maintenance"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__create_uid
msgid "Created by"
msgstr "Créé par"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__create_date
msgid "Created on"
msgstr "Créé le"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__description
msgid "Description"
msgstr "Description"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__display_name
msgid "Display Name"
msgstr "Nom affiché"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__duration
msgid "Duration"
msgstr "Durée"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,help:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__duration
msgid "Duration in hours."
msgstr "Durée en heures."
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__equipment_domain
msgid "Equipment Domain"
msgstr "Domaine des équipements"
#. module: maintenance_create_requests_from_project_task
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.create_maintenance_requests_wizard_view_form
msgid "Equipments targetted"
msgstr "Équipements ciblés"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__priority__3
msgid "High"
msgstr "Élevé"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__id
msgid "ID"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard____last_update
msgid "Last Modified on"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__write_uid
msgid "Last Updated by"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__write_date
msgid "Last Updated on"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__priority__1
msgid "Low"
msgstr "Bas"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_project_task__maintenance_request_count
msgid "Maintenance Request Count"
msgstr "Nombre de demandes de maintenance"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_project_task__maintenance_request_ids
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.view_task_form2_maintenance_inherited
msgid "Maintenance Requests"
msgstr "Demandes de maintenance"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__maintenance_type
msgid "Maintenance Type"
msgstr "Type de maintenance"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__priority__2
msgid "Normal"
msgstr "Normal"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__maintenance_type__preventive
msgid "Preventive"
msgstr "Préventive"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__priority
msgid "Priority"
msgstr "Priorité"
#. module: maintenance_create_requests_from_project_task
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.create_maintenance_requests_wizard_view_form
msgid "Requests data"
msgstr "Données"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__schedule_date
msgid "Scheduled Date"
msgstr "Date prévue"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model,name:maintenance_create_requests_from_project_task.model_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__task_id
msgid "Task"
msgstr "Tâche"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__user_id
msgid "Technician"
msgstr "Technicien"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__name
msgid "Title"
msgstr "Nom"
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__priority__0
msgid "Very Low"
msgstr "Très bas"

View File

@@ -1,182 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * maintenance_create_requests_from_project_task
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-06-02 22:52+0000\n"
"PO-Revision-Date: 2025-06-02 22:52+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: maintenance_create_requests_from_project_task
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.create_maintenance_requests_wizard_view_form
msgid "Cancel"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model,name:maintenance_create_requests_from_project_task.model_create_maintenance_requests_wizard
msgid "Configure the maintenance requests to create from the current task."
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__maintenance_type__corrective
msgid "Corrective"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.create_maintenance_requests_wizard_view_form
msgid "Create"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.actions.act_window,name:maintenance_create_requests_from_project_task.action_create_maintenance_requests_wizard
msgid "Create Maintenance Requests"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#. odoo-python
#: code:addons/maintenance_create_requests_from_project_task/wizard/create_maintenance_requests_wizard.py:0
#: model:ir.actions.server,name:maintenance_create_requests_from_project_task.task_wizard_action_create_maintenance_requests
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.create_maintenance_requests_wizard_view_form
#, python-format
msgid "Create maintenance requests"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__create_uid
msgid "Created by"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__create_date
msgid "Created on"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__description
msgid "Description"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__display_name
msgid "Display Name"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__duration
msgid "Duration"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,help:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__duration
msgid "Duration in hours."
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__equipment_domain
msgid "Equipment Domain"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.create_maintenance_requests_wizard_view_form
msgid "Equipments targetted"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__priority__3
msgid "High"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__id
msgid "ID"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard____last_update
msgid "Last Modified on"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__write_uid
msgid "Last Updated by"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__write_date
msgid "Last Updated on"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__priority__1
msgid "Low"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_project_task__maintenance_request_count
msgid "Maintenance Request Count"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_project_task__maintenance_request_ids
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.view_task_form2_maintenance_inherited
msgid "Maintenance Requests"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__maintenance_type
msgid "Maintenance Type"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__priority__2
msgid "Normal"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__maintenance_type__preventive
msgid "Preventive"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__priority
msgid "Priority"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model_terms:ir.ui.view,arch_db:maintenance_create_requests_from_project_task.create_maintenance_requests_wizard_view_form
msgid "Requests data"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__schedule_date
msgid "Scheduled Date"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model,name:maintenance_create_requests_from_project_task.model_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__task_id
msgid "Task"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__user_id
msgid "Technician"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields,field_description:maintenance_create_requests_from_project_task.field_create_maintenance_requests_wizard__name
msgid "Title"
msgstr ""
#. module: maintenance_create_requests_from_project_task
#: model:ir.model.fields.selection,name:maintenance_create_requests_from_project_task.selection__create_maintenance_requests_wizard__priority__0
msgid "Very Low"
msgstr ""

View File

@@ -1 +0,0 @@
from . import project_task

View File

@@ -1,29 +0,0 @@
from odoo import fields, models, api
class ProjectTask(models.Model):
_inherit = "project.task"
maintenance_request_ids = fields.One2many("maintenance.request", "task_id", string="Maintenance Requests")
maintenance_request_count = fields.Integer(
compute="_compute_maintenance_request_count"
)
@api.depends("maintenance_request_ids")
def _compute_maintenance_request_count(self):
for task in self:
task.maintenance_request_count = len(
task.maintenance_request_ids.filtered(lambda x: not x.stage_id.done)
)
def action_view_maintenance_request_ids(self):
"""
Access to the undone maintenance requests for this task
"""
self.ensure_one()
action = self.env["ir.actions.actions"]._for_xml_id(
"maintenance.hr_equipment_request_action"
)
action["domain"] = [("task_id", "=", self.id), ("stage_id.done", "=", False)]
action["context"] = {"default_task_id": self.id}
return action

View File

@@ -1,2 +0,0 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_create_maintenance_requests_wizard,maintenance_create_requests_from_portal_tasks.create_maintenance_requests_wizard.access,model_create_maintenance_requests_wizard,base.group_user,1,1,1,0
1 id name model_id/id group_id/id perm_read perm_write perm_create perm_unlink
2 access_create_maintenance_requests_wizard maintenance_create_requests_from_portal_tasks.create_maintenance_requests_wizard.access model_create_maintenance_requests_wizard base.group_user 1 1 1 0

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_task_form2_maintenance_inherited" model="ir.ui.view">
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_form2" />
<field name="arch" type="xml">
<xpath expr="//field[@name='company_id']" position="after">
<field name="maintenance_request_count" invisible="1"/>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_maintenance_request_ids" type="object" attrs="{'invisible': [('maintenance_request_count', '=', 0)]}" class="oe_stat_button" icon="fa-tasks" >
<div class="o_field_widget o_stat_info">
<span class="o_stat_value ">
<field name="maintenance_request_count" widget="statinfo" nolabel="1" />
Maintenance Requests
</span>
</div>
</button>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1 +0,0 @@
from . import create_maintenance_requests_wizard

View File

@@ -1,109 +0,0 @@
from odoo import api, fields, models, _
from odoo.tools.safe_eval import safe_eval
class CreateMaintenanceRequestsWizard(models.TransientModel):
_name= "create.maintenance.requests.wizard"
_description= "Configure the maintenance requests to create from the current task."
@api.model
def _default_task_id(self):
return self.env["project.task"].browse(self._context.get("active_ids"))
@api.model
def _default_equipment_model(self):
default_domain = []
task_id = self.env["project.task"].browse(self._context.get("active_ids"))
project_equipements = self.env["maintenance.equipment"].search([("project_id", "=", task_id.project_id.id)])
if project_equipements:
equipment_ids_list = [x.id for x in project_equipements]
default_domain.append(("id", "in", equipment_ids_list))
return default_domain
name = fields.Char("Title", required=True)
user_id = fields.Many2one('res.users', string='Technician')
priority = fields.Selection([('0', 'Very Low'), ('1', 'Low'), ('2', 'Normal'), ('3', 'High')], string='Priority')
maintenance_type = fields.Selection([('corrective', 'Corrective'), ('preventive', 'Preventive')], string='Maintenance Type', default="corrective")
schedule_date = fields.Datetime('Scheduled Date')
duration = fields.Float(help="Duration in hours.")
description = fields.Html('Description')
equipment_domain = fields.Char("Equipment Domain", default=_default_equipment_model)
task_id = fields.Many2one(
"project.task",
string="Task",
required=True,
default=_default_task_id,
)
@api.model
def action_open_wizard(self):
"""
Open the form view.
"""
return {
'name': _('Create maintenance requests'),
'type': 'ir.actions.act_window',
'res_model': 'create.maintenance.requests.wizard',
'view_type': 'form',
'view_mode': 'form',
'target': 'new',
}
def create_maintenance_requests(self):
"""
Create the maintenance requests with the data filled in the wizard form.
"""
vals_list = self._compute_vals_list()
maintenance_requests = self.env["maintenance.request"].sudo().create(vals_list)
return self._get_action(maintenance_requests)
def _compute_vals_list(self):
"""
Compute the list of data to use for all the maintenance requests creation
"""
equipment_list = self.env["maintenance.equipment"].search(safe_eval(self.equipment_domain))
if len(equipment_list) == 0:
raise UserError("No equipment is matching the domain. Maintenance request creation is not possible.")
vals_list = []
common_vals = {
"name": self.name,
"user_id": self.user_id.id,
"priority": self.priority,
"maintenance_type": self.maintenance_type,
"schedule_date": self.schedule_date,
"duration": self.duration,
"description": self.description,
"task_id": self.task_id.id,
"project_id": self.task_id.project_id.id
}
for equipment in equipment_list:
vals = common_vals.copy()
vals["equipment_id"] = equipment.id
if equipment.maintenance_team_id:
vals["maintenance_team_id"] = equipment.maintenance_team_id.id
vals_list.append(vals)
return vals_list
def _get_action(self, maintenance_requests):
"""
Provide the action to go to the tree view of the maintenance requests created.
"""
search_view_ref = self.env.ref('maintenance.hr_equipment_request_view_search', False)
form_view_ref = self.env.ref('maintenance.hr_equipment_request_view_form', False)
tree_view_ref = self.env.ref('maintenance.hr_equipment_request_view_tree', False)
return {
'domain': [('id', 'in', maintenance_requests.ids)],
'name': 'Maintenance Requests',
'res_model': 'maintenance.request',
'type': 'ir.actions.act_window',
'views': [(tree_view_ref.id, 'tree'), (form_view_ref.id, 'form')],
'search_view_id': search_view_ref and [search_view_ref.id],
}

View File

@@ -1,54 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="create_maintenance_requests_wizard_view_form" model="ir.ui.view">
<field name="name">create.maintenance.requests.wizard.view.form</field>
<field name="model">create.maintenance.requests.wizard</field>
<field name="arch" type="xml">
<form string="Create maintenance requests">
<sheet>
<group name="name">
<field name="task_id" readonly="1" />
<field name="name" />
</group>
<group name="domain" string="Equipments targetted">
<field
name="equipment_domain"
widget="domain"
options='{"model": "maintenance.equipment"}'
/>
</group>
<group name="data" string="Requests data">
<field name="user_id" />
<field name="maintenance_type" />
<field name="priority" />
<field name="duration" />
<field name="schedule_date" />
<field name="description" />
</group>
</sheet>
<footer>
<button string="Create" name="create_maintenance_requests" type="object"
class="btn-primary" />
<button string="Cancel" class="btn-secondary" special="cancel" />
</footer>
</form>
</field>
</record>
<record
id="action_create_maintenance_requests_wizard" model="ir.actions.act_window">
<field name="name">Create Maintenance Requests</field>
<field name="res_model">create.maintenance.requests.wizard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="create_maintenance_requests_wizard_view_form" />
<field name="target">new</field>
</record>
<record id="task_wizard_action_create_maintenance_requests" model="ir.actions.server">
<field name="name">Create maintenance requests</field>
<field name="model_id" ref="maintenance_create_requests_from_project_task.model_create_maintenance_requests_wizard"/>
<field name="binding_model_id" ref="project.model_project_task"/>
<field name="state">code</field>
<field name="code">action = model.action_open_wizard()</field>
</record>
</odoo>

View File

@@ -3,7 +3,7 @@
{
"name": "maintenance_server_data",
"version": "16.0.1.0.0",
"version": "14.0.1.0.0",
"author": "Elabore",
"website": "https://elabore.coop",
"maintainer": "Stéphan Sainléger",

View File

@@ -1,23 +1,17 @@
from odoo import fields, models, api
from odoo import fields, models
class MaintenanceEquipment(models.Model):
_inherit = "maintenance.equipment"
_inherit = 'maintenance.equipment'
server_ip = fields.Char("Server Ip Address")
distribution_id = fields.Many2one("os.distribution", string="Distribution")
service_ids = fields.One2many("service.instance", "equipment_id", string="Services")
hosting_city = fields.Char("Hosting City")
nb_cores = fields.Integer("Nb Cores")
ram = fields.Integer("RAM (Go)")
disk_storage = fields.Integer("Disk Storage (Go)")
backup_activated = fields.Boolean("Backup Activated ?")
backup_server_id = fields.Many2one("backup.server", string="Backup Server")
backup_ok = fields.Boolean("Backup OK ?")
name_fr = fields.Char("Name (FR)", compute="_compute_name_fr", store=True)
@api.depends("name")
def _compute_name_fr(self):
for record in self:
record.name_fr = record.with_context(lang="fr_FR").name
server_ip = fields.Char('Server Ip Address')
distribution_id = fields.Many2one('os.distribution', string='Distribution')
service_ids = fields.One2many('service.instance', 'equipment_id', string='Services')
hosting_city = fields.Char('Hosting City')
nb_cores = fields.Integer('Nb Cores')
ram = fields.Integer('RAM (Go)')
disk_storage = fields.Integer('Disk Storage (Go)')
backup_activated = fields.Boolean('Backup Activated ?')
backup_server_id = fields.Many2one('backup.server', string='Backup Server')
backup_ok = fields.Boolean('Backup OK ?')

View File

@@ -3,7 +3,7 @@
{
"name": "maintenance_server_monitoring",
"version": "16.0.1.0.0",
"version": "14.0.1.0.0",
"author": "Elabore",
"website": "https://elabore.coop",
"maintainer": "Clément Thomas",

View File

@@ -6,31 +6,20 @@ from io import StringIO
LOG_LIMIT = 100000
AVAILABLE_MEMORY_PERCENT_COMMAND = "free | grep Mem | awk '{print $3/$2 * 100.0}'"
MIN_AVAILABLE_MEMORY_PERCENT_WARNING = 20
MIN_AVAILABLE_MEMORY_PERCENT_ERROR = 5
USED_DISK_SPACE_COMMAND = "df /srv -h | tail -n +2 | sed -r 's/ +/ /g' | cut -f 5 -d ' ' | cut -f 1 -d %"
MAX_USED_DISK_SPACE_WARNING = 70
MAX_USED_DISK_SPACE_ERROR = 90
MAX_PING_MS_WARNING = 1000
MAX_PING_MS_ERROR = 5000
"""
if you want to add a new test :
* create new module named maintenance_server_monitoring_{your_test}
* add new field to MaintenanceEquipment (named {fieldname} below)
* add a new function named test_{fieldname} which return a filled MonitoringTest class with :
-> log = logs you want to appear in logs
-> result = value which will be set to {fieldname}
-> error = MonitoringTest.ERROR or MonitoringTest.WARNING to generate maintenance request
** Note you can use test_ok, test_warning, and test_error functions to simplify code **
* add requirements if necessary in install_dependencies function
* call your function in monitoring_test() with a simple launch_test({fieldname}, *args)
if needed, *args can be passed by parameters to your test function
* inherit get_tests() to reference your field {fieldname}
be inspired by maintenance_server_monitoring_ping for exemple
"""
@@ -39,11 +28,7 @@ class MaintenanceEquipment(models.Model):
last_monitoring_test_date = fields.Datetime('Date of last monitoring test', readonly=True)
#tests
ping_ok = fields.Boolean("Ping ok", readonly=True)
available_memory_percent = fields.Float('Percent of available memory', readonly=True)
used_disk_space = fields.Float('Percent of used disk space', readonly=True)
ssh_ok = fields.Boolean("SSH ok", readonly=True)
enable_monitoring = fields.Boolean('Monitoring enabled', help="If enabled, cron will test this equipment")
#log
log = fields.Html("Log", readonly=True)
@@ -57,7 +42,7 @@ class MaintenanceEquipment(models.Model):
"""Class to make the tests
"""
WARNING = "warning"
ERROR = "error"
ERROR = "error"
def __init__(self, name):
self.name = name # name of the test
@@ -123,72 +108,62 @@ class MaintenanceEquipment(models.Model):
def cron_monitoring_test(self):
"""cron launch test on all equipments
"""
self.search([]).monitoring_test()
def monitoring_test(self):
self.search([("enable_monitoring","=",True)]).monitoring_test()
def launch_test(attribute, *test_function_args):
def launch_test(self, field_name, *test_function_args):
"""run test function with name = test_[attribute]
associate result of test to equipment
write logs of test
Args:
attribute (string): attribute of MaintenanceEquipment we want to test
attribute_name (string): attribute of MaintenanceEquipment we want to test
*test_function_args = optionnal args to pass to function (unused for the moment)
Returns:
MonitoringTest: returned by test function
"""
test_function = getattr(equipment,"test_"+attribute)
test_function = getattr(self,"test_"+field_name)
test = test_function(*test_function_args)
setattr(equipment, attribute, test.result)
log.write(test.log)
tests.append(test)
setattr(self, field_name, test.result)
return test
def get_tests(self):
"""function to inherit in sub-modules
Returns:
array[string]: names of fields to test
"""
return []
def monitoring_test(self):
for equipment in self:
# we use StingIO instead of string to use mutable object
log = StringIO()
for equipment in self:
# array of all tests
tests = []
tests_results = []
# install dependencies and log it
log.write(equipment.install_dependencies().log) # launch_test is not used, only logs are necessary
# run ping test
launch_test("ping_ok")
# SSH dependant test
ssh = launch_test("ssh_ok").result
if ssh:
# test available memory
launch_test("available_memory_percent", ssh)
# test disk usage
launch_test("used_disk_space", ssh)
else:
equipment.available_memory_percent = -1 #set -1 by convention if error
equipment.used_disk_space = -1 #set -1 by convention if error
# run all tests referenced in get_tests and save result
for test in self.get_tests():
tests_results.append(equipment.launch_test(test))
# set test date
equipment.last_monitoring_test_date = fields.Datetime.now()
# write logs
log.seek(0) #log is a StringIO so seek to beginning before read
new_log = f'📣 {fields.Datetime.now()}\n{log.read()}\n'
new_log = new_log.replace("\n","<br />") # log field is HTML, so format lines
#new logs are current datetime + join of test results logs
new_log = f'📣 {fields.Datetime.now()}\n{"".join([tests_result.log for tests_result in tests_results])}\n'.replace("\n","<br />")
#add new logs to the beginning of equipment log
equipment.log = f'{new_log}<br />{equipment.log}'[:LOG_LIMIT] #limit logs
#Create maintenance request only if monitoring is enabled
if not equipment.enable_monitoring:
return
# if error create maintenance request
error = warning =False
if any(test.error == test.ERROR for test in tests):
if any(test.error == test.ERROR for test in tests_results):
error = True # if any arror in tests
elif any(test.error == test.WARNING for test in tests):
elif any(test.error == test.WARNING for test in tests_results):
warning = True # if any warning in tests
if error or warning:
@@ -202,171 +177,45 @@ class MaintenanceEquipment(models.Model):
existing_not_done_warning_request = equipment.warning_maintenance_request
if (error and not existing_not_done_error_request) \
or (warning and not existing_not_done_warning_request and not existing_not_done_error_request):
maintenance_request = self.env['maintenance.request'].create({
"name":f'[{"ERROR" if error else "WARNING"}] {equipment.name}',
"equipment_id":equipment.id,
"employee_id":equipment.employee_id,
"user_id":equipment.technician_user_id,
"maintenance_team_id":equipment.maintenance_team_id.id or self.env["maintenance.team"].search([], limit=1),
"priority":'2' if error else '3',
"maintenance_type":"corrective" if error else "preventive",
"description":new_log
})
if error:
equipment.error_maintenance_request = maintenance_request
else:
equipment.warning_maintenance_request = maintenance_request
equipment.create_maintenance_request(self.MonitoringTest.ERROR if error else self.MonitoringTest.WARNING, new_log)
else:
equipment.no_error()
def create_maintenance_request(self, error_level, description):
"""create a maintenance request for equipment (self)
Args:
error_level (string): MonitoringTest.ERROR or MonitoringTest.WARNING
description (string): description of maintenance request
"""
maintenance_request = self.env['maintenance.request'].create({
"name":f'[{error_level.upper()}] {self.name}',
"equipment_id":self.id,
"user_id":self.technician_user_id.id,
"maintenance_team_id":self.maintenance_team_id.id or self.env["maintenance.team"].search([], limit=1),
"priority":'2' if error_level == self.MonitoringTest.ERROR else '3',
"maintenance_type":"corrective" if error_level == self.MonitoringTest.ERROR else "preventive",
"description":description
})
if error_level == self.MonitoringTest.ERROR:
self.error_maintenance_request = maintenance_request
self.warning_maintenance_request = None
else:
self.warning_maintenance_request = maintenance_request
self.error_maintenance_request = None
def install_dependencies(self):
def no_error(self):
"""set error and warning maintenance request to None
"""
install dependencies needed to do all tests, as python or shell programs
Returns:
MonitoringTest: representing current test with result=0 if not error
"""
monitoring_test = self.MonitoringTest("install dependencies")
if "ping3" in sys.modules:
return monitoring_test.test_ok(0, "ping3 already installed")
else:
try:
command = ['pip','install',"ping3"]
response = subprocess.call(command) # run "pip install ping3" command
if response == 0:
return monitoring_test.test_ok(0, "ping3 installation successful")
else:
monitoring_test.test_error(f"ping3 : unable to install : response = {response}")
except Exception as e:
return monitoring_test.test_error(f"ping3 : unable to install : {e}")
def test_ssh_ok(self):
"""
test ssh with maintenance_server_ssh module
Returns:
MonitoringTest: representing current test with :
* result = False if error
* result = ssh connection if no error
* error = MonitoringTest.ERROR if connection failed
* log file
"""
test = self.MonitoringTest("SSH OK")
try:
# SSH connection ok : set ssh connection in result, converted in boolean (True) when set in ssh_ok field
return test.test_ok(self.get_ssh_connection(), "SSH Connection OK") #ssh connection given by maintenance_server_ssh module
except Exception as e:
# SSH connection failed
return test.test_error(False, f"{fields.Datetime.now()} > SSH > connection failed {e}\n")
self.error_maintenance_request = None
self.warning_maintenance_request = None
def test_available_memory_percent(self, ssh):
"""
test available memory with a bash command called by ssh
Args:
ssh (paramiko.SSHClient): ssh client
Returns:
MonitoringTest: representing current test with :
* result = -2 if error
* result = percent of available memory if no error
* error defined with MonitoringTest.ERROR or MonitoringTest.WARNING depending on result comparaison
with MIN_AVAILABLE_MEMORY_PERCENT_WARNING and MIN_AVAILABLE_MEMORY_PERCENT_ERROR
* log file
"""
try:
test = self.MonitoringTest("Available memory percent")
_stdin, stdout, _stderr = ssh.exec_command(AVAILABLE_MEMORY_PERCENT_COMMAND)
available_memory_percent = float(stdout.read().decode())
if available_memory_percent > MIN_AVAILABLE_MEMORY_PERCENT_WARNING:
return test.test_ok(available_memory_percent, f"{available_memory_percent}% available")
elif available_memory_percent > MIN_AVAILABLE_MEMORY_PERCENT_ERROR:
# memory between warning and error step
return test.test_warning(available_memory_percent, f"{available_memory_percent}% available (<{MIN_AVAILABLE_MEMORY_PERCENT_WARNING})")
else:
# memory available lower than error step
return test.test_error(available_memory_percent, f"{available_memory_percent}% available (<{MIN_AVAILABLE_MEMORY_PERCENT_ERROR})")
except Exception as e:
return test.test_error(-2, f"{e}")
def test_used_disk_space(self, ssh):
"""
test Used disk space with a bash command called by ssh
Args:
ssh (paramiko.SSHClient): ssh client
Returns:
MonitoringTest: representing current test with :
* result = -2 if error
* result = percent of Used disk space if no error
* error defined with MonitoringTest.ERROR or MonitoringTest.WARNING depending on result comparaison
with MAX_USED_DISK_SPACE_WARNING and MAX_USED_DISK_SPACE_ERROR
* log file
"""
try:
test = self.MonitoringTest("Used disk space")
_stdin, stdout, _stderr = ssh.exec_command(USED_DISK_SPACE_COMMAND)
used_disk_space = float(stdout.read().decode())
if used_disk_space < MAX_USED_DISK_SPACE_WARNING:
return test.test_ok(used_disk_space, f"{used_disk_space}% used")
elif used_disk_space < MAX_USED_DISK_SPACE_ERROR:
# disk usage between WARNING and ERROR steps
return test.test_warning(used_disk_space, f"{used_disk_space}% used (>{MAX_USED_DISK_SPACE_WARNING})")
else:
# disk usage higher than ERROR steps
return test.test_error(used_disk_space, f"{used_disk_space}% used (>{MAX_USED_DISK_SPACE_ERROR})")
except Exception as e:
return test.test_error(-2, f"{e}")
def test_ping_ok(self):
"""
test PING with ping3 library
Returns:
MonitoringTest: representing current test with :
* result = False if error
* result = True if no error
* error defined with MonitoringTest.ERROR or MonitoringTest.WARNING depending on ping time comparaison
with MAX_PING_MS_WARNING and MAX_PING_MS_ERROR
* log file
"""
test = self.MonitoringTest("Ping")
try:
from ping3 import ping
except Exception as e:
# unable to import ping3
return test.test_error(False, f"ping3 dependencie not satisfied : {e}")
hostname = self.server_domain
if not hostname:
# equipment host name not filled
return test.test_error(False, f"host name seems empty !")
try:
r = ping(hostname)
except Exception as e:
# Any problem when call ping
return test.test_error(False, f"unable to call ping ! > {e}")
if r:
test.result = True
ping_ms = int(r*1000)
if ping_ms < MAX_PING_MS_WARNING:
# ping OK
return test.test_ok(True, f"PING OK in {ping_ms} ms")
elif ping_ms < MAX_PING_MS_ERROR:
# ping result between WARNING and ERROR => WARNING
return test.test_warning(True, f"PING OK in {ping_ms}ms (> {MAX_PING_MS_WARNING})")
else:
# ping result higher than ERROR => ERROR
return test.test_error(False, f"PING OK in {ping_ms}ms (> {MAX_PING_MS_ERROR})")
else:
return test.test_error(False, "PING FAILED")

View File

@@ -8,13 +8,11 @@
<xpath expr="//notebook" position="inside">
<page name="monitoring" string="Monitoring">
<group name="monitoring_test" string="Test">
<field name="last_monitoring_test_date" />
<field name="ping_ok" />
<field name="ssh_ok" />
<field name="available_memory_percent" />
<field name="used_disk_space" />
<field name="enable_monitoring" />
<field name="last_monitoring_test_date" />
<button name="monitoring_test" type="object" string="Test" />
</group>
<group name="monitoring_test_result" />
<group name="monitoring_log" string="Log">
<field name="log" />
</group>
@@ -29,11 +27,8 @@
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_tree" />
<field name="arch" type="xml">
<xpath expr="//field[@name='category_id']" position="after">
<field name="ping_ok" optional="hide" />
<field name="ssh_ok" />
<field name="available_memory_percent" optional="hide" />
<field name="used_disk_space" optional="hide" />
<xpath expr="//field[@name='category_id']" position="after">
<field name="enable_monitoring" />
</xpath>
</field>
</record>

View File

@@ -1,18 +1,14 @@
=============================================
maintenance_create_requests_from_project_task
=============================================
======================================
maintenance_server_monitoring_memory
======================================
Allow the creation of multiple maintenance requests from a projet task.
When user click on the button "Create maintenance requests", a wizard appears.
The wizard allows the user to configure the requests and to select the maintenance equipments concerned.
At wizard validation, one or several maintenance requests are created, one for each equipement selected.
Improve monitoring with ping test
Installation
============
Use Odoo normal module installation procedure to install
``maintenance_create_requests_from_project_task``.
``maintenance_server_monitoring_memory``.
Known issues / Roadmap
======================
@@ -33,7 +29,7 @@ Credits
Contributors
------------
* Stéphan Sainléger
* Clément Thomas
Funders
-------

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from . import models
from . import wizard

View File

@@ -0,0 +1,37 @@
# Copyright 2023 Stéphan Sainléger (Elabore)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "maintenance_server_monitoring_memory",
"version": "14.0.1.0.0",
"author": "Elabore",
"website": "https://elabore.coop",
"maintainer": "Clément Thomas",
"license": "AGPL-3",
"category": "Tools",
"summary": "Monitor memory on remote hosts",
# any module necessary for this one to work correctly
"depends": [
"maintenance_server_monitoring",
"maintenance_server_ssh"
],
"qweb": [
# "static/src/xml/*.xml",
],
"external_dependencies": {
"python": [],
},
# always loaded
"data": [
"views/maintenance_equipment_views.xml",
],
# only loaded in demonstration mode
"demo": [],
"js": [],
"css": [],
"installable": True,
# Install this module automatically if all dependency have been previously
# and independently installed. Used for synergetic or glue modules.
"auto_install": False,
"application": False,
}

View File

@@ -0,0 +1 @@
from . import maintenance_equipment

View File

@@ -0,0 +1,52 @@
from odoo import fields, models, api
USED_DISK_SPACE_COMMAND = "df /srv -h | tail -n +2 | sed -r 's/ +/ /g' | cut -f 5 -d ' ' | cut -f 1 -d %"
MAX_USED_DISK_SPACE_WARNING = 70
MAX_USED_DISK_SPACE_ERROR = 90
class MaintenanceEquipment(models.Model):
_inherit = 'maintenance.equipment'
used_disk_space = fields.Float('Percent of used disk space', readonly=True)
def get_tests(self):
res = super(MaintenanceEquipment, self).get_tests()
res.append("used_disk_space")
return res
def test_used_disk_space(self):
"""
test Used disk space with a bash command called by ssh
Args:
ssh (paramiko.SSHClient): ssh client
Returns:
MonitoringTest: representing current test with :
* result = -2 if error
* result = percent of Used disk space if no error
* error defined with MonitoringTest.ERROR or MonitoringTest.WARNING depending on result comparaison
with MAX_USED_DISK_SPACE_WARNING and MAX_USED_DISK_SPACE_ERROR
* log file
"""
test = self.MonitoringTest("Used disk space")
try:
ssh = self.get_ssh_connection()
if not ssh:
return test.test_error(-2, "No ssh connection")
_stdin, stdout, _stderr = ssh.exec_command(USED_DISK_SPACE_COMMAND)
used_disk_space = float(stdout.read().decode())
if used_disk_space < MAX_USED_DISK_SPACE_WARNING:
return test.test_ok(used_disk_space, f"{used_disk_space}% used")
elif used_disk_space < MAX_USED_DISK_SPACE_ERROR:
# disk usage between WARNING and ERROR steps
return test.test_warning(used_disk_space, f"{used_disk_space}% used (>{MAX_USED_DISK_SPACE_WARNING})")
else:
# disk usage higher than ERROR steps
return test.test_error(used_disk_space, f"{used_disk_space}% used (>{MAX_USED_DISK_SPACE_ERROR})")
except Exception as e:
return test.test_error(-2, f"{e}")

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="equipment_view_form_server_inherit" model="ir.ui.view">
<field name="name">equipment.form.server.inherit</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_form" />
<field name="arch" type="xml">
<group name="monitoring_test_result" position="inside">
<field name="used_disk_space" />
</group>
</field>
</record>
<record id="equipment_view_tree_server_inherit" model="ir.ui.view">
<field name="name">equipment.tree.server.inherit</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_tree" />
<field name="arch" type="xml">
<xpath expr="//field[@name='enable_monitoring']" position="after">
<field name="used_disk_space" optional="hide" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,2 @@
*.*~
*pyc

View File

@@ -0,0 +1,44 @@
======================================
maintenance_server_monitoring
======================================
Monitor some data on remote hosts
Installation
============
Use Odoo normal module installation procedure to install
``maintenance_server_monitoring``.
Known issues / Roadmap
======================
None yet.
Bug Tracker
===========
Bugs are tracked on `our issues website <https://github.com/elabore-coop/maintenance-tools/issues>`_. In case of
trouble, please check there if your issue has already been
reported. If you spotted it first, help us smashing it by providing a
detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Clément Thomas
Funders
-------
The development of this module has been financially supported by:
* Elabore (https://elabore.coop)
Maintainer
----------
This module is maintained by Elabore.

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -2,20 +2,19 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "maintenance_create_requests_from_project_task",
"version": "16.0.1.0.0",
"name": "maintenance_server_monitoring_maintenance_equipment_status",
"version": "14.0.1.0.0",
"author": "Elabore",
"website": "https://elabore.coop",
"maintainer": "Stéphan Sainléger",
"maintainer": "Clément Thomas",
"license": "AGPL-3",
"category": "Tools",
"summary": "Allow the creation of multiple maintenance requests from a projet task.",
"summary": "Monitor some data on remote hosts",
# any module necessary for this one to work correctly
"depends": [
"base",
"maintenance",
"maintenance_project",
"project",
"maintenance_server_monitoring",
"maintenance_equipment_status"
],
"qweb": [
# "static/src/xml/*.xml",
@@ -24,10 +23,8 @@
"python": [],
},
# always loaded
"data": [
"security/ir.model.access.csv",
"views/project_task.xml",
"wizard/create_maintenance_requests_wizard.xml",
"data": [
"views/maintenance_equipment_status_views.xml",
],
# only loaded in demonstration mode
"demo": [],
@@ -36,6 +33,6 @@
"installable": True,
# Install this module automatically if all dependency have been previously
# and independently installed. Used for synergetic or glue modules.
"auto_install": False,
"auto_install": True,
"application": False,
}
}

View File

@@ -0,0 +1,2 @@
from . import maintenance_equipment
from . import maintenance_equipment_status

View File

@@ -0,0 +1,23 @@
from odoo import fields, models, api
class MaintenanceEquipment(models.Model):
_inherit = 'maintenance.equipment'
def create_maintenance_request(self, error_level, description):
res = super(MaintenanceEquipment, self).create_maintenance_request(error_level, description)
if self.error_maintenance_request:
error_status = self.env["maintenance.equipment.status"].search([("is_error_status",'=',True),'|', ('category_ids', 'in', [self.category_id.id]), ('category_ids', '=', False)], limit=1)
if error_status:
self.status_id = error_status
else:
warning_status = self.env["maintenance.equipment.status"].search([("is_warning_status",'=',True),'|', ('category_ids', 'in', [self.category_id.id]), ('category_ids', '=', False)], limit=1)
if warning_status:
self.status_id = warning_status
return res
def no_error(self):
res = super(MaintenanceEquipment, self).no_error()
ok_status = self.env["maintenance.equipment.status"].search([("is_error_status",'=',False),("is_warning_status",'=',False),'|', ('category_ids', 'in', [self.category_id.id]), ('category_ids', '=', False)], limit=1)
self.status_id = ok_status
return res

View File

@@ -0,0 +1,11 @@
from odoo import fields, models, api
import subprocess
import sys
import psutil
from io import StringIO
class MaintenanceEquipmentStatus(models.Model):
_inherit = "maintenance.equipment.status"
is_warning_status = fields.Boolean('Is warning status')
is_error_status = fields.Boolean('Is error status')

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="maintenance_equipment_status_view_form_inherit" model="ir.ui.view">
<field name="name">maintenance.equipment.status.form.inherit</field>
<field name="model">maintenance.equipment.status</field>
<field name="inherit_id" ref="maintenance_equipment_status.maintenance_equipment_status_view_form" />
<field name="arch" type="xml">
<group name="notes" position="after">
<group name="monitoring">
<field name="is_warning_status" />
<field name="is_error_status" />
</group>
</group>
</field>
</record>
<record id="maintenance_equipment_status_view_tree_inherit" model="ir.ui.view">
<field name="name">maintenance.equipment.status.tree.inherit</field>
<field name="model">maintenance.equipment.status</field>
<field name="inherit_id" ref="maintenance_equipment_status.maintenance_equipment_status_view_tree" />
<field name="arch" type="xml">
<field name="category_ids" position="after">
<field name="is_warning_status" />
<field name="is_error_status" />
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,2 @@
*.*~
*pyc

View File

@@ -0,0 +1,44 @@
======================================
maintenance_server_monitoring_memory
======================================
Improve monitoring with ping test
Installation
============
Use Odoo normal module installation procedure to install
``maintenance_server_monitoring_memory``.
Known issues / Roadmap
======================
None yet.
Bug Tracker
===========
Bugs are tracked on `our issues website <https://github.com/elabore-coop/maintenance-tools/issues>`_. In case of
trouble, please check there if your issue has already been
reported. If you spotted it first, help us smashing it by providing a
detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Clément Thomas
Funders
-------
The development of this module has been financially supported by:
* Elabore (https://elabore.coop)
Maintainer
----------
This module is maintained by Elabore.

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,37 @@
# Copyright 2023 Stéphan Sainléger (Elabore)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "maintenance_server_monitoring_memory",
"version": "14.0.1.0.0",
"author": "Elabore",
"website": "https://elabore.coop",
"maintainer": "Clément Thomas",
"license": "AGPL-3",
"category": "Tools",
"summary": "Monitor memory on remote hosts",
# any module necessary for this one to work correctly
"depends": [
"maintenance_server_monitoring",
"maintenance_server_ssh"
],
"qweb": [
# "static/src/xml/*.xml",
],
"external_dependencies": {
"python": [],
},
# always loaded
"data": [
"views/maintenance_equipment_views.xml",
],
# only loaded in demonstration mode
"demo": [],
"js": [],
"css": [],
"installable": True,
# Install this module automatically if all dependency have been previously
# and independently installed. Used for synergetic or glue modules.
"auto_install": False,
"application": False,
}

View File

@@ -0,0 +1 @@
from . import maintenance_equipment

View File

@@ -0,0 +1,51 @@
from odoo import fields, models, api
AVAILABLE_MEMORY_PERCENT_COMMAND = "free | grep Mem | awk '{print $3/$2 * 100.0}'"
MIN_AVAILABLE_MEMORY_PERCENT_WARNING = 20
MIN_AVAILABLE_MEMORY_PERCENT_ERROR = 5
class MaintenanceEquipment(models.Model):
_inherit = 'maintenance.equipment'
available_memory_percent = fields.Float('Percent of available memory', readonly=True)
def get_tests(self):
res = super(MaintenanceEquipment, self).get_tests()
res.append("available_memory_percent")
return res
def test_available_memory_percent(self):
"""
test available memory with a bash command called by ssh
Args:
ssh (paramiko.SSHClient): ssh client
Returns:
MonitoringTest: representing current test with :
* result = -2 if error
* result = percent of available memory if no error
* error defined with MonitoringTest.ERROR or MonitoringTest.WARNING depending on result comparaison
with MIN_AVAILABLE_MEMORY_PERCENT_WARNING and MIN_AVAILABLE_MEMORY_PERCENT_ERROR
* log file
"""
test = self.MonitoringTest("Available memory percent")
try:
ssh = self.get_ssh_connection()
if not ssh:
return test.test_error(-2, "No ssh connection")
_stdin, stdout, _stderr = ssh.exec_command(AVAILABLE_MEMORY_PERCENT_COMMAND)
available_memory_percent = float(stdout.read().decode())
if available_memory_percent > MIN_AVAILABLE_MEMORY_PERCENT_WARNING:
return test.test_ok(available_memory_percent, f"{available_memory_percent}% available")
elif available_memory_percent > MIN_AVAILABLE_MEMORY_PERCENT_ERROR:
# memory between warning and error step
return test.test_warning(available_memory_percent, f"{available_memory_percent}% available (<{MIN_AVAILABLE_MEMORY_PERCENT_WARNING})")
else:
# memory available lower than error step
return test.test_error(available_memory_percent, f"{available_memory_percent}% available (<{MIN_AVAILABLE_MEMORY_PERCENT_ERROR})")
except Exception as e:
return test.test_error(-2, f"{e}")

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="equipment_view_form_server_inherit" model="ir.ui.view">
<field name="name">equipment.form.server.inherit</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_form" />
<field name="arch" type="xml">
<group name="monitoring_test_result" position="inside">
<field name="available_memory_percent" />
</group>
</field>
</record>
<record id="equipment_view_tree_server_inherit" model="ir.ui.view">
<field name="name">equipment.tree.server.inherit</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_tree" />
<field name="arch" type="xml">
<xpath expr="//field[@name='enable_monitoring']" position="after">
<field name="available_memory_percent" optional="hide" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,2 @@
*.*~
*pyc

View File

@@ -0,0 +1,44 @@
======================================
maintenance_server_monitoring_ping
======================================
Improve monitoring with ping test
Installation
============
Use Odoo normal module installation procedure to install
``maintenance_server_monitoring_ping``.
Known issues / Roadmap
======================
None yet.
Bug Tracker
===========
Bugs are tracked on `our issues website <https://github.com/elabore-coop/maintenance-tools/issues>`_. In case of
trouble, please check there if your issue has already been
reported. If you spotted it first, help us smashing it by providing a
detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Clément Thomas
Funders
-------
The development of this module has been financially supported by:
* Elabore (https://elabore.coop)
Maintainer
----------
This module is maintained by Elabore.

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,36 @@
# Copyright 2023 Stéphan Sainléger (Elabore)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "maintenance_server_monitoring_ping",
"version": "14.0.1.0.0",
"author": "Elabore",
"website": "https://elabore.coop",
"maintainer": "Clément Thomas",
"license": "AGPL-3",
"category": "Tools",
"summary": "Monitor ping on remote hosts",
# any module necessary for this one to work correctly
"depends": [
"maintenance_server_monitoring"
],
"qweb": [
# "static/src/xml/*.xml",
],
"external_dependencies": {
"python": [],
},
# always loaded
"data": [
"views/maintenance_equipment_views.xml",
],
# only loaded in demonstration mode
"demo": [],
"js": [],
"css": [],
"installable": True,
# Install this module automatically if all dependency have been previously
# and independently installed. Used for synergetic or glue modules.
"auto_install": False,
"application": False,
}

View File

@@ -0,0 +1 @@
from . import maintenance_equipment

View File

@@ -0,0 +1,71 @@
from odoo import fields, models, api
import subprocess
MAX_PING_MS_WARNING = 1000
MAX_PING_MS_ERROR = 5000
class MaintenanceEquipment(models.Model):
_inherit = 'maintenance.equipment'
ping_ok = fields.Boolean("Ping ok", readonly=True)
def get_tests(self):
res = super(MaintenanceEquipment, self).get_tests()
res.append("ping_ok")
return res
def test_ping_ok(self):
"""
test PING with ping3 library
Returns:
MonitoringTest: representing current test with :
* result = False if error
* result = True if no error
* error defined with MonitoringTest.ERROR or MonitoringTest.WARNING depending on ping time comparaison
with MAX_PING_MS_WARNING and MAX_PING_MS_ERROR
* log file
"""
test = self.MonitoringTest("Ping")
try:
from ping3 import ping
except ImportError as e:
# unable to import ping3
try:
command = ['pip3','install',"ping3==4.0.5"]
response = subprocess.call(command) # run "pip install ping3" command
if response != 0:
return test.test_error(False, f"ping3 : unable to install : response = {response}")
else:
from ping3 import ping
except Exception as e:
return test.test_error(False, f"ping3 : unable to install : {e}")
hostname = self.server_domain
if not hostname:
# equipment host name not filled
return test.test_error(False, f"host name seems empty !")
try:
r = ping(hostname)
except Exception as e:
# Any problem when call ping
return test.test_error(False, f"unable to call ping ! > {e}")
if r:
test.result = True
ping_ms = int(r*1000)
if ping_ms < MAX_PING_MS_WARNING:
# ping OK
return test.test_ok(True, f"PING OK in {ping_ms} ms")
elif ping_ms < MAX_PING_MS_ERROR:
# ping result between WARNING and ERROR => WARNING
return test.test_warning(True, f"PING OK in {ping_ms}ms (> {MAX_PING_MS_WARNING})")
else:
# ping result higher than ERROR => ERROR
return test.test_error(True, f"PING OK in {ping_ms}ms (> {MAX_PING_MS_ERROR})")
else:
return test.test_error(False, "PING FAILED")

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="equipment_view_form_server_inherit" model="ir.ui.view">
<field name="name">equipment.form.server.inherit</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_form" />
<field name="arch" type="xml">
<group name="monitoring_test_result" position="inside">
<field name="ping_ok" />
</group>
</field>
</record>
<record id="equipment_view_tree_server_inherit" model="ir.ui.view">
<field name="name">equipment.tree.server.inherit</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_tree" />
<field name="arch" type="xml">
<xpath expr="//field[@name='enable_monitoring']" position="after">
<field name="ping_ok" optional="hide" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,2 @@
*.*~
*pyc

View File

@@ -0,0 +1,44 @@
======================================
maintenance_server_monitoring_ssh
======================================
Improve monitoring with ping test
Installation
============
Use Odoo normal module installation procedure to install
``maintenance_server_monitoring_ssh``.
Known issues / Roadmap
======================
None yet.
Bug Tracker
===========
Bugs are tracked on `our issues website <https://github.com/elabore-coop/maintenance-tools/issues>`_. In case of
trouble, please check there if your issue has already been
reported. If you spotted it first, help us smashing it by providing a
detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Clément Thomas
Funders
-------
The development of this module has been financially supported by:
* Elabore (https://elabore.coop)
Maintainer
----------
This module is maintained by Elabore.

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,37 @@
# Copyright 2023 Stéphan Sainléger (Elabore)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "maintenance_server_monitoring_ssh",
"version": "14.0.1.0.0",
"author": "Elabore",
"website": "https://elabore.coop",
"maintainer": "Clément Thomas",
"license": "AGPL-3",
"category": "Tools",
"summary": "Monitor ssh response on remote hosts",
# any module necessary for this one to work correctly
"depends": [
"maintenance_server_monitoring",
"maintenance_server_ssh"
],
"qweb": [
# "static/src/xml/*.xml",
],
"external_dependencies": {
"python": [],
},
# always loaded
"data": [
"views/maintenance_equipment_views.xml",
],
# only loaded in demonstration mode
"demo": [],
"js": [],
"css": [],
"installable": True,
# Install this module automatically if all dependency have been previously
# and independently installed. Used for synergetic or glue modules.
"auto_install": False,
"application": False,
}

View File

@@ -0,0 +1 @@
from . import maintenance_equipment

View File

@@ -0,0 +1,30 @@
from odoo import fields, models, api
class MaintenanceEquipment(models.Model):
_inherit = 'maintenance.equipment'
ssh_ok = fields.Boolean("SSH ok", readonly=True)
def get_tests(self):
res = super(MaintenanceEquipment, self).get_tests()
res.append("ssh_ok")
return res
def test_ssh_ok(self):
"""
test ssh with maintenance_server_ssh module
Returns:
MonitoringTest: representing current test with :
* result = False if error
* result = ssh connection if no error
* error = MonitoringTest.ERROR if connection failed
* log file
"""
test = self.MonitoringTest("SSH OK")
try:
# SSH connection ok : set ssh connection in result, converted in boolean (True) when set in ssh_ok field
return test.test_ok(self.get_ssh_connection(), "SSH Connection OK") #ssh connection given by maintenance_server_ssh module
except Exception as e:
# SSH connection failed
return test.test_error(False, "connection failed {e}")

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="equipment_view_form_server_inherit" model="ir.ui.view">
<field name="name">equipment.form.server.inherit</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_form" />
<field name="arch" type="xml">
<group name="monitoring_test_result" position="inside">
<field name="ssh_ok" />
</group>
</field>
</record>
<record id="equipment_view_tree_server_inherit" model="ir.ui.view">
<field name="name">equipment.tree.server.inherit</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_tree" />
<field name="arch" type="xml">
<xpath expr="//field[@name='enable_monitoring']" position="after">
<field name="ssh_ok" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -3,7 +3,7 @@
{
"name": "maintenance_server_ssh",
"version": "16.0.1.0.0",
"version": "14.0.1.0.0",
"author": "Elabore",
"website": "https://elabore.coop",
"maintainer": "Clément Thomas",

View File

@@ -10,11 +10,18 @@ class MaintenanceEquipment(models.Model):
server_domain = fields.Char('Server Domain')
ssh_private_key_path = fields.Char("SSH private key path", default="/opt/odoo/auto/dev/ssh_keys/id_rsa")
def get_ssh_connection(self):
def get_ssh_connection(self):
ssh_connections = self.env.context.get('ssh_connections',{})
if self.id in ssh_connections:
return ssh_connections[self.id]
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(self.server_domain, username="root", key_filename=self.ssh_private_key_path)
ssh_connections[self.id] = ssh
self = self.with_context(ssh_connections=ssh_connections)
return ssh