Merge lp:~allenap/maas-test/configure-abstract into lp:maas-test

Proposed by Gavin Panella
Status: Rejected
Rejected by: Graham Binns
Proposed branch: lp:~allenap/maas-test/configure-abstract
Merge into: lp:maas-test
Prerequisite: lp:~allenap/maas-test/refactor-prep
Diff against target: 576 lines (+226/-136)
7 files modified
docs/man/maas-test.8.rst (+47/-17)
maastest/cases.py (+39/-16)
maastest/main.py (+20/-10)
maastest/parser.py (+75/-74)
maastest/utils.py (+3/-0)
man/maas-test.8 (+42/-17)
packages.txt (+0/-2)
To merge this branch: bzr merge lp:~allenap/maas-test/configure-abstract
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) Needs Fixing
Review via email: mp+196453@code.launchpad.net

Commit message

Use abstract properties to make the test cases configuration clearer.

Description of the change

The TestMAAS.configure() call had become vestigial with the passing of the raw arguments object. I've removed it, and replaced the configurable properties with abstract properties. This way we can configure the subclass in a natural way, without extra function-calling boilerplate, but we also document the meaning of the configuration, and we will encounter errors if we forget to provide the necessary configuration.

I've also removed the prepare_parser() call as an unnecessary indirection. Argument parsers are not mutated in practice, so we need only prepare one.

I've also changed --interactive-enlistment to --interactive. Firstly it was a lot to type. More importantly it's relevant when using the daemon mode, coming in a later branch. The daemon mode will have interactive elements (it's more of a server than a "proper" daemon really). The daemon mode may be delayed - it has been de-prioritised - but I'd like to switch to this spelling of the flag now, in preparation.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

There are definitely good things here: avoiding the weird dynamic setting of a class variable, renaming --interactive-enlistment to --interactive, and the new help string for the --interactive option.

I also like having documentation for the arguments, but the abstract properties are a tail wagging its dog. Instead of defining an option in parser.py and the manpage, we'd have to define them in parser.py, cases.py, main.py and the manpage. K.I.S.S.! Forget just the abstract property and you violate the design but nothing actually happens; forget just the bit in ConfiguredTestMAAS and you get a traceback to remind you to define the abstract property; forget both and you violate the design but get a different traceback without the reminder. It's bad enough that manpages are generally like that, but in the code it's easy to avoid.

The option definitions on the arguments parser are effectively documentation in themselves, which if anything just needs to be brought to the fore. The args object encapsulates them in a way that makes them easy to identify as arguments. That's actually a pretty good setup — light, flexible, and clear. All in one place, so consistency comes naturally without enforcement. If you want to isolate and document the options that are relevant to test scenarios, I'd suggest sticking very close to the existing "args" pattern. Once you have cross-module inheritance relationships separating a field's specification, use, and definition, you're no longer any better off than with simple cross-module delegation.

Nor do I agree with inlining prepare_parser() into module-level code. A declarative list of argparse.Action as a module-level variable would make sense — though redundant if abstract properties actually serve their purpose of documenting those options! — but I fail to see any advantage in making these method calls side effects of importing the module. Without reliable policing of __all__, module-level code at zero indentation just gives me lots more items to read that may hide effective interface points for the module. Doing it this way just makes it awkward to test properties like “the parser accepts this command line specified by the original product design,” or “this particular mistake on the command line leads to this user-friendly error message.”

One other thing: main(), in main.py, has imports in unexpected places. Explain why those are necessary, or move them to the expected places.

review: Needs Fixing
59. By Gavin Panella

Merged refactor-prep into configure-abstract, resolving conflicts.

60. By Gavin Panella

Snapshot changed man page.

61. By Gavin Panella

Add argument group for image selection.

62. By Gavin Panella

Improve metavars.

63. By Gavin Panella

Not using testresources right now.

64. By Gavin Panella

Merged refactor-prep into configure-abstract, resolving conflicts.

65. By Gavin Panella

Merged refactor-prep into configure-abstract, resolving conflicts.

66. By Gavin Panella

Switch more options to using abstract properties.

67. By Gavin Panella

Move proxy options into their own argument group.

68. By Gavin Panella

Add some TODOs.

Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (6.1 KiB)

Thanks for the review. I disagree with most of it! But let's see where
we can go from here, and if I can win you over :)

> There are definitely good things here: avoiding the weird dynamic setting of a
> class variable, renaming --interactive-enlistment to --interactive, and the
> new help string for the --interactive option.
>
> I also like having documentation for the arguments, but the abstract
> properties are a tail wagging its dog.  Instead of defining an option in
> parser.py and the manpage, we'd have to define them in parser.py, cases.py,
> main.py and the manpage.  K.I.S.S.!  Forget just the abstract property and you
> violate the design but nothing actually happens;

Except that your tests crash when they refer to
{self,cls}.missing_attribute.

>                                                  forget just the bit in
> ConfiguredTestMAAS and you get a traceback to remind you to define the
> abstract property;

Which happens early; no need to wait for a VM to fire up, etc.

>                    forget both and you violate the design but get a different
> traceback without the reminder.

Back to test crashes when they refer to {self,cls}.missing_attribute?

>                                  It's bad enough that manpages are generally
> like that, but in the code it's easy to avoid.

The abstract properties, once defined in TestMAAS (or a subclass), mean
that we document TestMAAS's requirements, and avert wasteful runs to
determine that we forgot to add a command-line argument to populate the
args object that we refer to in the last-but-one test case, for example.
Locality: those properties define what we need closer to where they're
needed.

>
> The option definitions on the arguments parser are effectively documentation
> in themselves, which if anything just needs to be brought to the fore.  The
> args object encapsulates them in a way that makes them easy to identify as
> arguments.  That's actually a pretty good setup — light, flexible, and clear.
> All in one place, so consistency comes naturally without enforcement.

Enforcement is useful here, and it's not clear as it is. The args object
is a pin-cushion, and does nothing more to identify arguments as such
than the abstract properties.

>                                                                       If you
> want to isolate and document the options that are relevant to test scenarios,
> I'd suggest sticking very close to the existing "args" pattern.  Once you have
> cross-module inheritance relationships separating a field's specification,
> use, and definition, you're no longer any better off than with simple cross-
> module delegation.

The abstract properties mean that we break the cross-module dependency
between maastest.cases and maastest.parser; the same names may be used
for convenience and consistency, but TestMAAS is no longer bound
directly to the command-line.

We also add a formal contract with maastest.main, in that it cannot
instantiate TestMAAS or any of its subclasses without providing values
for the missing properties.

>
> Nor do I agree with inlining prepare_parser() into module-level code.  A
> declarative list of argparse.Action as a module-level...

Read more...

69. By Gavin Panella

Update the man page from the --help output.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Actually I think we really just disagree about one important thing: whether forgetting to define an abstract property for one of the options will produce an appropriate error. If it does, I generally agree with your considerations. But my position is: no it doesn't, because any references the methods make to these properties are bound when they execute, not when they are defined. And by the time they execute, these properties have been set. No error, no enforcement.

But we're in luck: we can just try it! Which is what I'm doing right now. I removed the abstractproperty definitions, all of them for good measure, though I kept the base class abstract. I hope you'll agree that's a good, extreme example of forgetting to define some. Tests pass. Running maas-test seems to work just fine so far. At the time of writing it's trying to import boot images, at which point you'll agree it must have noticed if there were a problem reading the proxy, series, and architecture options if nothing else.

70. By Gavin Panella

Merge trunk, resolving conflicts.

71. By Gavin Panella

Update man page.

Revision history for this message
Gavin Panella (allenap) wrote :

> Actually I think we really just disagree about one important thing: whether
> forgetting to define an abstract property for one of the options will produce
> an appropriate error. If it does, I generally agree with your considerations.
> But my position is: no it doesn't, because any references the methods make to
> these properties are bound when they execute, not when they are defined. And
> by the time they execute, these properties have been set. No error, no
> enforcement.
>
> But we're in luck: we can just try it! Which is what I'm doing right now. I
> removed the abstractproperty definitions, all of them for good measure, though
> I kept the base class abstract. I hope you'll agree that's a good, extreme
> example of forgetting to define some. Tests pass. Running maas-test seems to
> work just fine so far. At the time of writing it's trying to import boot
> images, at which point you'll agree it must have noticed if there were a
> problem reading the proxy, series, and architecture options if nothing else.

I didn't argue that forgetting to define an abstract property would
cause an error. I argued that forgetting to set that property in a
subclass would cause an error, which it does. Removing the abstract
properties from TestMAAS doesn't demonstrate anything particularly
useful.

The purpose of the abstract properties is to define TestMAAS's
requirements close to their use. Try removing one of the command-line
options, or forgetting to set one of the properties on
ConfiguredTestMAAS, then you'll get an early warning. The args pattern
displays neither of those properties; you'll only ever get "late"
breakage.

72. By Gavin Panella

Switch over to disable_cache.

73. By Gavin Panella

Merge trunk, resolving lots of conflicts.

74. By Gavin Panella

Fix lint.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Now you have me utterly confused. You quoted what I said:

> Forget just the abstract property and you
> violate the design but nothing actually happens;

And your response to that was:

> Except that your tests crash when they refer to
> {self,cls}.missing_attribute.

And I'm telling you that nothing crashes, as long as you don't _also_ forget to set the property in ConfiguredTestMAAS. If you forget the latter, it crashes either way. But you can forget just the abstract property and everything still works. And that makes the enforcement rather weak!

Revision history for this message
Gavin Panella (allenap) wrote :

> Now you have me utterly confused. You quoted what I said:
>
> > Forget just the abstract property and you
> > violate the design but nothing actually happens;
>
> And your response to that was:
>
> > Except that your tests crash when they refer to
> > {self,cls}.missing_attribute.
>
> And I'm telling you that nothing crashes, as long as you don't _also_ forget
> to set the property in ConfiguredTestMAAS. If you forget the latter, it
> crashes either way. But you can forget just the abstract property and
> everything still works. And that makes the enforcement rather weak!

Okay, I misunderstood your point there; I had assumed you meant not
providing a setting for the abstract property in the subclass. If you
forget to define an abstract property you're hardly better off than you
are now.

However, the point of the abstract properties is that they're nearby the
code that needs them. Right now you must marry up all references to
cls.args.* or self.args.* in maastest.cases with the command-line
options defined in maastest.parser.

It is an improvement, and that's usually our measure for what may land.

75. By Gavin Panella

Merge trunk, resolving conflicts.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

The mere fact that we can have a misunderstanding about which of the definitions of the same option I'm talking about speaks volumes. That complexity is a steep price to pay when there is no particularly profound divide between program options and test parameters which we need to enforce or document.

And aside from that divide between options and parameters, what does it protect us from? If we stick to a single args object, there is really just one mistake you can make with a test parameter: inconsistency between its use and its definition in the args parser. Simple, lightweight, flexible, obvious.

With the "wide interface" to parameters which you're creating here, there's a lot more to go wrong. Now it's the argparser, abstract properties, initialisations, and uses that all need to be consistent. There are now several failure modes for different (combinations of) inconsistencies. How many of those are improvements? You're increasing the odds of failing early, but at the cost of increasing the odds of failing at all.

I suppose the real problem is that it can be slow and difficult to discover mistakes in accessing those options from the main code. A current-day linter won't tell us when we're going to access undefined parameters. The abstract properties won't help with that either; they only provide a redundancy check on the copying of the parameters. But this is properly a matter of test coverage. Isn't the logical solution to address that, rather than add red tape in the hope that it will trip up mistakes? For example, I see no test to cover the instantiation of ConfiguredTestMAAS that would report any missing initialisations. If we could just pass realistic args objects to unit tests, mistakes in parameter access should be easy to beat.

Revision history for this message
Raphaël Badin (rvb) wrote :

I had a quick look at the code and read the ragging discussion.

My two cents: I tend to think Jeroen's point is justified: adding these abstract properties simply adds another thing you have to modify each time you want to add a new command which translates into a property. The argument that adding these properties would speed up the debugging when someone makes a mistake is only half valid: if, for instance, one has two mutually exclusive properties, only proper testing can detect this kind of problem.

Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (4.0 KiB)

What I see as a fairly obvious improvement has been voted down because
it's too convenient to tightly couple the command-line to TestMAAS.

One of the dependent branches of this already breaks that: it adds a
--daemon option which is nothing to do with TestMAAS; it's an option to
select a different test suite class, among other things.

Now two things would be coupled closely to the command-line, and
TestMAAS will be free to use args.daemon if it so chooses, and perhaps
someone will make it do that one day, thus starting the process of
spaghettification.

Do you not see that this is bad?

> The mere fact that we can have a misunderstanding about which of the
> definitions of the same option I'm talking about speaks volumes.

I just misunderstood what you wrote, not the concepts involved.

> That complexity is a steep price to pay when there is no particularly
> profound divide between program options and test parameters which we
> need to enforce or document.

That's short-sighted.

> And aside from that divide between options and parameters, what does
> it protect us from? If we stick to a single args object, there is
> really just one mistake you can make with a test parameter:
> inconsistency between its use and its definition in the args
> parser. Simple, lightweight, flexible, obvious.

Simple: Briefly, while there's still a 1:1 correspondence between the
command-line and TestMAAS.

Lightweight: Indeed, but I've not proposed anything heavyweight.

Flexible: It's so flexible you can point straight ahead and still shoot
your toes off.

Obvious: As in, it's obvious it can be improved?

> With the "wide interface" to parameters which you're creating here,
> there's a lot more to go wrong. Now it's the argparser, abstract
> properties, initialisations, and uses that all need to be
> consistent. There are now several failure modes for different
> (combinations of) inconsistencies. How many of those are improvements?
> You're increasing the odds of failing early, but at the cost of
> increasing the odds of failing at all.

Come on, this is specious. The abstract properties are provided by a
package in the standard library, and they're fairly simple anyway. The
failure modes are part of the design, to fail fast so that we know early
that there's a programming error.

> I suppose the real problem is that it can be slow and difficult to
> discover mistakes in accessing those options from the main code. A
> current-day linter won't tell us when we're going to access undefined
> parameters. The abstract properties won't help with that either; they
> only provide a redundancy check on the copying of the parameters.

As I've said several times, these parameters are defined close to where
they're used. Locality is generally a desirable property. It makes us
less likely to forget or misuse, and helps our future selves and others
understand our intentions.

> But this is properly a matter of test coverage. Isn't the logical
> solution to address that, rather than add red tape in the hope that it
> will trip up mistakes?

I was able to do this change in a short amount of time. Adding tests to
cover every use of the args object would have taken a lot longe...

Read more...

76. By Gavin Panella

Merge trunk, resolving conflicts.

77. By Gavin Panella

Merge trunk.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

I'm sorry that feelings are flaring up about this... Yes, of course I do see that spaghettification from inappropriate mixing of program options and test parameters is bad. I'm sure we all do. I just don't see it as worse, in the near future for this limited short-term project, than the cost of detailed gatekeeping.

In other words, I think this is a case where short-sightedness is appropriate for the situation. As things change, separating program options from test parameters may become more useful. But you've got to give the patterns a chance to emerge before you set them in stone. An option like --daemon does not make a pattern, especially if you're worried that actually it may still prove relevant to the test cases. Do we have a good reason to believe that review will not be enough to stop the spaghettification?

We keep mis-understanding each other about this. That in itself worries me. This is of course not what I meant:

> Come on, this is specious. The abstract properties are provided by a
> package in the standard library, and they're fairly simple anyway. The
> failure modes are part of the design, to fail fast so that we know early
> that there's a programming error.

I'm not saying abstract properties in general have to be consistent with our option definitions. Only the abstract properties we actually define for this. It has nothing to do with the standard library.

I would love to have early catching of mistakes, but the only kind of mistake that I see being caught early here is inconsistency between the abstract property definitions and the initialisations in ConfiguredTestMAAS. But it's a mistake which the existing design doesn't make possible in the first place.

You argue that testing all uses of the args object would be costly. My view is that the real cost is in testing our code at all, which we should have been doing anyway. The tests can just say "make me an args object," with perhaps some customised fields — just like we do in our customary test factories. I imagine you had much the same in mind for testing with the abstract properties.

Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (5.5 KiB)

> I'm sorry that feelings are flaring up about this...  Yes, of course I do see
> that spaghettification from inappropriate mixing of program options and test
> parameters is bad.  I'm sure we all do.  I just don't see it as worse, in the
> near future for this limited short-term project, than the cost of detailed
> gatekeeping.

I think that the status-quo is worse, but even if I'm wrong, we're not
speculating: this code is done, and it's simply not "detailed
gatekeeping".

>
> In other words, I think this is a case where short-sightedness is appropriate
> for the situation.  As things change, separating program options from test
> parameters may become more useful.  But you've got to give the patterns a
> chance to emerge before you set them in stone.

We're not setting anything in stone. Decoupling TestMAAS from the
command-line is the opposite.

> An option like --daemon does not make a pattern, especially if you're
> worried that actually it may still prove relevant to the test cases.
> Do we have a good reason to believe that review will not be enough to
> stop the spaghettification?

Yes, it's already started: the args object is being passed through to
ConfiguredTestMAAS.configure().

>
> We keep mis-understanding each other about this.  That in itself worries me.
> This is of course not what I meant:
>
> > Come on, this is specious. The abstract properties are provided by a
> > package in the standard library, and they're fairly simple anyway. The
> > failure modes are part of the design, to fail fast so that we know early
> > that there's a programming error.
>
> I'm not saying abstract properties in general have to be consistent with our
> option definitions.  Only the abstract properties we actually define for this.
> It has nothing to do with the standard library.
>
> I would love to have early catching of mistakes, but the only kind of mistake
> that I see being caught early here is inconsistency between the abstract
> property definitions and the initialisations in ConfiguredTestMAAS.  But it's
> a mistake which the existing design doesn't make possible in the first place.

A run will fail eventually, true, when it tries to deference a missing
argument. That's our ultimate measure of correctness in this regard.
This proposed change moves some of these possible failures close to the
start of a run. That's an improvement.

>
> You argue that testing all uses of the args object would be costly.  My view
> is that the real cost is in testing our code at all, which we should have been
> doing anyway.  The tests can just say "make me an args object," with perhaps
> some customised fields — just like we do in our customary test factories.  I
> imagine you had much the same in mind for testing with the abstract
> properties.

The tests could also say "make me a testcase", and the abstract
properties would be there to ensure that nothing had been omitted.
That's a small improvement.

Here is the essence of the code that we've been arguing about for a
week:

{{{
=== modified file 'maastest/cases.py'
--- maastest/cases.py   2013-11-28 17:09:31 +0000
+++ maastest/cases.py   2013-12-02 09:44:27 +0000
@@ -42,1 +45,1 @@

class TestMAAS(testto...

Read more...

78. By Gavin Panella

Merge trunk, resolving conflicts.

79. By Gavin Panella

Merge trunk, resolving one conflict.

Revision history for this message
Raphaël Badin (rvb) wrote :

I have the feeling we have reached a stalemate here… I know it's not a great feeling when one has invested time in a branch to see it rejected but, well, it happens. I'm afraid the only solution here is to agree to disagree on this, mark this branch rejected, and move on. Gavin?

Revision history for this message
Graham Binns (gmb) wrote :

I'm going to mark this branch as rejected just to clear it out of the way. Sorry Gavin; it's great work but we've pretty much reached an impasse (and besides, this branch will be bit-rotten by now).

Unmerged revisions

79. By Gavin Panella

Merge trunk, resolving one conflict.

78. By Gavin Panella

Merge trunk, resolving conflicts.

77. By Gavin Panella

Merge trunk.

76. By Gavin Panella

Merge trunk, resolving conflicts.

75. By Gavin Panella

Merge trunk, resolving conflicts.

74. By Gavin Panella

Fix lint.

73. By Gavin Panella

Merge trunk, resolving lots of conflicts.

72. By Gavin Panella

Switch over to disable_cache.

71. By Gavin Panella

Update man page.

70. By Gavin Panella

Merge trunk, resolving conflicts.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'docs/man/maas-test.8.rst'
--- docs/man/maas-test.8.rst 2013-11-28 11:06:23 +0000
+++ docs/man/maas-test.8.rst 2013-12-05 16:31:01 +0000
@@ -50,7 +50,7 @@
50a route to the internet.50a route to the internet.
5151
52For the node, that means that both the node's own network interface card (NIC)52For the node, that means that both the node's own network interface card (NIC)
53and its baseboard management controller (BMC) should be on the testing network. 53and its baseboard management controller (BMC) should be on the testing network.
54The testing system only needs one NIC on the testing network.54The testing system only needs one NIC on the testing network.
5555
56In addition to being on the testing network, the testing system must also have56In addition to being on the testing network, the testing system must also have
@@ -75,7 +75,8 @@
75testing network. You will be passing this interface to `maas-test`.75testing network. You will be passing this interface to `maas-test`.
7676
775. Depending on caching, the test may download and store large amounts of data775. Depending on caching, the test may download and store large amounts of data
78on the testing system. Make sure you have sufficient disk space and network bandwidth.78on the testing system. Make sure you have sufficient disk space and network
79bandwidth.
7980
80The data that needs downloading and/or caching consists mostly of system images81The data that needs downloading and/or caching consists mostly of system images
81for the virtual machine, and for booting the node. As a rule of thumb, count82for the virtual machine, and for booting the node. As a rule of thumb, count
@@ -100,9 +101,42 @@
100Options101Options
101=======102=======
102103
104Positional arguments:
105---------------------
106
107interface
108 The interface on which MAAS will provide DHCP. This is the machine's
109 interface on which the node under test should be plugged-in.
110
111Optional arguments:
112-------------------
113
103-h, --help114-h, --help
104 Show help and exit.115 Show help and exit.
105116
117--interactive
118 Interactive mode. Instead of powering up the node automatically through
119 IPMI, prompt the user to turn it on manually. In this mode there is no need
120 to specify BMC details; the MAAS enlistment process will discover them
121 automatically.
122
123--archive=archive
124 Optional package repository name. If given, the virtual machine may install
125 packages from this additional archive as well as from the main Ubuntu
126 archive. The archive is added to the virtual machine using
127 'add-apt-repository'; it may be either a line in the format of apt's
128 sources.list, or a personal package archive identifier in the form
129 'ppa:<user>/<ppa-name>', or a distribution component that should be enabled.
130 This is typically used to test recent versions of MAAS that are only
131 available in a PPA such as "ppa:maas-maintainers/dailybuilds".
132
133
134BMC details:
135------------
136
137BMC/IPMI details for performing the first power on of the node under test.
138Required when --interactive has not been specified.
139
106--bmc-mac=MAC140--bmc-mac=MAC
107 MAC address for the node's baseboard management controller. MAAS will141 MAC address for the node's baseboard management controller. MAAS will
108 control the node's power and boot sequence through this controller.142 control the node's power and boot sequence through this controller.
@@ -127,21 +161,11 @@
127 Username for IPMI authentication. Use with **--bmc-password**.161 Username for IPMI authentication. Use with **--bmc-password**.
128 Not needed in interactive mode.162 Not needed in interactive mode.
129163
130--interactive164
131 Interactive mode. Instead of powering up the node automatically through165MAAS images:
132 IPMI, prompt the user to turn it on manually. In this mode there is no need166------------
133 to specify BMC details; the MAAS enlistment process will discover them167
134 automatically.168Series and architecture choices pertaining to the node-under-test.
135
136--archive=archive
137 Optional package repository name. If given, the virtual machine may install
138 packages from this additional archive as well as from the main Ubuntu
139 archive. The archive is added to the virtual machine using
140 'add-apt-repository'; it may be either a line in the format of apt's
141 sources.list, or a personal package archive identifier in the form
142 'ppa:<user>/<ppa-name>', or a distribution component that should be enabled.
143 This is typically used to test recent versions of MAAS that are only
144 available in a PPA such as "ppa:maas-maintainers/dailybuilds".
145169
146--series=codename170--series=codename
147 Code name for the Ubuntu release series that should be run on the node during171 Code name for the Ubuntu release series that should be run on the node during
@@ -154,6 +178,12 @@
154 which defaults to `generic`, so e.g. `i386/generic` may be abbreviated to178 which defaults to `generic`, so e.g. `i386/generic` may be abbreviated to
155 `i386`. The default architecture is `amd64`.179 `i386`. The default architecture is `amd64`.
156180
181
182HTTP caching proxies:
183---------------------
184
185Save bandwidth and time, especially when repeatedly invoking maas-test.
186
157--http-proxy=URL187--http-proxy=URL
158 Use the given HTTP proxy for all downloads, both on the testing system and on188 Use the given HTTP proxy for all downloads, both on the testing system and on
159 the nodes: KVM images, MAAS boot images, and Ubuntu packages. Like189 the nodes: KVM images, MAAS boot images, and Ubuntu packages. Like
160190
=== modified file 'maastest/cases.py'
--- maastest/cases.py 2013-11-28 17:09:31 +0000
+++ maastest/cases.py 2013-12-05 16:31:01 +0000
@@ -14,6 +14,10 @@
14 "TestOneNode",14 "TestOneNode",
15 ]15 ]
1616
17from abc import (
18 ABCMeta,
19 abstractproperty,
20 )
17import httplib21import httplib
18import json22import json
19import logging23import logging
@@ -25,7 +29,6 @@
25from maastest.maas_enums import NODE_STATUS29from maastest.maas_enums import NODE_STATUS
26from maastest.maasfixture import MAASFixture30from maastest.maasfixture import MAASFixture
27from maastest.proxyfixture import LocalProxyFixture31from maastest.proxyfixture import LocalProxyFixture
28import testresources
29import testtools32import testtools
30from testtools.testcase import gather_details33from testtools.testcase import gather_details
3134
@@ -49,12 +52,32 @@
49 raise52 raise
5053
5154
52class TestMAAS(testtools.TestCase, testresources.ResourcedTestCase):55class TestMAAS(testtools.TestCase):
53 """Base class for testing machine compatibility with MAAS."""56 """Base class for testing machine compatibility with MAAS."""
5457
55 @classmethod58 __metaclass__ = ABCMeta
56 def configure(cls, args):59
57 cls.args = args60 interface = abstractproperty(doc=(
61 "The interface on which MAAS will provide DHCP."))
62 interactive = abstractproperty(doc=(
63 "Run interactively, prompting the user for input as necessary."))
64
65 bmc_mac = abstractproperty(doc=(
66 "The MAC address of the node's BMC."))
67 bmc_username = abstractproperty(doc=(
68 "The username to use when authenticating with the node's BMC."))
69 bmc_password = abstractproperty(doc=(
70 "The password to use when authenticating with the node's BMC."))
71
72 series = abstractproperty(doc=(
73 "Ubuntu series to use for enlistment and commissioning."))
74 architecture = abstractproperty(doc=(
75 "Architecture of the node being tested."))
76
77 http_proxy = abstractproperty(doc=(
78 "Optionally, an HTTP proxy to use, defined as a URL."))
79 disable_cache = abstractproperty(doc=(
80 "Don't attempt to cache downloaded resources."))
5881
59 fixtures = ClassFixture()82 fixtures = ClassFixture()
6083
@@ -64,12 +87,12 @@
64 # TODO: series and architecture should be script parameters.87 # TODO: series and architecture should be script parameters.
65 architecture = utils.determine_vm_architecture()88 architecture = utils.determine_vm_architecture()
6689
67 if cls.args.http_proxy is not None:90 if cls.http_proxy is not None:
68 # The user has passed an external proxy; don't start polipo,91 # The user has passed an external proxy; don't start polipo,
69 # just use that proxy for everything.92 # just use that proxy for everything.
70 proxy_url = cls.args.http_proxy93 proxy_url = cls.http_proxy
71 logging.info("Using external proxy %s." % proxy_url)94 logging.info("Using external proxy %s." % proxy_url)
72 elif cls.args.disable_cache:95 elif cls.disable_cache:
73 # The user has passed --disable-cache, so don't start polipo96 # The user has passed --disable-cache, so don't start polipo
74 # and don't try to use an external proxy.97 # and don't try to use an external proxy.
75 proxy_url = ''98 proxy_url = ''
@@ -83,13 +106,13 @@
83106
84 cls.machine = KVMFixture(107 cls.machine = KVMFixture(
85 series='saucy', architecture=architecture,108 series='saucy', architecture=architecture,
86 proxy_url=proxy_url, direct_interface=cls.args.interface)109 proxy_url=proxy_url, direct_interface=cls.interface)
87 cls.fixtures.addCleanup(delattr, cls, "machine")110 cls.fixtures.addCleanup(delattr, cls, "machine")
88 cls.fixtures.useFixture(cls.machine)111 cls.fixtures.useFixture(cls.machine)
89112
90 cls.maas = MAASFixture(113 cls.maas = MAASFixture(
91 cls.machine, proxy_url=proxy_url, series=cls.args.series,114 cls.machine, proxy_url=proxy_url, series=cls.series,
92 architecture=cls.args.architecture)115 architecture=cls.architecture)
93 cls.fixtures.addCleanup(delattr, cls, "maas")116 cls.fixtures.addCleanup(delattr, cls, "maas")
94 cls.fixtures.useFixture(cls.maas)117 cls.fixtures.useFixture(cls.maas)
95118
@@ -163,7 +186,7 @@
163 """186 """
164 # TODO: only print this in debug/verbose mode.187 # TODO: only print this in debug/verbose mode.
165 self.print_connection_details()188 self.print_connection_details()
166 if self.args.interactive:189 if self.interactive:
167 self.power_up_node_interactive()190 self.power_up_node_interactive()
168 else:191 else:
169 self.power_up_node_noninteractive()192 self.power_up_node_noninteractive()
@@ -182,13 +205,13 @@
182 power_address = self.find_bmc_ip_address()205 power_address = self.find_bmc_ip_address()
183 self.maas.kvm_fixture.run_command([206 self.maas.kvm_fixture.run_command([
184 'ipmipower', '-h', power_address,207 'ipmipower', '-h', power_address,
185 '-u', self.args.bmc_username,208 '-u', self.bmc_username,
186 '-p', self.args.bmc_password, '--off'],209 '-p', self.bmc_password, '--off'],
187 check_call=True)210 check_call=True)
188 self.maas.kvm_fixture.run_command([211 self.maas.kvm_fixture.run_command([
189 'ipmipower', '-h', power_address,212 'ipmipower', '-h', power_address,
190 '-u', self.args.bmc_username,213 '-u', self.bmc_username,
191 '-p', self.args.bmc_password, '--on'],214 '-p', self.bmc_password, '--on'],
192 check_call=True)215 check_call=True)
193216
194 def find_bmc_ip_address(self):217 def find_bmc_ip_address(self):
195218
=== modified file 'maastest/main.py'
--- maastest/main.py 2013-12-05 14:02:09 +0000
+++ maastest/main.py 2013-12-05 16:31:01 +0000
@@ -21,8 +21,8 @@
2121
2222
23def main(argv=None):23def main(argv=None):
24 from maastest.parser import prepare_parser24 from maastest.parser import parser
25 args = prepare_parser().parse_args()25 args = parser.parse_args(argv)
2626
27 import logging27 import logging
28 logging.basicConfig(28 logging.basicConfig(
@@ -41,18 +41,28 @@
41 "poorer performance than on physical hardware.")41 "poorer performance than on physical hardware.")
4242
43 from maastest.cases import TestOneNode43 from maastest.cases import TestOneNode
44 from maastest.console import run_console
4445
45 class ConfiguredTestMAAS(TestOneNode):46 class ConfiguredTestMAAS(TestOneNode):
46 """A configured version of TestInteractiveOneNode.47 """A configured test case for testing a node's MAAS compatibility.
4748
48 ConfiguredTestMAAS is a TestMAAS use to configure it by calling49 `TestMAAS` has a number of abstract properties; it cannot be
49 cls.configure. We need to configure the class itself and not50 instantiated until those properties are defined.
50 the instance because the KVMFixture TestMAAS instanciates and needs
51 to configure is created at the class level.
52 """51 """
53 ConfiguredTestMAAS.configure(args)52
5453 interface = args.interface
55 from maastest.console import run_console54 interactive = args.interactive
55
56 bmc_mac = args.bmc_mac
57 bmc_username = args.bmc_username
58 bmc_password = args.bmc_password
59
60 series = args.series
61 architecture = args.architecture
62
63 http_proxy = args.http_proxy
64 disable_cache = args.disable_cache
65
56 # This try/except is a band-aid to make sure the fixtures gets cleaned66 # This try/except is a band-aid to make sure the fixtures gets cleaned
57 # up when a KeyboardInterrupt exception is raised.67 # up when a KeyboardInterrupt exception is raised.
58 # This does miss out on test-level and module-level tearDowns.68 # This does miss out on test-level and module-level tearDowns.
5969
=== modified file 'maastest/parser.py'
--- maastest/parser.py 2013-11-29 13:22:37 +0000
+++ maastest/parser.py 2013-12-05 16:31:01 +0000
@@ -11,7 +11,7 @@
1111
12__metaclass__ = type12__metaclass__ = type
13__all__ = [13__all__ = [
14 "prepare_parser",14 "parser",
15 ]15 ]
1616
17import argparse17import argparse
@@ -23,8 +23,8 @@
2323
24class MAASTestArgumentParser(argparse.ArgumentParser):24class MAASTestArgumentParser(argparse.ArgumentParser):
2525
26 def parse_args(self):26 def parse_args(self, argv=None):
27 args = super(MAASTestArgumentParser, self).parse_args()27 args = super(MAASTestArgumentParser, self).parse_args(argv)
28 bmc_details = (args.bmc_mac, args.bmc_username, args.bmc_password)28 bmc_details = (args.bmc_mac, args.bmc_username, args.bmc_password)
29 if args.interactive:29 if args.interactive:
30 if bmc_details != (None, None, None):30 if bmc_details != (None, None, None):
@@ -47,74 +47,75 @@
47 return args47 return args
4848
4949
50latest_LTS_series = distro_info.UbuntuDistroInfo().lts()50# TODO: This is not tested since how maas-test will interface with
5151# checkbox is still in flux. Right now, this is just to get the
5252# testing going.
53def prepare_parser():53parser = MAASTestArgumentParser(
54 # TODO: This is not tested sinc how maas-test will interface with54 description="Test whether or not a node is compatible with MAAS.")
55 # checkbox is still in flux. Right now, this is just to get the55
56 # testing going.56parser.add_argument(
57 parser = MAASTestArgumentParser(57 'interface', type=text_type,
58 description="Test whether or not a node is compatible with MAAS.")58 help="Network interface that connects the testing system to the "
5959 "dedicated testing network. MAAS will serve DHCP on this "
60 parser.add_argument(60 "interface.")
61 'interface', type=text_type,61# TODO: Actually use the archive value.
62 help="Network interface that connects the testing system to the "62parser.add_argument(
63 "dedicated testing network. MAAS will serve DHCP on this "63 '--archive', type=text_type, default=None,
64 "interface.")64 help="Additional personal package archive, 'sources.list' entry, or "
65 parser.add_argument(65 "distribution component to install on the virtual MAAS system.")
66 '--archive', type=text_type, default=None,66parser.add_argument(
67 help="Additional personal package archive, 'sources.list' entry, or "67 '--interactive', action='store_true',
68 "distribution component to install on the virtual MAAS system.")68 help="Interactive mode. Prompt the user to power up the node "
69 parser.add_argument(69 "manually instead of doing it automatically through the BMC.")
70 '--interactive', action='store_true',70
71 help="Interactive mode. Prompt the user to power up the node "71bmc_details = parser.add_argument_group(
72 "manually instead of doing it automatically through the BMC.")72 "bmc details", "BMC/IPMI details for performing the first power on of "
7373 "the node under test. Required when --interactive has not been "
74 # BMC details.74 "specified.")
75 parser.add_argument(75bmc_details.add_argument(
76 '--bmc-mac', type=text_type,76 '--bmc-mac', type=text_type, metavar="MAC",
77 help="MAC address of the node's baseboard management controller. "77 help="MAC address of the node's baseboard management controller. "
78 "Use this if the BMC is connected to the interface given as "78 "Use this if the BMC is connected to the interface given as "
79 "argument. In non-interactive mode, either --bmc-mac or "79 "argument. In non-interactive mode, either --bmc-mac or "
80 "--bmc-ip. Not needed in interactive mode.")80 "--bmc-ip. Not needed in interactive mode.")
81 parser.add_argument(81bmc_details.add_argument(
82 '--bmc-ip', type=text_type,82 '--bmc-ip', type=text_type, metavar="IP",
83 help="IP address of the node's baseboard management controller. "83 help="IP address of the node's baseboard management controller. "
84 "Use this if the BMC is not connected to the inteface given as "84 "Use this if the BMC is not connected to the inteface given as "
85 "argument. Note that the IP address must not change for the "85 "argument. Note that the IP address must not change for the "
86 "duration of the testing. In non-interactive mode, either "86 "duration of the testing. In non-interactive mode, either "
87 "--bmc-mac or --bmc-ip. Not needed in interactive mode.")87 "--bmc-mac or --bmc-ip. Not needed in interactive mode.")
88 parser.add_argument(88bmc_details.add_argument(
89 '--bmc-username', type=text_type,89 '--bmc-username', type=text_type, metavar="USERNAME",
90 help="Username for authenticating to the node's BMC. "90 help="Username for authenticating to the node's BMC. "
91 "Not needed in interactive mode.")91 "Not needed in interactive mode.")
92 parser.add_argument(92bmc_details.add_argument(
93 '--bmc-password', type=text_type,93 '--bmc-password', type=text_type, metavar="PASSWORD",
94 help="Password for authenticating to the node's BMC. "94 help="Password for authenticating to the node's BMC. "
95 "Not needed in interactive mode.")95 "Not needed in interactive mode.")
9696
97 # MAAS images.97maas_images = parser.add_argument_group(
98 parser.add_argument(98 "maas images", "Series and architecture choices pertaining to the "
99 '--series', type=text_type,99 "node-under-test.")
100 choices=utils.get_supported_series(),100maas_images.add_argument(
101 default=latest_LTS_series,101 '--series', type=text_type,
102 help="Code name for the Ubuntu release series that the node should "102 choices=utils.get_supported_series(),
103 "run. Defaults to the latest LTS (%(default)s).")103 default=distro_info.UbuntuDistroInfo().lts(),
104 parser.add_argument(104 help="Code name for the Ubuntu release series that the node should "
105 '--architecture', type=text_type,105 "run. Defaults to the latest LTS (%(default)s).")
106 default='amd64',106maas_images.add_argument(
107 help="Node's CPU architecture, e.g. armhf/highbank or amd64. "107 '--architecture', type=text_type, default='amd64',
108 "Defaults to %(default)s.")108 help="Node's CPU architecture, e.g. arm/highbank or amd64. "
109109 "Defaults to %(default)s.")
110 # Proxy.110
111 parser.add_argument(111proxy_options = parser.add_argument_group(
112 '--http-proxy', type=text_type,112 "http proxies & caching", "Save bandwidth and time, especially when "
113 help="HTTP proxy to use for all downloads. Imples --disable-cache.")113 "repeatedly invoking %(prog)s.")
114 parser.add_argument(114proxy_options.add_argument(
115 '--disable-cache', action='store_true',115 '--http-proxy', type=text_type, metavar="URL",
116 help="By default, maas-test uses polipo to cache all KVM images, "116 help="HTTP proxy to use for all downloads. Imples --disable-cache.")
117 "MAAS images and apt packages. If --disable-cache is specified, "117proxy_options.add_argument(
118 "polipo will be disabled.")118 '--disable-cache', action='store_true',
119119 help="By default, maas-test uses polipo to cache all KVM images, "
120 return parser120 "MAAS images and apt packages. If --disable-cache is specified, "
121 "polipo will be disabled.")
121122
=== modified file 'maastest/utils.py'
--- maastest/utils.py 2013-11-29 12:38:02 +0000
+++ maastest/utils.py 2013-12-05 16:31:01 +0000
@@ -253,4 +253,7 @@
253def get_user_input(message):253def get_user_input(message):
254 """A wrapper around raw_input() that allows us to sanely mock it.254 """A wrapper around raw_input() that allows us to sanely mock it.
255 """255 """
256 # TODO: Find out why it's not possible to sanely mock raw_input()
257 # without this.
258 # TODO: Don't use this unless --interactive has been set.
256 return input(message)259 return input(message)
257260
=== modified file 'man/maas-test.8'
--- man/maas-test.8 2013-11-29 05:30:32 +0000
+++ man/maas-test.8 2013-12-05 16:31:01 +0000
@@ -91,7 +91,8 @@
91testing network. You will be passing this interface to \fImaas\-test\fP\&.91testing network. You will be passing this interface to \fImaas\-test\fP\&.
92.sp92.sp
935. Depending on caching, the test may download and store large amounts of data935. Depending on caching, the test may download and store large amounts of data
94on the testing system. Make sure you have sufficient disk space and network bandwidth.94on the testing system. Make sure you have sufficient disk space and network
95bandwidth.
95.sp96.sp
96The data that needs downloading and/or caching consists mostly of system images97The data that needs downloading and/or caching consists mostly of system images
97for the virtual machine, and for booting the node. As a rule of thumb, count98for the virtual machine, and for booting the node. As a rule of thumb, count
@@ -112,11 +113,41 @@
112to power up the node.113to power up the node.
113.UNINDENT114.UNINDENT
114.SH OPTIONS115.SH OPTIONS
116.SS Positional arguments:
117.INDENT 0.0
118.TP
119.B interface
120The interface on which MAAS will provide DHCP. This is the machine\(aqs
121interface on which the node under test should be plugged\-in.
122.UNINDENT
123.SS Optional arguments:
115.INDENT 0.0124.INDENT 0.0
116.TP125.TP
117.B \-h\fP,\fB \-\-help126.B \-h\fP,\fB \-\-help
118Show help and exit.127Show help and exit.
119.TP128.TP
129.B \-\-interactive
130Interactive mode. Instead of powering up the node automatically through
131IPMI, prompt the user to turn it on manually. In this mode there is no need
132to specify BMC details; the MAAS enlistment process will discover them
133automatically.
134.TP
135.BI \-\-archive\fB= archive
136Optional package repository name. If given, the virtual machine may install
137packages from this additional archive as well as from the main Ubuntu
138archive. The archive is added to the virtual machine using
139\(aqadd\-apt\-repository\(aq; it may be either a line in the format of apt\(aqs
140sources.list, or a personal package archive identifier in the form
141\(aqppa:<user>/<ppa\-name>\(aq, or a distribution component that should be enabled.
142This is typically used to test recent versions of MAAS that are only
143available in a PPA such as "ppa:maas\-maintainers/dailybuilds".
144.UNINDENT
145.SS BMC details:
146.sp
147BMC/IPMI details for performing the first power on of the node under test.
148Required when \-\-interactive has not been specified.
149.INDENT 0.0
150.TP
120.BI \-\-bmc\-mac\fB= MAC151.BI \-\-bmc\-mac\fB= MAC
121MAC address for the node\(aqs baseboard management controller. MAAS will152MAC address for the node\(aqs baseboard management controller. MAAS will
122control the node\(aqs power and boot sequence through this controller.153control the node\(aqs power and boot sequence through this controller.
@@ -140,22 +171,11 @@
140.BI \-\-bmc\-user\fB= user171.BI \-\-bmc\-user\fB= user
141Username for IPMI authentication. Use with \fB\-\-bmc\-password\fP\&.172Username for IPMI authentication. Use with \fB\-\-bmc\-password\fP\&.
142Not needed in interactive mode.173Not needed in interactive mode.
143.TP174.UNINDENT
144.B \-\-interactive175.SS MAAS images:
145Interactive mode. Instead of powering up the node automatically through176.sp
146IPMI, prompt the user to turn it on manually. In this mode there is no need177Series and architecture choices pertaining to the node\-under\-test.
147to specify BMC details; the MAAS enlistment process will discover them178.INDENT 0.0
148automatically.
149.TP
150.BI \-\-archive\fB= archive
151Optional package repository name. If given, the virtual machine may install
152packages from this additional archive as well as from the main Ubuntu
153archive. The archive is added to the virtual machine using
154\(aqadd\-apt\-repository\(aq; it may be either a line in the format of apt\(aqs
155sources.list, or a personal package archive identifier in the form
156\(aqppa:<user>/<ppa\-name>\(aq, or a distribution component that should be enabled.
157This is typically used to test recent versions of MAAS that are only
158available in a PPA such as "ppa:maas\-maintainers/dailybuilds".
159.TP179.TP
160.BI \-\-series\fB= codename180.BI \-\-series\fB= codename
161Code name for the Ubuntu release series that should be run on the node during181Code name for the Ubuntu release series that should be run on the node during
@@ -167,6 +187,11 @@
167architecture only. The architecture may include a sub\-architecture name,187architecture only. The architecture may include a sub\-architecture name,
168which defaults to \fIgeneric\fP, so e.g. \fIi386/generic\fP may be abbreviated to188which defaults to \fIgeneric\fP, so e.g. \fIi386/generic\fP may be abbreviated to
169\fIi386\fP\&. The default architecture is \fIamd64\fP\&.189\fIi386\fP\&. The default architecture is \fIamd64\fP\&.
190.UNINDENT
191.SS HTTP caching proxies:
192.sp
193Save bandwidth and time, especially when repeatedly invoking maas\-test.
194.INDENT 0.0
170.TP195.TP
171.BI \-\-http\-proxy\fB= URL196.BI \-\-http\-proxy\fB= URL
172Use the given HTTP proxy for all downloads, both on the testing system and on197Use the given HTTP proxy for all downloads, both on the testing system and on
173198
=== modified file 'packages.txt'
--- packages.txt 2013-11-28 07:46:00 +0000
+++ packages.txt 2013-12-05 16:31:01 +0000
@@ -8,7 +8,6 @@
8python-netaddr8python-netaddr
9python-netifaces9python-netifaces
10python-six10python-six
11python-testresources
12python-testtools11python-testtools
13python-virtualenv12python-virtualenv
14python-xdg13python-xdg
@@ -17,7 +16,6 @@
17python3-mock16python3-mock
18python3-netaddr17python3-netaddr
19python3-six18python3-six
20python3-testresources
21python3-testtools19python3-testtools
22python3-xdg20python3-xdg
23qemu-kvm21qemu-kvm

Subscribers

People subscribed via source and target branches