Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hilbert YAML configuration parser and Hilbert Server/Client-side tools #34

Merged
merged 45 commits into from
Dec 9, 2016

Conversation

malex984
Copy link
Member

@malex984 malex984 commented Nov 9, 2016

This is the first iteration of implementing Configuration Design.

Major Parts:

  • minimal CLI for hilbert (server part)
  • minimal CLI for hilbert-station (client part)
  • Makefile supports documentation generation: make epydoc / make apidocs
  • Makefile supports automated testing: make check
  • Server-side tool uses $HOME/.ssh/config to execute remote actions via ssh. Currently it expects client tool: hilbert-station to be available remotely.
  • Hilbert client-side tool: hilbert-station (currently requires legacy scripts and older config files)
  • logging output: iterated -v adds verbosity whereas iterated -q removes verbosity. Default: WARNING level.
  • add automated testing pytest (maybe also tox / unittest later on). Currently for (Danger of silent overrides due to YAML #27): tests/test_loadwarnings.py, VersionValidator: tests/test_version.py and whole configuration validator (Hilbert) + (Configuration design: Wrong format: list vs mapping #17): tests/test_validate.py

Oleksandr Motsak added 5 commits November 7, 2016 08:05
FIX: many fixes
CHG/ADD: changes and additions to the class hierarchie, e.g.:
* DockerMachie variant for PowerOnMethod
* expect default values for optional fields!
NOTE: due to @porst17's comment #32 (comment)

ADD: minor comments (TODOs for later)
…Scalar and BaseString

CHG: only keep the processed data is there was no error
TODO: check that every validator follows this!

REN: Abstract -> AbstractValidator
@malex984 malex984 added this to the Configuration design milestone Nov 9, 2016
Oleksandr Motsak added 17 commits November 15, 2016 03:53
Changed API: version is implicitly globally available, use of classmethod: `Validator.parse(input, parent=...)`
Filled in Validators with actual checks (URI verification, ssh alias, docker-compose config etc)
Added CLI with subcommands to hilbert-cli-config.py (arghandler & argparse)
Added pickling with `dill/pickle`
NOTE: all validators can be tested separately now!

Infrastructure:
Added unit-tests using py.test
Added `Makefile check` to run unit-tests
Added doxygen/epydoc/pylint/pep8 execution to the Makefile
NOTE: the resulting .data.pickle should be generated under python2 (to be understood also by python3)
NOTE: parent should also be a Validator...
* Station:type may be: standalone, server and default: standard
+ Station:client_settings is not required for standalone stations!

TODO: implement the above + add to the design document

* Profiles and Groups may not to be displayed on UI: no need in name/icon/description...

Q: Tags are to be implemented via UI - outside of the general configuration file, right?
helpers, loader, validators + main tool: hilbert

Changes to the subcommand names in hilbert
NOTE: it requires ../config/*.py

Also a few fixes in subcommand's argument handling (e.g. input sources & default values)
For instance:
* printing
* show
* config library package may be anywhere (see adding path to /config/ in config/tests/test_validate.py and /tools/hilbert.py)

Update: tests/data/Hilbert.yml.data.pickle contains Hilbert top class

TODO: Validator.parse should return Validator (or throw an exception)
NOTE: use .parse instead of .validate on temporary objects
…sub-commands to the hilbert tool

CHG: improved Version Validator (keep semantic Version object, but show a version string)
ADD: switched to logging instead of printing
server-side hilbert tool:

- poweron/shutdown <StationID> [<args>]
- deploy <StationID>
- start/finish <StationID> [<ServiceID/ApplicationID>]
- app_switch <StationID> <new ApplicationID>
NOTE: see the legend to "General Hilbert Architecture" in the "Management back-end" google.document
Copy link
Contributor

@porst17 porst17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull request was supposed to be about config validation. Now it is also about the new CLI, so you are mixing different things again. The whole pull request is huge, making it hard to focus my comments on one particular issue. I will start commenting on the new cli.

@elondaits Please add your thought on the cli.

def cmd_poweron(parser, context, args):
log.debug("Running '{}'" . format('cmd_poweron'))

parser.add_argument('-s', '--station', required=True, help="specify the station")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have an additional parameter -s when hilbert poweron should just expect a station as argument by default, e.g. hilbert [global-options] poweron [subcommand-options] station_id?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry - for later

def cmd_shutdown(parser, context, args):
log.debug("Running '{}'" . format('cmd_shutdown'))

parser.add_argument('-s', '--station', required=True, help="specify the station")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just hilbert shutdown station_id?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

later

def cmd_deploy(parser, context, args):
log.debug("Running '{}'" . format('cmd_deploy'))

parser.add_argument('-s', '--station', required=True, help="specify the station")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hilbert deploy station_id

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

later

def cmd_start(parser, context, args):
log.debug("Running '{}'" . format('cmd_deploy'))

parser.add_argument('-s', '--station', required=True, help="specify the station")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hilbert start station_id app_or_service_id

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

later

def cmd_finish(parser, context, args):
log.debug("Running '{}'" . format('cmd_finish'))

parser.add_argument('-s', '--station', required=True, help="specify the station")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hilbert finish station_id app_or_service_id

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

later

def cmd_query(parser, context, args):
log.debug("Running '{}'" . format('cfg_query'))

parser.add_argument('-o', '--object', required=False, default='all',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Streamline the CLI: hilbert cfg_query [config_object].

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry - this is for later

# 2 input sources: .YAML or .PICKLE
handler.add_argument('-if', '--inputfile', required=False,
help="specify input .YAML file (default: 'Hilbert.yml')")
handler.add_argument('-id', '--inputdump', required=False,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-id does not make sense for the `cfg_verify´ subcommand, so it should not be a global option.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are --inputfile and --inputdump a relic of the config validation cli? It suggests that the config is the only valid form of input to hilbert, but I assume that other kinds of input might be necessary in the future. It would be better to call it --configfile and --configdump.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High level representations of data (e.g. a JSON file or SQL file) are also called dumps... in fact, if you have a database in binary format the dump is a SQL text file... so calling the "compiled" representation a dump is confusing.

In NORMAL operation the UI will be using one cfg, and the cli should use the same... and we said the UI server should restart if the cfg changes... so users should be careful not to use the cli with the wrong cfg. Having the option of running the "pickle" file or the YAML one seems error prone and "dangerous" in this way. I recommend the YAML and pickle dates be compared, if the YAML is newer it's automatically compiled to .pickle in the same location. If not, the pickle is used directly.

... But really... it's an optimization already where the program doesn't even do anything. We shouldn't optimize before we even have code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PS: If the CLI runs one command with one cfg and one command with another it can be dangerous as well... it's not just a matter of UI vs CLI.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PS2: Pickle files will break if there are changes to the classes that you serialized... so you should ignore pickle files written by a different version of hilbert (ANY level of changes, even a patch). At the minimum you should add a key to the pickle file that corresponds to the version of hilbert or something like that...

@@ -0,0 +1,589 @@
#!/usr/bin/env python2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to be able to actually call the hilbert command instead of calling

python[3] tools/hilbert.py

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure. you can do that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well just like any script: e.g ./hilbert.py - it will use Python2 by default.
I can add a link named hilbert if .py should be avoided?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .py exposes the implementation detail that the script is written in python (at least it suggests that). The user interface should not expose such things. Please add the link.

return cfg.query(obj)


@subcmd('cfg_query', help='Load Configuration and query some part of it')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like Load Configuration is done by any subcommand except version (which is not really a subcommand and could also be replace by a global switch --version). If it is a common thing, it should be part of the global hilbert documentation and not of the subcommand documentation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

return load_yaml_file(f) # TODO: check that this is a dictionary!


@subcmd('version', help='Display version info')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be replaced by global option --version similar to git --version.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will add that later, Ok?

@elondaits
Copy link
Contributor

I'll check it later today.
Alex, two things now:

  • As far as I remember argparse doesn't really support subcommands (git style) where you can have different number of positional commands depending on the subcommand passed. Check these out, they are popular Python argument parsing libraries that I think support our case:

  • PyCharm does on-the-fly PEP8 validation on the sidebar (the thin stripe next to the scrollbar where a red line indicates an error, yellow a warning, and a gray one a style error). We should support PEP8... static analysis of all sorts is very important with a dynamic language. For instance the spell checker in PyCharm will let you know when you've misspelled something and avoid finding out on runtime.

@elondaits
Copy link
Contributor

The tests are not well structured. Each test should test something specific, so it needs a descriptive name and a descriptive comment. Also tests should not print anything... they're run from the test runner which is the only one that indicates whether things run OK or not. They're not for debugging, rather for indicating there's something wrong.

Also, you should test that the code you wrote is doing what it's supposed to do... assume that third party libraries were already tested and work OK (i.e. no need to test the semantic versioning class).

The test where you load the cfg and then the pickle version of the cfg (produced by dumping your binary representation of the loaded cfg) is tautological... any errors in loading the cfg are there in the pickle as well, so knowing they're the same doesn't give you any reassurances that things are working.

Anyway I'm not sure if @porst17 would want to block the pull request because of these issues, or if we can fix them later. Although I recommend developing the tests alongside the code (not necessarily TDD, but close) because it helps to verify the interfaces are decent, that one can understand the code and that it doesn't have errors. Adding testing at the end is much more painful and we'll have a lot to do next year with the hardware and dockering the apps.

@elondaits
Copy link
Contributor

Finally: That our code supports both Python2 and Python3 is worse, not better. Because we're giving ourselves a bigger problem, twice the amount of testing, and all the incompatibility problems (things we can't do in 2 or that we can't do in 3) instead of half of them. Just choose one... use 3 if it's the same.

@elondaits
Copy link
Contributor

  • I said I have no problem calling hilbert with -q, but I want verbose output... just not any logging. There's a difference between the output of the operation and any errors and logging. As long as -q only affects logging there's no problem.

  • app_start is your previous "start" as I understand. app_stop was not listed but I assume it's logical if start exists. If there's no stop we don't need a start. And it seems a good idea to allow the station to be without any running app without shutting it down, which might take time or could be undesirable for other reasons (faulty HW, etc.)

@malex984
Copy link
Member Author

  • As long as -q only affects logging there's no problem.

Yes. -v and -q only affect logging. -p affects the treatment of some cases: wrong ssh alias/missing file: in pedantic mode such cases will result in an error.

BTW: what about redirecting all of logging output into a separate log file and showing it in case of non-zero exit code?

  • app_start is your previous "start" as I understand. app_stop was not listed but I assume it's logical if start exists. If there's no stop we don't need a start. And it seems a good idea to allow the station to be without any running app without shutting it down, which might take time or could be undesirable for other reasons (faulty HW, etc.)

I think previously there were commands like start_all and finish_all to start/stop all services and an application on a station. But if we consider start_all as a part of poweron and finish_all as a part of shutdown - they not be necessary anymore.

I also think that app_stop and app_start would be good to have but we have to define what should be shown after the current top application was stopped. Desktop? Splash screen? Special dummy application?

Note that OMD server expects that there is a single top application running on a station at any moment and sending heartbeats...
@elondaits Q: do you really want to change this logic now?

Hence app_stop can only stop the currently running top application.
@elondaits Q: should i remove ApplicationID argument to app_stop?

I would suggest to implement app_stop as app_change SplashApplication and
app_start AppID as app_change AppID, with a special dummy application (SplashApplication) which would send heartbeats, disable any user input and take over the display to show some banner.
This way we do not need to change deep OMD logic yet but already have those actions.

@elondaits
Copy link
Contributor

I don't mean to discuss important aspects of the system's working in this issue.

I mentioned app_start because just two days ago you had this:

@subcmd('start', help='start a service/application on a station')

... I just suggested changing "start" to "app_start" and "poweron" to "start" to simplify the syntax, not change the whole logic of the system.

But if there is a command to "start a service/application on a station" it's logical that there is one to stop it. Just that.

@malex984
Copy link
Member Author

I see. Sorry for confusion!

@elondaits Please check the updated Hilbert CLI.
Does it look minimal enough?

Oleksandr Motsak and others added 8 commits December 1, 2016 06:25
Added remote execution via ssh/scp + Dummy client script
Usage output:

hilbert-station - Station part for Linux systems
Usage:
    hilbert-station -h                      Display this help message.
    hilbert-station -v                      Increase verbosity
    hilbert-station -q                      Decrease verbosity
    hilbert-station -p                      Pedantic mode
    hilbert-station -V                      Display version info.
    hilbert-station start                   Start everything
    hilbert-station stop                    Stop everything and shutdown
    hilbert-station prepare <new_cfg>       Install a new local configuration
    hilbert-station app_switch <app_id>     Change the currently running top application to specified one
    hilbert-station dm_start <vm_name>      Start a VM using docker-machine
= changes in config/hilbert_cli_config.py:
  * _execute,
  + Host::ssh/Host::scp
  + Station::type: StationType: [hidden, server, standard?, standalone?]
  * fixes / logging

= extended Hilbert::parse testing (test_validate.py): singleHostHilbert.yml
Added locking, SubCommands use legacy client scripts
Updated client scripts: prepare/default/finishall/luncher seem to work
TODO: check.fix topswitch.sh!
NOTE: temporary include docker-compose.yml for completeness
hilbert Server-side tool:
 - Better general Logging in
 * disabled checking docker-compose-file during verification as it takes too long at HITS :-(
 * removed general timeout option to _execute

Client tool:
 + More locations for Lockfile (special lock group on Fedora...)
 * Try sudo if simple shutdown fails...

TODO:
 - repair x11vnc,
 - fix OGL.tgz generation (for NVIdia OpenGL)
 - When should we generate OGL.tgz?
@malex984 malex984 self-assigned this Dec 9, 2016
Updates and Fixes due to (initial) testing installation and its presentation
@malex984 malex984 changed the title Configuration validator Hilbert YAML configuration parser and Hilbert Server/Client-side tools Dec 9, 2016
@malex984 malex984 merged commit b9c5031 into devel Dec 9, 2016
@malex984 malex984 deleted the feature/config_validate branch December 9, 2016 06:56
@malex984
Copy link
Member Author

malex984 commented Dec 9, 2016

Note: remaining implementation-related issues will be collected in (#14)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants