diff --git a/QUICKSTART b/QUICKSTART new file mode 100644 index 000000000..5903165cb --- /dev/null +++ b/QUICKSTART @@ -0,0 +1,42 @@ +This is at this point essentially a demo of xCAT 2.0 concepts. + +Currently, all it can do is the set of out-of-band management of IPMI systems. + +To see how it works: +./rpminstallperlxcat +./rpminstallserver +(at this point it will list failed dependencies probably, have fun filling those, +SLES10 includes the SSL one, and SLES10 sdk has perl-XML-Simple, +the details of making these deps easy is a TODO for 1.3 release) +./rpminstallclient + + +Now that it is installed, setup of the security for 1.2 like operation: +/usr/share/xcat/scripts/setup-xcat-ca.sh +(put whatever common name you want) +/usr/share/xcat/scripts/setup-server-cert.sh +(name something for your server, say y twice) +/usr/share/xcat/scripts/setup-local-client.sh +(use the name root for now) + +chtab key=xcatdport site.value=3002 (using 3002 so you can use it on an xcat 1.2 server without worry) + +(now assuming setup for a node called node1 with a bmc called node1-bmc) +chtab node=node1 nodelist.groups=ipmi +chtab node=ipmi nodehm.mgt=ipmi ipmi.bmc='/\z/-bmc/' + +(start xcatd) +xcatd & (the warnings are not actually useful to correct now, so don't worry) + +export XCATHOST=localhost:3002 +testclient power node1 stat + +(should work, number of commands will work, obviously the name indicates I haven't bother with the shorter +rpower/rinv/rvitals names yet, but it will come, also obviously since the client can do everything, +it will be busybox like most likely) + +To add another node2 with same name scheme as node1: +chtab node=node2 nodelist.groups=ipmi +(can immediately start rpowering) + + diff --git a/doc/configuration.pod b/doc/configuration.pod new file mode 100644 index 000000000..f1b73e80e --- /dev/null +++ b/doc/configuration.pod @@ -0,0 +1,27 @@ +=pod + +=head1 xCAT configuration core architecture + +xCAT configuration spans a number of tables, some with records associated with particular nodes (such as nodelist and nodehm) and others that do not have a direct relationship with any given node. + +The tables not associated with a given node are straightforward, the data is stored and retrieved as-is from the database without interpretation, and without any configuration layer inheritance (though some calling code may have an internal concept of inheritance with such fields, the core configuration will have no facilities to explicitly aid this. + +The tables with records typically retrieved by node name have some extra features to enable a more template-based style to be used: + +=over + +=item * + +Any tagname can be used in lieu of a node name under the node field, and that record will then be taken to be applicable to any node with that tag. If a field is requested for a specific node, and either a record doesn't exist specifically for that nodename or a record exists, but has no definition for the requested field, that nodes tags are then used to search for general level records. If multiple records could apply from two different tags, the precedence is TBD. This is nearly identical to most xCAT 1.x tab file conventions. This is useful in tables such as noderes, where typical configurations have exactly the same field values for large sets of nodes. + +=item * + +xCAT 2 extends the above to be made useful where a field will vary for every node with a given tag, but in ways that would be trivial to describe. If a field is of the format /I/I/, it is taken to be a perl regular expression, to be performed on the nodename. For example, the bmc field of the ipmi table might be /\z/-bmc/ for a record with node=ipmi to specify that the BMC hostname is derived by appending -bmc to the end of the nodename of every node tagged ipmi. This is useful in tables such as ipmi. + +=item * + +As an extension to the above, a regular expression extended with arithmetic operators is available, by using the format |I|I|. This behaves similarly to the above, but () enclosed parts in I are taken to signify arithmetic operations and substituted in. All operations are integer arithmetic, so 5/4 would come out as 1. The typical perl positional variables are available in such expressions. For example, the mpa field of the mp table for node=blade might be |\D+(\d+)|bc((${1}-1)/14+1)|. Specifically in this case, if you had a node named blade572 tagged blade, the 572 would be set to ${1} by the left hand parentheses, and on the right hand, that would become bc((572-1/14+1)), and finally evaluated and returned as bc41. To complete the example, the id field of mp would be |\D+(\d+)|((${1}-1)%14+1)|, which upon evaluation indicates slot 12. This is useful for tables that call out infrastructure devices/port numbers, such as switch, nodehm (for terminal servers), and mp (for IBM Bladecenter configurations). + +=back + +An administrator may chose to ignore any and all of the functions. For example, an administrator may chose to only make use of the first mentioned feature, and preserve an xCAT 1.x layout for their tables. diff --git a/makeclientrpm b/makeclientrpm new file mode 100755 index 000000000..654862185 --- /dev/null +++ b/makeclientrpm @@ -0,0 +1,29 @@ +#!/bin/ksh + +OSNAME=$(uname) + +if [ "$OSNAME" = "AIX" ] +then + + tar -cvf xCAT-client-2.0.tar xCAT-client-2.0 + gzip xCAT-client-2.0.tar + cp xCAT-client-2.0.tar.gz /opt/freeware/src/packages/SOURCES + cd ./xCAT-client-2.0 + rm /opt/freeware/src/packages/RPMS/ppc/xCAT-client-2.0*rpm + rpm -ba xCAT-client.spec + #rpm -Uvh /opt/freeware/src/packages/RPMS/ppc/xCAT-client-2.0*rpm + +else + + if [ -f /etc/redhat-release ] + then + pkg="redhat" + else + pkg="packages" + fi + + tar czvf xCAT-client-2.0.tar.gz xCAT-client-2.0; + rm /usr/src/$pkg/RPMS/noarch/xCAT-client-2.0*rpm + rpmbuild -ta xCAT-client-2.0.tar.gz ; + #rpm -Uvh /usr/src/$pkg/RPMS/noarch/xCAT-client-2.0*rpm +fi diff --git a/makeperlxcatrpm b/makeperlxcatrpm new file mode 100755 index 000000000..4b106140a --- /dev/null +++ b/makeperlxcatrpm @@ -0,0 +1,28 @@ +#!/bin/ksh + +OSNAME=$(uname) + +if [ "$OSNAME" = "AIX" ] +then + + tar -cvf perl-xCAT-2.0.tar perl-xCAT-2.0 + gzip perl-xCAT-2.0.tar + cp perl-xCAT-2.0.tar.gz /opt/freeware/src/packages/SOURCES + cd ./perl-xCAT-2.0 + rm /opt/freeware/src/packages/RPMS/ppc/perl-xCAT-2.0*rpm + rpm -ba perl-xCAT.spec + #rpm -Uvh /opt/freeware/src/packages/RPMS/ppc/perl-xCAT-2.0*rpm + +else + if [ -f /etc/redhat-release ] + then + pkg="redhat" + else + pkg="packages" + fi + + tar --exclude .svn -czvf perl-xCAT-2.0.tar.gz perl-xCAT-2.0/; + rm /usr/src/$pkg/RPMS/noarch/perl-xCAT-2.0*rpm + rpmbuild -ta perl-xCAT-2.0.tar.gz ; + #rpm -Uvh /usr/src/$pkg/RPMS/noarch/perl-xCAT-2.0*rpm +fi diff --git a/makeserverrpm b/makeserverrpm new file mode 100755 index 000000000..ba041dc38 --- /dev/null +++ b/makeserverrpm @@ -0,0 +1,28 @@ +#!/bin/ksh + +OSNAME=$(uname) + +if [ "$OSNAME" = "AIX" ] +then + tar -cvf xCAT-server-2.0.tar xCAT-server-2.0 + gzip xCAT-server-2.0.tar + cp xCAT-server-2.0.tar.gz /opt/freeware/src/packages/SOURCES + cd ./xCAT-server-2.0 + rm /opt/freeware/src/packages/RPMS/ppc/xCAT-server-2.0*rpm + rpm -ba xCAT-server.spec + #rpm -Uvh /opt/freeware/src/packages/RPMS/ppc/xCAT-server-2.0*rpm + +else + + if [ -f /etc/redhat-release ] + then + pkg="redhat" + else + pkg="packages" + fi + + tar czvf xCAT-server-2.0.tar.gz xCAT-server-2.0; + rm /usr/src/$pkg/RPMS/noarch/xCAT-server-2.0*rpm + rpmbuild -ta xCAT-server-2.0.tar.gz ; + #rpm -Uvh /usr/src/$pkg/RPMS/noarch/xCAT-server-2.0*rpm +fi diff --git a/perl-xCAT-2.0/LICENSE.html b/perl-xCAT-2.0/LICENSE.html new file mode 100644 index 000000000..83d0eebb0 --- /dev/null +++ b/perl-xCAT-2.0/LICENSE.html @@ -0,0 +1,326 @@ + + + + + + + +Eclipse Public License - Version 1.0 + + + + + + +
+ +

Eclipse Public License - v 1.0 +

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER +THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, +REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE +OF THIS AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) +in the case of the initial Contributor, the initial code and documentation +distributed under this Agreement, and
+b) in the case of each subsequent Contributor:

+ +

i) +changes to the Program, and

+ +

ii) +additions to the Program;

+ +

where +such changes and/or additions to the Program originate from and are distributed +by that particular Contributor. A Contribution 'originates' from a Contributor +if it was added to the Program by such Contributor itself or anyone acting on +such Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in conjunction +with the Program under their own license agreement, and (ii) are not derivative +works of the Program.

+ +

"Contributor" means any person or +entity that distributes the Program.

+ +

"Licensed Patents " mean patent +claims licensable by a Contributor which are necessarily infringed by the use +or sale of its Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions +distributed in accordance with this Agreement.

+ +

"Recipient" means anyone who +receives the Program under this Agreement, including all Contributors.

+ +

2. GRANT OF RIGHTS

+ +

a) +Subject to the terms of this Agreement, each Contributor hereby grants Recipient +a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly +display, publicly perform, distribute and sublicense the Contribution of such +Contributor, if any, and such derivative works, in source code and object code +form.

+ +

b) +Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free +patent license under Licensed Patents to make, use, sell, offer to sell, import +and otherwise transfer the Contribution of such Contributor, if any, in source +code and object code form. This patent license shall apply to the combination +of the Contribution and the Program if, at the time the Contribution is added +by the Contributor, such addition of the Contribution causes such combination +to be covered by the Licensed Patents. The patent license shall not apply to +any other combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) +Recipient understands that although each Contributor grants the licenses to its +Contributions set forth herein, no assurances are provided by any Contributor +that the Program does not infringe the patent or other intellectual property +rights of any other entity. Each Contributor disclaims any liability to Recipient +for claims brought by any other entity based on infringement of intellectual +property rights or otherwise. As a condition to exercising the rights and +licenses granted hereunder, each Recipient hereby assumes sole responsibility +to secure any other intellectual property rights needed, if any. For example, +if a third party patent license is required to allow Recipient to distribute +the Program, it is Recipient's responsibility to acquire that license before +distributing the Program.

+ +

d) +Each Contributor represents that to its knowledge it has sufficient copyright +rights in its Contribution, if any, to grant the copyright license set forth in +this Agreement.

+ +

3. REQUIREMENTS

+ +

A Contributor may choose to distribute the +Program in object code form under its own license agreement, provided that: +

+ +

a) +it complies with the terms and conditions of this Agreement; and

+ +

b) +its license agreement:

+ +

i) +effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose;

+ +

ii) +effectively excludes on behalf of all Contributors all liability for damages, +including direct, indirect, special, incidental and consequential damages, such +as lost profits;

+ +

iii) +states that any provisions which differ from this Agreement are offered by that +Contributor alone and not by any other party; and

+ +

iv) +states that source code for the Program is available from such Contributor, and +informs licensees how to obtain it in a reasonable manner on or through a +medium customarily used for software exchange.

+ +

When the Program is made available in source +code form:

+ +

a) +it must be made available under this Agreement; and

+ +

b) a +copy of this Agreement must be included with each copy of the Program.

+ +

Contributors may not remove or alter any +copyright notices contained within the Program.

+ +

Each Contributor must identify itself as the +originator of its Contribution, if any, in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

Commercial distributors of software may +accept certain responsibilities with respect to end users, business partners +and the like. While this license is intended to facilitate the commercial use +of the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes the +Program in a commercial product offering, such Contributor ("Commercial +Contributor") hereby agrees to defend and indemnify every other +Contributor ("Indemnified Contributor") against any losses, damages and +costs (collectively "Losses") arising from claims, lawsuits and other +legal actions brought by a third party against the Indemnified Contributor to +the extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may participate +in any such claim at its own expense.

+ +

For example, a Contributor might include the +Program in a commercial product offering, Product X. That Contributor is then a +Commercial Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance claims and +warranties are such Commercial Contributor's responsibility alone. Under this +section, the Commercial Contributor would have to defend claims against the +other Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, +WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and distributing the +Program and assumes all risks associated with its exercise of rights under this +Agreement , including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs or +equipment, and unavailability or interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF +THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES.

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid +or unenforceable under applicable law, it shall not affect the validity or +enforceability of the remainder of the terms of this Agreement, and without +further action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Program itself (excluding combinations of the Program with +other software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the date +such litigation is filed.

+ +

All Recipient's rights under this Agreement +shall terminate if it fails to comply with any of the material terms or +conditions of this Agreement and does not cure such failure in a reasonable +period of time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute +copies of this Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The Agreement +Steward reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement Steward has +the right to modify this Agreement. The Eclipse Foundation is the initial +Agreement Steward. The Eclipse Foundation may assign the responsibility to +serve as the Agreement Steward to a suitable separate entity. Each new version +of the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version of +the Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly stated +in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to +the intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved.

+ +

This Agreement is governed by the laws of the +State of New York and the intellectual property laws of the United States of +America. No party to this Agreement will bring a legal action under this +Agreement more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation.

+ +

 

+ +
+ + + + diff --git a/perl-xCAT-2.0/Makefile.PL b/perl-xCAT-2.0/Makefile.PL new file mode 100644 index 000000000..3f0df876d --- /dev/null +++ b/perl-xCAT-2.0/Makefile.PL @@ -0,0 +1,5 @@ +use ExtUtils::MakeMaker; +WriteMakefile( + 'NAME' => 'xCAT', + 'VERSION' => '2.0', + ); diff --git a/perl-xCAT-2.0/perl-xCAT.spec b/perl-xCAT-2.0/perl-xCAT.spec new file mode 100644 index 000000000..d2556f92b --- /dev/null +++ b/perl-xCAT-2.0/perl-xCAT.spec @@ -0,0 +1,62 @@ +Summary: xCAT perl libraries +Name: perl-xCAT +Version: 2.0 +Release: snap%(date +"%Y%m%d%H%M") +License: EPL +Group: System Environment/Libraries +Source: perl-xCAT-2.0.tar.gz +Packager: IBM Corp. +Vendor: IBM Corp. +Distribution: %{?_distribution:%{_distribution}}%{!?_distribution:%{_vendor}} +Prefix: %{_prefix} +BuildRoot: /var/tmp/%{name}-%{version}-%{release}-root +%ifos linux +BuildArch: noarch +%endif + +Provides: perl(xCAT) = %{version} + +%description +Provides perl xCAT libraries for core functionality. Required for all xCAT installations. +Includes xCAT::Table, xCAT::NodeRange, among others. + +%prep +%setup -q -n perl-xCAT-%{version} + +%build +perl Makefile.PL +%{__make} %{?mflags} + +%install +%{__make} install DESTDIR=$RPM_BUILD_ROOT %{?mflags_install} +test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT/%{_datadir} ||: +test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT/%{_libdir}/perl5/5* ||: + +find %{buildroot} -name "perllocal.pod" \ + -o -name ".packlist" \ + -o -name "*.bs" \ + |xargs -i rm -f {} + +# ndebug - this seems to break the AIX build - need to investigate +%ifos linux +find %{buildroot}%{_prefix} \ + -type d -depth \ + -exec rmdir {} \; 2>/dev/null +%endif + +find $RPM_BUILD_ROOT -type f | sed -e "s@$RPM_BUILD_ROOT@/@" > files.list + +%clean +test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT + +%files -f files.list +%defattr(-, root, root) +%doc LICENSE.html + +%changelog +* Wed May 2 2007 - Norm Nott nott@us.ibm.com +- Made changes to make this work on AIX + +* Wed Jan 24 2007 Jarrod Johnson +-It begins + diff --git a/perl-xCAT-2.0/xCAT/Client.pm b/perl-xCAT-2.0/xCAT/Client.pm new file mode 100644 index 000000000..0adeba7f6 --- /dev/null +++ b/perl-xCAT-2.0/xCAT/Client.pm @@ -0,0 +1,380 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT::Client; +use xCAT::NodeRange; +use IO::Socket::SSL; +use XML::Simple; +use Data::Dumper; +use Storable qw(dclone); +my $xcathost='localhost:3001'; +my $plugins_dir='/usr/lib/xcat/plugins'; +my %resps; +1; + + +################################# +# submit_request will take an xCAT command and pass it to the xCAT +# server for execution. +# +# If the XCATBYPASS env var is set, the connection to the server/daemon +# will be bypassed and the plugin will be called directly. If it is +# set to one or more directories (separated by ":"), all perl modules +# in those directories will be loaded in as plugins (for duplicate +# commands, last one in wins). If it is set to any other value +# (e.g. "yes", "default", whatever string you want) the default plugin +# directory /usr/lib/xcat/plugins will be used. +# +# Input: +# Request hash - A hash ref containing the input command and args to be +# passed to the plugin. The the xcatd daemon (or this routine when +# XCATBYPASS) reads the {noderange} entry and builds a flattened array +# of nodes that gets added as request->{node} +# The format for the request hash is: +# { command => [ 'xcatcmd' ], +# noderange => [ 'noderange_string' ], +# arg => [ 'arg1', 'arg2', '...', 'argn' ] +# } +# Callback - A subroutine ref that will be called to process the output +# from the plugin. +# +# NOTE: The request hash will get converted to XML when passed to the +# xcatd daemon, and will get converted back to a hash before being +# passed to the plugin. The XMLin ForceArray option is used to +# force all XML constructs to be arrays so that the plugin code +# and callback routines can access the data consistently. +# The input request and the response hash created by the plugin should +# always create hashes with array values. +################################# +sub submit_request { + my $request = shift; + my $callback = shift; + +# If XCATBYPASS is set, invoke the plugin process_request method directly +# without going through the socket connection to the xcatd daemon + if ($ENV{XCATBYPASS}) { + # Load plugins from either specified or default dir + my %cmd_handlers; + my @plugins_dirs = split('\:',$ENV{XCATBYPASS}); + if (-d $plugins_dirs[0]) { + foreach (@plugins_dirs) { + $plugins_dir = $_; + scan_plugins(); + } + } else { + scan_plugins(); + } + + # don't do XML transformation -- assume request is well-formed + # my $xmlreq=XMLout($request,RootName=>xcatrequest,NoAttr=>1,KeyAttr=>[]); + # $request = XMLin($xmlreq,SuppressEmpty=>undef,ForceArray=>1) ; + + + # Call the plugin directly + # ${"xCAT_plugin::".$modname."::"}{process_request}->($request,$callback); + plugin_command($request,undef,$callback); + return 0; + } + +# No XCATBYPASS, so establish a socket connection with the xcatd daemon +# and submit the request + if ($ENV{XCATHOST}) { + $xcathost=$ENV{XCATHOST}; + } + my $client = IO::Socket::SSL->new( + PeerAddr => $xcathost, + SSL_key_file => $ENV{HOME}."/.xcat/client-key.pem", + SSL_cert_file => $ENV{HOME}."/.xcat/client-cert.pem", + SSL_ca_file => $ENV{HOME}."/.xcat/ca.pem", + SSL_use_cert => 1, + ); + die "Connection failure: $!\n" unless ($client); + my $msg=XMLout($request,RootName=>xcatrequest,NoAttr=>1,KeyAttr=>[]); + print $client $msg; + my $response; + my $rsp; + while (<$client>) { + $response .= $_; + if ($response =~ m/<\/xcatresponse>/) { + $rsp = XMLin($response,SuppressEmpty=>undef,ForceArray=>1); + $response=''; + if ($rsp->{warning}) { + printf ("Warning: ".$rsp->{warning}->[0]."\n"); + } + if ($rsp->{error}) { + printf "Error: ". $rsp->{error}->[0]."\n"; + } + $callback->($rsp); + if ($rsp->{serverdone}) { + last; + } + } + } + +################################### +# scan_plugins +# will load all plugin perl modules and build a list of supported +# commands +# +# NOTE: This is copied from xcatd (last merge 10/3/07). +# Will eventually move to using common source.... +################################### +sub scan_plugins { + my @plugins=glob($plugins_dir."/*.pm"); + foreach (@plugins) { + /.*\/([^\/]*).pm$/; + my $modname = $1; + require "$_"; + no strict 'refs'; + my $cmd_adds=${"xCAT_plugin::".$modname."::"}{handled_commands}->(); + foreach (keys %$cmd_adds) { + my $value = $_; + if (defined($cmd_handlers{$_})) { + my $add=1; + #This next bit of code iterates through the handlers. + #If the value doesn't contain an equal, and has an equivalent entry added by + # another plugin already, don't add (otherwise would hit the DB multiple times) + # a better idea, restructure the cmd_handlers as a multi-level hash + # prove out this idea real quick before doing that + foreach (@{$cmd_handlers{$_}}) { + if (($_->[1] eq $cmd_adds->{$value}) and (($cmd_adds->{$value} !~ /=/) or ($_->[0] eq $modname))) { + $add = 0; + } + } + if ($add) { push @{$cmd_handlers{$_}},[$modname,$cmd_adds->{$_}]; } + #die "Conflicting handler information from $modname"; + } else { + $cmd_handlers{$_} = [ [$modname,$cmd_adds->{$_}] ]; + } + } + } +} + + + +################################### +# plugin_command +# will invoke the correct plugin +# +# NOTE: This is copied from xcatd (last merge 10/3/07). +# Will eventually move to using common source.... +################################### +sub plugin_command { + my $req = shift; + my $sock = shift; + my $callback = shift; + my %handler_hash; +# use xCAT::NodeRange; + $Main::resps={}; + my @nodes; + if ($req->{node}) { + @nodes = @{$req->{node}}; + } elsif ($req->{noderange}) { + @nodes = noderange($req->{noderange}->[0]); + if (nodesmissed) { +# my $rsp = {errorcode=>1,error=>"Invalid nodes in noderange:".join(',',nodesmissed)}; + print "Invalid nodes in noderange:".join(',',nodesmissed); +# if ($sock) { +# print $sock XMLout($rsp,RootName=>'xcatresponse' ,NoAttr=>1); +# } +# return ($rsp); + return 1; + } + } + if (@nodes) { $req->{node} = \@nodes; } + if (defined($cmd_handlers{$req->{command}->[0]})) { + my $hdlspec; + foreach (@{$cmd_handlers{$req->{command}->[0]}}) { + $hdlspec =$_->[1]; + my $ownmod = $_->[0]; + if ($hdlspec =~ /:/) { #Specificed a table lookup path for plugin name + my $table; + my $cols; + ($table,$cols) = split(/:/,$hdlspec); + my @colmns=split(/,/,$cols); + my @columns; + my $hdlrtable=xCAT::Table->new($table); + unless ($hdlrtable) { + #TODO: proper error handling + } + my $node; + my $colvals = {}; + foreach my $colu (@colmns) { + if ($colu =~ /=/) { #a value redirect to a pattern/specific name + my $coln; my $colv; + ($coln,$colv) = split(/=/,$colu,2); + $colvals->{$coln} = $colv; + push (@columns,$coln); + } else { + push (@columns,$colu); + } + } + + foreach $node (@nodes) { + my $attribs = $hdlrtable->getNodeAttribs($node,\@columns); + unless (defined($attribs)) { next; } #TODO: This really ought to craft an unsupported response for this request + foreach (@columns) { + my $col=$_; + if (defined($attribs->{$col})) { + if ($colvals->{$col}) { #A pattern match style request. + if ($attribs->{$col} =~ /$colvals->{$col}/) { + $handler_hash{$ownmod}->{$node} = 1; + last; + } + } else { + $handler_hash{$attribs->{$col}}->{$node} = 1; + last; + } + } + } + } + } else { + unless (@nodes) { + $handler_hash{$hdlspec} = 1; + } + foreach (@nodes) { #Specified a specific plugin, not a table lookup + $handler_hash{$hdlspec}->{$_} = 1; + } + } + } + } else { + print "$req->{command}->[0] xCAT command not found \n"; + return 1; #TODO: error back that request has no known plugin for it + } + +## FOR NOW, DON'T FORK CHILD PROCESS TO MAKE BYPASS SIMPLER AND EASIER TO DEBUG +# my $children=0; +# $SIG{CHLD} = sub {while (waitpid(-1, WNOHANG) > 0) { $children--; } }; +# my $check_fds; +# if ($sock) { +# $check_fds = new IO::Select; +# } + foreach (keys %handler_hash) { + my $modname = $_; + if (-r $plugins_dir."/".$modname.".pm") { + require $plugins_dir."/".$modname.".pm"; +# $children++; +# my $pfd; #will be referenced for inter-process messaging. +# my $child; +# if ($sock) { #If $sock not passed in, don't fork.. +# socketpair($pfd, $parent_fd,AF_UNIX,SOCK_STREAM,PF_UNSPEC) or die "socketpair: $!"; +# #pipe($pfd,$cfd); +# $parent_fd->autoflush(1); +# $pfd->autoflush(1); +# $child = fork; +# } else { +# $child = 0; +# } +# unless (defined $child) { die "Fork failed"; } +# if ($child == 0) { +# if ($sock) { close $pfd; } + unless ($handler_hash{$_} == 1) { + my @nodes = sort {($a =~ /(\d+)/)[0] <=> ($b =~ /(\d+)/)[0] || $a cmp $b } (keys %{$handler_hash{$_}}); + $req->{node}=\@nodes; + } + no strict "refs"; + ${"xCAT_plugin::".$modname."::"}{process_request}->($req,$callback,\&do_request); +# if ($sock) { +# close($parent_fd); +# exit(0); +# } +# } else { +# close $parent_fd; +# $check_fds->add($pfd); +# } + } + } + unless ($sock) { return $Main::resps }; +# while ($children > 0) { +# relay_fds($check_fds,$sock); +# } +# #while (relay_fds($check_fds,$sock)) {} +# my %done; +# $done{serverdone} = {}; +# if ($req->{transid}) { +# $done{transid}=$req->{transid}->[0]; +# } +# if ($sock) { print $sock XMLout(\%done,RootName => 'xcatresponse',NoAttr=>1); } +} + + + +################################### +# do_request +# called from a plugin to execute another xCAT plugin command internally +# +# NOTE: This is copied from xcatd (last merge 10/3/07). +# Will eventually move to using common source.... +################################### +sub do_request { + my $req = shift; + my $second = shift; + my $rsphandler = \&build_response; + my $sock = undef; + if ($second) { + if (ref($second) eq "CODE") { + $rsphandler = $second; + } elsif (ref($second) eq "GLOB") { + $sock = $second; + } + } + + #my $sock = shift; #If no sock, will return a response hash + if ($cmd_handlers{$req->{command}->[0]}) { + return plugin_command($req,$sock,$rsphandler); + } elsif ($req->{command}->[0] eq "noderange" and $req->{noderange}) { + my @nodes = noderange($req->{noderange}->[0]); + my %resp; + if (nodesmissed) { + $resp{warning}="Invalid nodes in noderange:".join ',',nodesmissed; + } + $resp{serverdone} = {}; + @{$resp{node}}=@nodes; + if ($req->{transid}) { + $resp{transid}=$req->{transid}->[0]; + } + if ($sock) { + print $sock XMLout(\%resp,RootName => 'xcatresponse',NoAttr=>1); + } else { + return (\%resp); + } + } else { + my %resp=(error=>"Unsupported request"); + $resp{serverdone} = {}; + if ($req->{transid}) { + $resp{transid}=$req->{transid}->[0]; + } + if ($sock) { + print $sock XMLout(\%resp,RootName => 'xcatresponse',NoAttr=>1); + } else { + return (\%resp); + } + } +} + + +################################### +# build_response +# This callback handles responses from nested level plugin calls. +# It builds a merged hash of all responses that gets passed back +# to the calling plugin. +# Note: Need to create a "deep clone" of this response to add to the +# return, otherwise next time through the referenced data is overwritten +# +################################### +sub build_response { + my $rsp = shift; + foreach (keys %$rsp) { + my $subresp = dclone($rsp->{$_}); + push (@{$Main::resps->{$_}}, @{$subresp}); + } +} + + + +} + + + + + + diff --git a/perl-xCAT-2.0/xCAT/MacMap.pm b/perl-xCAT-2.0/xCAT/MacMap.pm new file mode 100755 index 000000000..aecfbe099 --- /dev/null +++ b/perl-xCAT-2.0/xCAT/MacMap.pm @@ -0,0 +1,277 @@ +#!/usr/bin/perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT::MacMap; +use xCAT::Table; +use IO::Select; +use IO::Handle; +use Sys::Syslog; +use Data::Dumper; +use SNMP; +openlog("MacMap",'','local0'); +my %cisco_vlans; #Special hash structure to reflect discovered VLANS on Cisco equip +#use IF-MIB (1.3.6.1.2.1.2) for all switches +# 1.3.6.1.2.1.31.1.1 - ifXtable +# 1.3.6.1.2.1.31.1.1.1.1.N = name - ifName +#Using BRIDGE-MIB for most switches( 1.3.6.1.2.1.17 ) +# 1.3.6.1.2.1.17.1.4 - dot1dBasePortTable +# 1.3.6.1.2.1.17.1.4.1.1.X = N - dot1dBasePort +# 1.3.6.1.2.1.17.4.3 - dot1dTpFdbTable #FAILS on FORCE10, +# +#If particular result fails, fallback to Q-BRIDGE-MIB for Force10 (1.3.6.1.2.1.17.7) +# 1.3.6.1.2.1.17.7.1.2.2 - dot1qTpFdbTable + + +sub namesmatch { +=pod + +MacMap attempts to do it's best to determine whether or not a particular SNMP description of +a port matches the user specified value in the configuration. Generally, if the configuration +consists of non-stacked switches without line cards, the user should only need to specify the +port number without any characters or / characters. If the configuration contains line cards +or stacked switches, use of that particular switch's appropriate / syntax in generally called +for. The exception being stacked SMC 8848 switches, in which all ports are still single +numbers, and the ports on the second switch begin at 57. + +If difficulty is encountered, or a switch is attempted with a format that doesn't match any +existing rule, it is recommended to use snmpwalk on the switch with the .1.3.6.1.2.1.31.1.1.1.1 +OID, and have the switch table port value match exactly the format suggested by that OID. + +=cut + my $namepercfg = shift; + my $namepersnmp = shift; + if ($namepercfg eq $namepersnmp) { + return 1; # They matched perfectly + } + #Begin guessing, first off, all tested scenarios have likely correct guesses ending + #in the cfg string, with some non-numeric prefix before it. + unless ($namepersnmp =~ /[^0123456789]$namepercfg\z/) { + #Most common case, won't match at all + return 0; + } + #stop contemplating vlan, Nu, and console interfaces + if (($namepersnmp =~ /vl/i) or ($namepersnmp =~ /Nu/) or ($namepersnmp =~ /onsole/)) { + return 0; + } + #broken up for code readablitiy, don't check port channel numbers or CPU + #have to distinguish betweer Port and Po and PortChannel + if (($namepersnmp !~ /Port #/) and ($namepersnmp !~ /Port\d/) and ($namepersnmp =~ /Po/) or ($namepersnmp =~ /LAG/) or ($namepersnmp =~ /CPU/)) { + return 0; + } + #don't contemplate ManagementEthernet + if (($namepersnmp =~ /Management/)) { + return 0; + } + #The blacklist approach has been exhausted. For now, assuming that means good, + #if something ambiguous happens, the whitelist would have been: + #'Port','Port #','/' (if namepercfg has no /, then / would be...), + #'Gi','Te','GigabitEthernet','TenGigabitEthernet' + return 1; +} + +sub new { + my $self = {}; + my $proto = shift; + my $class = ref($proto) || $proto; + + $self->{switchtab} = xCAT::Table->new('switch', -create => 1); + $self->{sitetab} = xCAT::Table->new('site'); + bless ($self, $class); + return $self; +} + +sub find_mac { +# This function is given a mac address, checks for given mac address +# and returns undef if unable to find the node, and the nodename otherwise + my $self = shift; + my $mac = shift; +# For now HARDCODE (TODO, configurable?) a cache as stale after five minutes +# Also, if things are changed in the config, our cache could be wrong, +# invalidate on switch table write? + if ($self->{mactable}->{lc($mac)} and ($self->{timestamp} > (time() - 300))) { + my $reftbl = 0; + foreach (keys %{$self->{mactable}}) { + if ((lc($mac) ne $_) and ($self->{mactable}->{lc($mac)} eq $self->{mactable}->{$_})) { + $reftbl = 1; + #The cache indicates confusion, flush it.. + } + } + unless ($reftbl) { return $self->{mactable}->{lc($mac)};} + } + $self->refresh_table; #not cached or stale cache, refresh + if ($self->{mactable}->{lc($mac)}) { + return $self->{mactable}->{lc($mac)}; + } + return undef; +} + +sub refresh_table { + $self->{mactable}={}; + my $self = shift; + my $community = "public"; + my $tmp = $self->{sitetab}->getAttribs({key=>'snmpc'},'value'); + if ($tmp and $tmp->{value}) { $community = $tmp->{value} } + else { #Would warn here.. + } + my %checked_pairs; + my @entries = $self->{switchtab}->getAllNodeAttribs(['port','switch']); + #Build hash of switch port names per switch + $self->{switches} = {}; + foreach $entry (@entries) { + $self->{switches}->{$entry->{switch}}->{$entry->{port}}=$entry->{node}; + } + my $children = 0; + my $inputs = new IO::Select; + $SIG{CHLD}= sub { while(waitpid(-1,WNOHANG) > 0) { $children-- } }; + foreach $entry (@entries) { + if ($checked_pairs{$entry->{switch}}) { + next; + } + $checked_pairs{$entry->{switch}}=1; + pipe my $child,my $parent; + $child->autoflush(1); + $parent->autoflush(1); + $children++; + $cpid = fork; + unless (defined $cpid) { die "Cannot fork" }; + if ($cpid == 0) { + close($child); + $self->refresh_switch($parent,$community,$entry->{switch}); + exit(0); + } + close($parent); + $inputs->add($child); + } + while($children) { + $self->handle_output($inputs); + } + while ($self->handle_output($inputs)) {}; #Drain the pipes + $self->{timestamp}=time; +} + +sub handle_output { + my $self = shift; + my $inputs = shift; + my @readied = $inputs->can_read(1); + my $rc = @readied; + my $ready; + foreach $ready (@readied) { + my $line = <$ready>; + unless ($line) { + $inputs->remove($ready); + close($ready); + next; + } + $line =~ m/^([^|]*)\|(.*)/; + $self->{mactable}->{$1}=$2; + } + return $rc; +} + +sub walkoid { + my $session = shift; + my $oid = shift; + my $retmap = undef; + my $varbind = new SNMP::Varbind([$oid,'']); + $session->getnext($varbind); + if ($session->{ErrorStr}) { + syslog("local6|err","Error communicating with ".$session->{DestHost}.": ".$session->{ErrorStr}); + return undef; + } + my $count=0; + while ($varbind->[0] =~ /^$oid\.?(.*)/) { + $count++; + if ($1) { + $retmap->{$1.".".$varbind->[1]}=$varbind->[2]; #If $1 is set, means key should + } else { + $retmap->{$varbind->[1]}=$varbind->[2]; #If $1 is set, means key should + } + + $session->getnext($varbind); + } + return $retmap; +} + + +sub refresh_switch { + my $self = shift; + my $output = shift; + my $community = shift; + my $switch = shift; + $session = new SNMP::Session( + DestHost => $switch, + Version => '1', + Community => $community, + UseNumeric => 1 + ); + #if ($error) { die $error; } + unless ($session) { syslog("err","Failed to communicate with $switch"); return; } + my $namemap = walkoid($session,'.1.3.6.1.2.1.31.1.1.1.1'); + unless ($namemap) { +# walkoid errored... + return; + } + #Above is valid without community string indexing, on cisco, we need it on the next one and onward + my $iftovlanmap = walkoid($session,'.1.3.6.1.4.1.9.9.68.1.2.2.1.2'); + my %vlans_to_check; + if (defined($iftovlanmap)) { #We have a cisco, the intelligent thing is to do SNMP gets on the ports +# that we can verify are populated per switch table + my $portid; + foreach $portid (keys %{$namemap}) { + my $portname; + my $switchport = $namemap->{$portid}; + foreach $portname (keys %{$self->{switches}->{$switch}}) { + unless (namesmatch($portname,$switchport)) { next } + $vlans_to_check{$iftovlanmap->{$portid}} = 1; + } + } + } else { + $vlans_to_check{'NA'}=1; + } + + my $vlan; + foreach $vlan (keys %vlans_to_check) { + unless ($vlan eq 'NA') { + $session = new SNMP::Session( + DestHost => $switch, + Version => '1', + Community => $community."@".$vlan, + UseNumeric => 1 + ); + } + unless ($session) { return; } + my $bridgetoifmap = walkoid($session,'.1.3.6.1.2.1.17.1.4.1.2'); # Good for all switches + # my $mactoindexmap = walkoid($session,'.1.3.6.1.2.1.17.4.3.1.2'); + my $mactoindexmap = walkoid($session,'.1.3.6.1.2.1.17.7.1.2.2.1.2'); + unless (defined($mactoindexmap)) { #if no qbridge defined, try bridge mib, probably cisco + #$mactoindexmap = walkoid($session,'.1.3.6.1.2.1.17.7.1.2.2.1.2'); + $mactoindexmap = walkoid($session,'.1.3.6.1.2.1.17.4.3.1.2'); + } #Ok, time to process the data + foreach my $oid (keys %$namemap) { + #$oid =~ m/1.3.6.1.2.1.31.1.1.1.1.(.*)/; + my $ifindex = $oid; + my $portname; + my $switchport = $namemap->{$oid}; + foreach $portname (keys %{$self->{switches}->{$switch}}) { # a little redundant, but + # computationally trivial + unless (namesmatch($portname,$switchport)) { next } + #if still running, we have match + foreach my $boid (keys %$bridgetoifmap) { + unless ($bridgetoifmap->{$boid} == $ifindex) { next; } + my $bridgeport = $boid; + foreach (keys %$mactoindexmap) { + if ($mactoindexmap->{$_} == $bridgeport) { + my @tmp = split /\./, $_; + my @mac = @tmp[-6 .. -1]; + printf $output "%02x:%02x:%02x:%02x:%02x:%02x|%s\n",@mac,$self->{switches}->{$switch}->{$portname}; + } + } + } + } + } + } +} + + + + +1; diff --git a/perl-xCAT-2.0/xCAT/MsgUtils.pm b/perl-xCAT-2.0/xCAT/MsgUtils.pm new file mode 100644 index 000000000..7a0ac469c --- /dev/null +++ b/perl-xCAT-2.0/xCAT/MsgUtils.pm @@ -0,0 +1,355 @@ +#!/usr/bin/perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html + +package xCAT::MsgUtils; + +use strict; + +use locale; +use Socket; +use File::Path; + +my $msgs; +my $distro; + +$::XCATLOG = "/var/log/xcat"; +$::NOK = -1; +$::OK = 0; +umask(0022); # This sets umask for all files so that group and world only + +#-------------------------------------------------------------------------------- + +=head1 xCAT::MsgUtils + + +=head2 Package Description + + +This program module file, supports the xcat messaging and logging + + + +=cut + +#-------------------------------------------------------------------------------- + +=head2 Package Dependancies + + use strict; + use Fcntl qw(:flock); + use File::Basename; + use File::Find; + use File::Path; # Provides mkpath() + + +=cut + +#-------------------------------------------------------------------------------- + +=head1 Subroutines by Functional Group + +=cut + +=head3 message + + Display a msg stdout, stderr, or a log file. + If callback routine is provide, the message will be returned to the callback + routine and logged, if logging is requested. + This function is primarily meant for commands and other code that is sending + output directly to the user. Even the log is really a capture of this + interactive output. + + Arguments: + The arguments of the message() function are: + + if $::VERBOSE is set, the message will be displayed. + + If $::LOG_FILE_HANDLE is set, the message goes to both + the screen and that log file. (Verbose msgs will be sent to + the log file even if $::VERBOSE is not set.) + A timestamp will automatically be put in front on any message + that is logged unless the T option is specified. + + if address of the call_back is provided, + then the message will be returned + as data to the call_back routine. + + - The argument is the message to be displayed/logged. + + + Here's the meaning of the 1st character: + I - informational goes to stdout + E - error. This type of message will be sent to stderr. + V - verbose. This message should only be displayed, + if $::VERBOSE is set. + T - Do not log timestamp, + + If $::LOG_FILE_HANDLE is set, the message goes to both + the screen and that log file. (Verbose msgs will be sent to + the log file even if $::VERBOSE is not set.) + A timestamp will automatically be put in front on any message + that is logged. + Optionally a T can be put before any of the above characters + "not" put a timestamp on + before the message when it is logged. + Note: T must be the first character. + + + Returns: + none + + Error: + _none + + Example: + xCAT::MsgUtils->message('E', "Operation $value1 failed\n"); + Use of T flag + xCAT::MsgUtils->message('TI', "Operation $value1 failed\n"); + Use of callback + xCAT::MsgUtils->message('I', $rsp,$call_back); + where $rsp is a data response structure for the callback function + + + Comments: + Returns: + none + + + +=cut + +#-------------------------------------------------------------------------------- + +sub message +{ + + # Process the arguments + shift; # get rid of the class name + my $sevcode = shift; + my $msg = shift; + my $call_back = shift; # optional + + # Parse the severity code + my $i = 1; + my $notimestamp = 0; + my $sev = substr($sevcode, 0, 1); + + # should be I, E, V, T + if ($sev eq 'T') # do not put timestamp + { + $notimestamp = 1; # no timestamp + $i = 2; # logically shift everything by 1 char + $sev = substr($sevcode, 1, 1); # now should be either I,E,V + } + + my $stdouterrf = \*STDOUT; + my $stdouterrd = ''; + if (my $sev =~ /[E]/) + { + $stdouterrf = \*STDERR; + $stdouterrd = '1>&2'; + } + + if (defined($::LOG_FILE_HANDLE)) + { + + if ($notimestamp == 0) + { # print a timestamp + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = + localtime(time); + my $time = $hour . ":" . $min . ":" . $sec . " "; + print $::LOG_FILE_HANDLE $time; + print $::LOG_FILE_HANDLE " "; + } + print $::LOG_FILE_HANDLE $msg; + } + + # if V option and $::VERBOSE set or not V option then display + # Note Verbose messages, will be thrown away if verbose is not + # turned on and there is no logging. + if (($sev eq 'V' && $::VERBOSE) || ($sev ne 'V')) + { + if ($::DSH_API) + { + $::DSH_API_MESSAGE = $::DSH_API_MESSAGE . $msg; + } + else + { + if ($call_back) { # callback routine provided + $call_back->($msg); + } else { + print $stdouterrf $msg; # print the message + + } + } + } + return 0; +} + +#-------------------------------------------------------------------------------- + +=head2 Message Logging Routines + +=cut + +#-------------------------------------------------------------------------------- + +#-------------------------------------------------------------------------------- + +=head3 backup_logfile + + Backup the current logfile. Move logfile to logfile.1. Shift all other logfiles + (logfile.[1-3]) up one number. The original logfile.4 is removed as in a FIFO. + + Arguments: + $logFileName + Returns: + $::OK + Error: + undefined + Example: + xCAT::MsgUtils->backup_logfile($logfile); + Comments: + Never used outside of ServerUtils. + +=cut + +#-------------------------------------------------------------------------------- + +sub backup_logfile +{ + my ($class, $logfile) = @_; + + my ($logfile1) = $logfile . ".1"; + my ($logfile2) = $logfile . ".2"; + my ($logfile3) = $logfile . ".3"; + my ($logfile4) = $logfile . ".4"; + + if (-f $logfile) + { + rename($logfile3, $logfile4) if (-f $logfile3); + rename($logfile2, $logfile3) if (-f $logfile2); + rename($logfile1, $logfile2) if (-f $logfile1); + rename($logfile, $logfile1); + } + return $::OK; +} + +#-------------------------------------------------------------------------------- + +=head3 start_logging + + Start logging messages to a logfile. Return the log file handle so it + can be used to close the file when done logging. + + Arguments: + $logFile + Returns: + $::LOG_FILE_HANDLE + Globals: + $::LOG_FILE_HANDLE + $::XCATLOG + Error: + $::NOK + Example: + xCAT::MsgUtils->start_logging($cfmupdatenode.log); + Comments: + Common method for logging script runtime output. + +=cut + +#-------------------------------------------------------------------------------- + +sub start_logging +{ + my ($class, $logfile) = @_; + my ($cmd, $rc); + xCAT::MsgUtils->backup_logfile($logfile); + + # create the log directory if it's not already there + if (!-d $::XCATLOG) + { + mkdir($::XCATLOG, 0755); + } + + # open the log file + unless (open(LOGFILE, ">>$logfile")) + { + + # Cannot open file + xCAT::MsgUtils->message("E", "Cannot open file: $logfile.\n"); + return $::NOK; + } + + $::LOG_FILE_HANDLE = \*LOGFILE; + + # Print the date to the top of the logfile + my $sdate = localtime(time); + chomp $sdate; + my $program = xCAT::Utils->programName(); + xCAT::MsgUtils->message( + "TV", + "#--------------------------------------------------------------------------#\n" + ); + xCAT::MsgUtils->message("TV", "$program: Logging Started:$sdate\n"); + xCAT::MsgUtils->message("TV", "Input: $::command_line\n"); + xCAT::MsgUtils->message( + "TV", + "#--------------------------------------------------------------------------#\n" + ); + + return ($::LOG_FILE_HANDLE); +} + +#-------------------------------------------------------------------------------- + +=head3 stop_logging + + Turn off message logging close file. Routine expects to have a file handle + passed in via the global $::LOG_FILE_HANDLE. + + Arguments: + + Returns: + $::OK + Globals: + $::LOG_FILE_HANDLE + Error: + none + Example: + xCAT::MsgUtils->stop_logging($cfmupdatenode.log); + Comments: + closes the logfile and undefines $::LOG_FILE_HANDLE + even on error. + +=cut + +#-------------------------------------------------------------------------------- + +sub stop_logging +{ + my ($class) = @_; + if (defined($::LOG_FILE_HANDLE)) + { + + my $sdate = localtime(time); + chomp $sdate; + my $program = xCAT::Utils->programName(); + xCAT::MsgUtils->message( + "TV", + "#--------------------------------------------------------------------------#\n" + ); + xCAT::MsgUtils->message("TV", "$program: Logging Stopped: $sdate\n"); + xCAT::MsgUtils->message( + "TV", + "#--------------------------------------------------------------------------#\n" + ); + + close($::LOG_FILE_HANDLE); + undef $::LOG_FILE_HANDLE; + } + return $::OK; +} + +1; + diff --git a/perl-xCAT-2.0/xCAT/NodeRange.pm b/perl-xCAT-2.0/xCAT/NodeRange.pm new file mode 100644 index 000000000..ab38e921d --- /dev/null +++ b/perl-xCAT-2.0/xCAT/NodeRange.pm @@ -0,0 +1,325 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT::NodeRange; +use xCAT::Table; +use Data::Dumper; +require Exporter; +use strict; + +#Perl implementation of noderange +our @ISA = qw(Exporter); +our @EXPORT = qw(noderange nodesmissed); + +my $missingnodes=[]; +my $nodelist; #=xCAT::Table->new('nodelist',-create =>1); +my $nodeprefix = "node"; + + +sub subnodes (\@@) { + #Subtract set of nodes from the first list + my $nodes = shift; + my $node; + foreach $node (@_) { + @$nodes = (grep(!/^$node$/,@$nodes)); + } +} +sub nodesmissed { + return @$missingnodes; +} + +sub expandatom { + my $atom = shift; + my $verify = (scalar(@_) == 1 ? shift : 1); + my @nodes= (); + my $nprefix=(defined ($ENV{'XCAT_NODE_PREFIX'}) ? $ENV{'XCAT_NODE_PREFIX'} : 'node'); + my $nsuffix=(defined ($ENV{'XCAT_NODE_SUFFIX'}) ? $ENV{'XCAT_NODE_PREFIX'} : ''); + if ($nodelist->getAttribs({node=>$atom},'node')) { + #The atom is a plain old nodename + return ($atom); + } + if ($atom =~ /^\(.*\)$/) { + $atom =~ s/^\((.*)\)$/$1/; + return noderange($atom); + } + foreach($nodelist->getAllAttribsWhere("groups like '%".$atom."%'",'node','groups')) { + my @groups=split(/,/,$_->{groups}); #The where clause doesn't guarantee the atom is a full group name, only that it could be + if (grep { $_ eq "$atom" } @groups ) { + push @nodes,$_->{node}; + } + } + if ($atom =~ m/^[0-9]+\z/) { + my $nodename=$nprefix.$atom.$nsuffix; + return expandatom($nodename,$verify); + } + my $nodelen=@nodes; + if ($nodelen > 0) { + return @nodes; + } + if ($atom =~ m/(.*)\[(.*)\](.*)/) { #bracket range + #for the time being, we are only going to consider one [] per atom + #xcat 1.2 does no better + my @subelems = split(/([\,\-\:])/,$2); + my $subrange=""; + while (my $subelem = shift @subelems) { + my $subop=shift @subelems; + $subrange=$subrange."$1$subelem$3$subop"; + } + foreach (split /,/,$subrange) { + my @newnodes=expandatom($_,$verify); + @nodes=(@nodes,@newnodes); + } + return @nodes; + } + if ($atom =~ m/\+/) {#process + operator + $atom =~ m/^([^0-9]*)([0-9]+)([^\+]*)\+([0-9]+)/; + my $pref=$1; + my $startnum=$2; + my $suf=$3; + my $end=$4+$startnum; + my $endnum = sprintf("%d",$end); + if (length ($startnum) > length ($endnum)) { + $endnum = sprintf("%0".length($startnum)."d",$end); + } + if (($pref eq "") && ($suf eq "")) { + $pref=$nprefix; + $suf=$nsuffix; + } + foreach ("$startnum".."$endnum") { + my @addnodes=expandatom($pref.$_.$suf,$verify); + @nodes=(@nodes,@addnodes); + } + return (@nodes); + } + if ($atom =~ m/[-:]/) {#process - range operator + my $left; + my $right; + if ($atom =~ m/:/) { + ($left,$right)=split /:/,$atom; + } else { + my $count= ($atom =~ tr/-//); + if (($count % 2)==0) { #can't understand even numbers of - in range context + if ($verify) { + push @$missingnodes,$atom; + return (); + } else { #but we might not really be in range context, if noverify + return ($atom); + } + } + my $expr="([^-]+?".("-[^-]*"x($count/2)).")-(.*)"; + $atom =~ m/$expr/; + $left=$1; + $right=$2; + } + if ($left eq $right) { #if they said node1-node1 for some strange reason + return expandatom($left,$verify); + } + my @leftarr=split(/(\d+)/,$left); + my @rightarr=split(/(\d+)/,$right); + if (scalar(@leftarr) != scalar(@rightarr)) { #Mismatch formatting.. + if ($verify) { + push @$missingnodes,$atom; + return (); #mismatched range, bail. + } else { #Not in verify mode, just have to guess it's meant to be a nodename + return ($atom); + } + } + my $prefix = ""; + my $suffix = ""; + foreach (0..$#leftarr) { + my $idx = $_; + if ($leftarr[$idx] =~ /^\d+$/ and $rightarr[$idx] =~ /^\d+$/) { #pure numeric component + if ($leftarr[$idx] ne $rightarr[$idx]) { #We have found the iterator (only supporting one for now) + my $prefix = join('',@leftarr[0..($idx-1)]); #Make a prefix of the pre-validated parts + my $luffix; #However, the remainder must still be validated to be the same + my $ruffix; + if ($idx eq $#leftarr) { + $luffix=""; + $ruffix=""; + } else { + $ruffix = join('',@rightarr[($idx+1)..$#rightarr]); + $luffix = join('',@leftarr[($idx+1)..$#leftarr]); + } + if ($luffix ne $ruffix) { #the suffixes mismatched.. + if ($verify) { + push @$missingnodes,$atom; + return (); + } else { + return ($atom); + } + } + foreach ($leftarr[$idx]..$rightarr[$idx]) { + my @addnodes=expandatom($prefix.$_.$luffix,$verify); + @nodes=(@nodes,@addnodes); + } + return (@nodes); #the return has been built, return, exiting loop and all + } + } elsif ($leftarr[$idx] ne $rightarr[$idx]) { + if ($verify) { + push @$missingnodes,$atom; + return (); + } else { + return ($atom); + } + } + $prefix .= $leftarr[$idx]; #If here, it means that the pieces were the same, but more to come + } + #I cannot conceive how the code could possibly be here, but whatever it is, it must be questionable + if ($verify) { + push @$missingnodes,$atom; + return (); #mismatched range, bail. + } else { #Not in verify mode, just have to guess it's meant to be a nodename + return ($atom); + } + } + + + if ($atom =~ m/^\//) { #A regex + unless ($verify) { #If not in verify mode, regex makes zero possible sense + return ($atom); + } + #TODO: check against all groups + $atom = substr($atom,1); + foreach ($nodelist->getAllAttribs('node')) { + if ($_->{node} =~ m/^${atom}$/) { + push(@nodes,$_->{node}); + } + } + return(@nodes); + } + push @$missingnodes,$atom; + if ($verify) { + return (); + } else { + return ($atom); + } +} + +sub noderange { + $missingnodes=[]; + #We for now just do left to right operations + #TODO: Parentheses... A parenthetical group to the right of an intersection makes the obvious + #answer not work + my $range=shift; + my $verify = (scalar(@_) == 1 ? shift : 1); + $nodelist =xCAT::Table->new('nodelist',-create =>1); + my %nodes = (); + my %delnodes = (); + my $op = ","; + #my @elems = split(/(,(?![^[]*?])|@)/,$range); #, or @ but ignore , within [] + my @elems = split(/(,(?![^[]*?])(?![^\(]*?\))|@(?![^\(]*?\)))/,$range); #, or @ but ignore , within [] + + while (my $atom = shift @elems) { + if ($atom =~ /^-/) { + $atom = substr($atom,1); + $op = $op."-"; + } + if ($atom =~ /^\^(.*)$/) { + open(NRF,$1); + while () { + my $line=$_; + unless ($line =~ m/^[\^#]/) { + $line =~ m/^([^: ]*)/; + my $newrange = $1; + chomp($newrange); + my @filenodes = noderange($newrange); + foreach (@filenodes) { + $nodes{$_}=1; + } + } + } + close(NRF); + next; + } + my %newset = map { $_ =>1 } expandatom($atom,$verify); + if ($op =~ /@/) { + foreach (keys %nodes) { + unless ($newset{$_}) { + delete $nodes{$_}; + } + } + } elsif ($op =~ /,-/) { + foreach (keys %newset) { + $delnodes{$_}=1; #delay removal to end + } + } else { + foreach (keys %newset) { + $nodes{$_}=1; + } + } + $op = shift @elems; + } + foreach (keys %nodes) { + if ($delnodes{$_}) { + delete $nodes{$_}; + } + } + undef $nodelist; + return sort (keys %nodes); + +} + + +1; + +=head1 NAME + +xCAT::NodeRange - Perl module for xCAT noderange expansion + +=head1 SYNOPSIS + + use xCAT::NodeRange; + my @nodes=noderange("storage@rack1,node[1-200],^/tmp/nodelist,node300-node400,node401+10,500-550"); + +=head1 DESCRIPTION + +noderange interprets xCAT noderange formatted strings and returns a list of xCAT nodelists. The following two operations are supported on elements, and interpreted left to right: + +, union next element with everything to the left. + +@ take intersection of element to the right with everything on the left (i.e. mask out anything to the left not belonging to what is described to the right) + +Each element can be a number of things: + +A node name, i.e.: + +=item * node1 + +A hyphenated node range (only one group of numbers may differ between the left and right hand side, and those numbers will increment in a base 10 fashion): + +node1-node200 node1-compute-node200-compute +node1:node200 node1-compute:node200-compute + +A noderange denoted by brackets: + +node[1-200] node[001-200] + +A regular expression describing the noderange: + +/d(1.?.?|200) + +A node plus offset (this increments the first number found in nodename): + +node1+199 + +And most of the above substituting groupnames. +3C +3C + +NodeRange tries to be intelligent about detecting padding, so you can: +node001-node200 +And it will increment according to the pattern. + + +=head1 AUTHOR + +Jarrod Johnson (jbjohnso@us.ibm.com) + +=head1 COPYRIGHT + +Copyright 2007 IBM Corp. All rights reserved. + +TODO: What license is this? + + + + +=cut diff --git a/perl-xCAT-2.0/xCAT/NotifHandler.pm b/perl-xCAT-2.0/xCAT/NotifHandler.pm new file mode 100644 index 000000000..8f79b4587 --- /dev/null +++ b/perl-xCAT-2.0/xCAT/NotifHandler.pm @@ -0,0 +1,334 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT::NotifHandler; +use File::Basename qw(fileparse); + +#%notif is a cache that holds the info from the "notification" table. +#the format of it is: +# {tablename=>{'a'=>[filename,...] +# 'u'=>[filename,,..] +# 'd'=>[filename,...] +# } +# } +%notif; +$masterpid=$$; +1; + +#------------------------------------------------------------------------------- +=head1 xCATi::NotifHandler +=head2 Package Description + This mondule caches the notification table and tracks the changes of it. + It also handles the event notification when xCAT database changes. +=cut +#------------------------------------------------------------------------------- + + +#-------------------------------------------------------------------------------- +=head3 setup + It is called by xcatd to get set the pid of the parent of all this object. + Setup the signal to trap any changes in the notification table. It also + initializes the cache with the current data in the notification table. + table and store it into %notif variable. + Arguments: + pid -- the process id of the caller. + Returns: + none +=cut +#------------------------------------------------------------------------------- +sub setup +{ + $masterpid=shift; + if ($masterpid =~ /xCAT::NotifHandler/) { + $masterpid=shift; + } + refreshNotification(); + $SIG{USR1}=\&handleNotifSigal; +} + +#-------------------------------------------------------------------------------- +=head3 handleNotifSigal + It is called when the signal is received. It then update the cache with the + latest data in the notification table. + Arguments: + none. + Returns: + none +=cut +#------------------------------------------------------------------------------- +sub handleNotifSigal { + refreshNotification(); + $SIG{USR1}=\&handleNotifSigal; +} + +#-------------------------------------------------------------------------------- +=head3 sendNotifSignal + It is called by any module that has made changes to the notification table. + Arguments: + none. + Returns: + none +=cut +#------------------------------------------------------------------------------- +sub sendNotifSignal { + kill('USR1', $masterpid); +} + + +#-------------------------------------------------------------------------------- +=head3 refreshNotification + It loads the notification info from the "notification" + table and store it into %notif variable. + The format of it is: + {tablename=>{filename=>{'ops'=>['y/n','y/n','y/n'], 'disable'=>'y/n'}}} + Arguments: + none + Returns: + none +=cut +#------------------------------------------------------------------------------- +sub refreshNotification +{ + #flush the cache + %notif=(); + my $table=xCAT::Table->new("notification", -create =>0); + if ($table) { + #get array of rows out of the notification table + my @row_array= $table->getTable; + if (@row_array) { + #store the information to the cache + foreach(@row_array) { + my $module=$_->{filename}; + my $ops=$_->{tableops}; + my $disable= $_->{disable}; + my @tablenames=split(/,/, $_->{tables}); + + foreach(@tablenames) { + if (!exists($notif{$_})) { + $notif{$_}={}; + } + + + my $tempdisable=0; + if ($disable) { + if ($disable =~ m/^(yes|YES|Yes|Y|y|1)$/) { + $tempdisable=1; + } + } + + if (!$disable) { + if ($ops) { + if ($ops =~ m/a/) { + if (exists($notif{$_}->{a})) { + my $pa=$notif{$_}->{a}; + push(@$pa, $module); + } else { + $notif{$_}->{a}=[$module]; + } + } + if ($ops =~ m/d/) { + if (exists($notif{$_}->{d})) { + my $pa=$notif{$_}->{d}; + push(@$pa, $module); + } else { + $notif{$_}->{d}=[$module]; + } + } + if ($ops =~ m/u/) { + if (exists($notif{$_}->{u})) { + my $pa=$notif{$_}->{u}; + push(@$pa, $module); + } else { + $notif{$_}->{u}=[$module]; + } + } + } #end if + } + } #end foreach + + } #end foreach(@row_array) + }end if (@row_array) + } #end if ($table) + + return 1; +} + + +#-------------------------------------------------------------------------------- +=head3 dumpNotificationCache + It print out the content of the notification cache for debugging purpose. + Arguments: + none + Returns: + 0 +=cut +#------------------------------------------------------------------------------- +sub dumpNotificationCache { + print "dump the notification cache:\n"; + foreach(keys(%notif)) { + my $tmptn=$_; + print " $tmptn: \n"; + + if (exists($notif{$_}->{a})) { + print " a--:"; + my $files=$notif{$_}->{a}; + print "@$files\n"; + } + if (exists($notif{$_}->{u})) { + print " u--:"; + my $files=$notif{$_}->{u}; + print "@$files\n"; + } + if (exists($notif{$_}->{d})) { + print " d--:"; + my $files=$notif{$_}->{d}; + print "@$files\n"; + } + } + return 0; +} + + +#-------------------------------------------------------------------------------- +=head3 needToNotify + It check if the given table has interested parties watching for its changes. + Arguments: + tablename - the name of the table to be checked. + tableop - the operation on the table. 'a' for add, 'u' for update + and 'd' for delete. + Returns: + 1 - if the table has interested parties. + 0 - if no parties are interested in its changes. +=cut +#------------------------------------------------------------------------------- +sub needToNotify { + if (!defined %notif) { + # print "notif not defined\n"; + refreshNotification(); + } + + my $tablename=shift; + if ($tablename =~ /xCAT::NotifHandler/) { + $tablename=shift; + } + my $tableop=shift; + + if (%notif) { + if (exists($notif{$tablename})) { + if (exists($notif{$tablename}->{$tableop})) { + return 1; + } + } + } + return 0; +} + + +#-------------------------------------------------------------------------------- +=head3 notify + It notifies the registered the modules with the latest changes in + a DB table. + Arguments: + action - table action. It can be d for rows deleted, a for rows added + or u for rows updated. + tablename - string. The name of the DB table whose data has been changed. + old_data - an array reference of the old row data that has been changed. + The first element is an array reference that contains the column names. + The rest of the elelments are also array references each contains + attribute values of a row. + It is set when the action is u or d. + new_data - a hash refernce of new row data; only changed values are present + in the hash. It is keyed by column names. + It is set when the action is u or a. + Returns: + 0 + Comments: + If the curent table is watched by a perl module, the module must implement + the following routine: + processTableChanges(action, table_name, old_data, new_data) + If it is a watched by a command, the data will be passed to the command + through STDIN. The format is: + action + table_name + [old data] + col1_name,col2_name,... + col1_value,col2_value,... + ... + [new data] + col1_name,col2_name,... + col1_value,col2_value,... + ... + +=cut +#------------------------------------------------------------------------------- +sub notify { + my $action=shift; + if ($action =~ /xCAT::NotifHandler/) { + $action=shift; + } + my $tablename=shift; + my $old_data=shift; + my $new_data=shift; + + # print "notify called: tablename=$tablename, action=$action\n"; + + my @filenames=(); + if (%notif) { + if (exists($notif{$tablename})) { + if (exists($notif{$tablename}->{$action})) { + my $pa=$notif{$tablename}->{$action}; + @filenames=@$pa; + } + } + } + + + foreach(@filenames) { + my ($modname, $path, $suffix) = fileparse($_, ".pm"); + # print "modname=$modname, path=$path, suffix=$suffix\n"; + if ($suffix =~ /.pm/) { #it is a perl module + my $pid; + if ($pid=fork()) { } + elsif (defined($pid)) { + if (($path eq "") || ($path eq ".\/")) { + #default path is /usr/lib/xcat/monitoring/ if there is no path specified + require "/usr/lib/xcat/monitoring/".$modname.".pm"; + } else { + require $_; + } + ${"xCAT_monitoring::".$modname."::"}{processTableChanges}->($action, $tablename, $old_data, $new_data); + exit 0; + } + } + else { #it is a command + my $pid; + if ($pid=fork()) { } + elsif (defined($pid)) { + # print "command=$_\n"; + if (open(CMD, "|$_")) { + print(CMD "$action\n"); + print(CMD "$tablename\n"); + + print(CMD "[old data]\n"); + foreach (@$old_data) { + print(CMD join(',', @$_)."\n"); + } + + print(CMD "[new data]\n"); + if (%$new_data) { + print(CMD join(',', keys %$new_data) . "\n"); + print(CMD join(',', values %$new_data) . "\n"); + } + close(CMD) or print "Cannot close the command $_\n"; + } + else { + print "Command $_ cannot be found\n"; + } + + exit 0; + } #elsif + } + } #foreach + + return 0; +} + diff --git a/perl-xCAT-2.0/xCAT/Postage.pm b/perl-xCAT-2.0/xCAT/Postage.pm new file mode 100644 index 000000000..bff4a0594 --- /dev/null +++ b/perl-xCAT-2.0/xCAT/Postage.pm @@ -0,0 +1,297 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT::Postage; +use xCAT::Table; +use xCAT::NodeRange; +use Data::Dumper; +my $depsfile = "/etc/xcat/postscripts.dep"; +my $rulesfile = "/etc/xcat/postscripts.rules"; +my $rules; +my $deps; +my $rulec; +my $node; + +sub writescript { + if (scalar(@_) eq 3) { shift; } #Discard self + $node = shift; + my $scriptfile = shift; + my $script; + open($rules,"<",$rulesfile); + open($deps,"<",$depsfile); + unless($rules) { + return undef; + } + open($script,">",$scriptfile); + unless ($scriptfile) { + return undef; + } + #Some common variables... + my $noderestab=xCAT::Table->new('noderes'); + my $typetab=xCAT::Table->new('nodetype'); + unless ($noderestab and $typetab) { + die "Unable to open noderes or nodetype table"; + } + my $master; + my $sitetab = xCAT::Table->new('site'); + (my $et) = $sitetab->getAttribs({key=>"master"},'value'); + if ($et and $et->{value}) { + $master = $et->{value}; + } + $et = $noderestab->getNodeAttribs($node,['xcatmaster']); + if ($et and $et->{'xcatmaster'}) { + $master = $et->{'xcatmaster'}; + } + unless ($master) { + die "Unable to identify master for $node"; + } + print $script "MASTER=".$master."\n"; + print $script "export MASTER\n"; + my $et = $typetab->getNodeAttribs($node,['os','arch']); + unless ($et and $et->{'os'} and $et->{'arch'}) { + die "No os/arch setting in nodetype table for $node"; + } + print $script "OSVER=".$et->{'os'}."\n"; + print $script "ARCH=".$et->{'arch'}."\n"; + print $script "export OSVER ARCH\n"; + print $script 'PATH=`dirname $0`:$PATH'."\n"; + print $script "export PATH\n"; + $rulec=""; + my @scripts; + my $inlist = 0; + my $pushmode = 0; + my $critline=""; + while (<$rules>) { + my $line = $_; + $line =~ s/^\s*//; + $line =~ s/\s*$//; + $line =~ s/#.*//; + if ($line =~ /^$/) { + next; + } + # we are left with content lines.. + my $donewithline=0; + while (not $donewithline) { + if ($line =~ /^\s*$/) { + $donewithline=1; + next; + } + if ($inlist) { + if ($line =~/^[^\}]*\{/) { + #TODO: error unbalanced {} in postscripts.rules + die "Unbalanced {}"; + return undef; + } + if ($pushmode) { + my $toadd; + ($toadd) = $line =~ /(^[^\}]*)/; + $line =~ s/(^[^\}]*)//; + unless ($toadd =~ /^\s*$/) { + push @scripts,$toadd; + } + } else { #strip non } characters leading + $line =~ s/^[^\}]*//; + } + if ($line =~ /\}/) { + $line =~ s/\}//; + $inlist=0; + } + } else { + if ($line =~/^[^\{]*\}/) { + #TODO: error unbalanced {} in postscripts.rules + return undef; + } + (my $tcritline) = $line =~ /^([^\{]*)/; + $critline .= $tcritline . " "; + if ($line =~ /\{/) { + if (criteriamatches($critline)) { + $pushmode=1; + } else { + $pushmode=0; + } + $critline = ""; + $inlist = 1; + $line =~ s/[^\{]*\{//; + } else { + $donewithline=1; + } + } + } + } + foreach (@scripts) { + print $script $_."\n"; + } + close($script); + chmod 0755,$scriptfile; +} + +#shamelessly brought forth from postrules.pl in xCAT 1.3 +sub criteriamatches { + my $cline = shift; + my $level=0; + my $pline; + my @opstack; + my @expstack; + my $r; + $cline =~ s/\{//g; + $cline =~ s/(\(|\))/ $1 /g; + $cline =~ s/\s*=\s*/=/g; + $cline =~ s/^\s*//; + $cline =~ s/\s*$//; + $cline =~ s/\s+/ /; + if ($cline =~ /^ALL$/) { + return 1; + } + my @tokens = split('\s+',$cline); + my $token; + foreach $token (@tokens) { + if ($token eq ')') { + $level--; + if ($level == 0) { + push @expstack,criteriamatches($pline); + $pline=""; + next; + } elsif ($level < 0) { + die "Unbalanecd () in postrules"; + } + } + if ($level) { + $pline .= " $token"; + } + if ($token eq '(') { + $level++; + next; + } + if ($level) { + next; + } + if ($token =~ /=/) { + push(@expstack,$token); + next; + } + if ($token =~ /^(and|or|not)$/i) { + push (@opstack,$token); + next; + } + die "Syntax error in postscripts rules, bad token $token"; + } + if ($level) { + die "Unbalanced () in postscripts rules"; + } + + while (@opstack) { + my $op; + my $r1 = 0; + my $r2 = 0; + + $op = pop(@opstack); + unless (defined $op) { + die "Stack underflow in postscripts rules"; + } + if ($op =~ /and/i) { + $r1 = popeval(\@expstack); + $r2 = popeval(\@expstack); + + if ($r1 && $r2) { + push(@expstack,1); + } else { + push(@expstack,0); + } + } elsif ($op =~ /or/i) { + $r1 = popeval(\@expstack); + $r2 = popeval(\@expstack); + if ($r1 || $r2) { + push(@expstack,1); + } else { + push(@expstack,0); + } + } elsif ($op =~ /not/i) { + $r1 = popeval(\@expstack); + if ($r1==0) { + push(@expstack,1); + } else { + push (@expstack,0); + } + } + } + if (@expstack == 1) { + $r = popeval(\@expstack); + push(@expstack,$r); + } + + $r = pop(@expstack); + unless (defined $r) { + die "Stack underflow in postscripts processing"; + } + if (@expstack != 0 || @opstack != 0) { + die "Stack underflow in postscripts processing"; + } + return $r; +} + + +sub popeval { + my ($expstack) = @_; + my $exp; + my $v; + my $r; + $exp = pop(@$expstack); + if (defined ($exp)) { + if ($exp =~ /=/) { + my @eqarr = split(/=/,$exp); + $r = $eqarr[$#eqarr]; + $v = join('=',@eqarr[0..($#eqarr-1)]); + #($v,$r) = split(/=/,$exp); + if ($v =~ /^OSVER$/) { #OSVER is redundant, but a convenient shortcut + $v = 'TABLE:nodetype:$NODE:os'; + } + if ($v =~ /^NODERANGE$/i) { + my @nr = noderange $r; + foreach (@nr) { + if ($node eq $_) { + return 1; + } + } + return 0; + } + if ($v =~ /^TABLE:/) { + my $table; + my $key; + my $field; + ($table,$key,$field) = $v =~ m/TABLE:([^:]+):([^:]+):(.*)/; + my $tabref = xCAT::Table->new($table); + unless ($tabref) { return 0; } + my $ent; + if ($key =~ /^\$NODE/) { + $ent = $tabref->getNodeAttribs($node,[$field]); + } else { + my @keys = split /,/,$key; + my %keyh; + foreach (@keys) { + my $keycol; + my $keyval; + ($keycol,$keyvol)=split /=/,$_; + $keyh{$keycol}=$keyvol; + } + ($ent)=$tabref->getAttribs(\%keyh,$field); + } + + unless ($ent and $ent->{$field}) { return 0; } + if ($ent->{$field} =~ /^$r$/) { + return 1; + } else { + return 0; + } + } + #TODO? support for env vars? Don't see much of a point now, but need input + } elsif ($exp == 0 || $exp == 1) { + return ($exp); + } else { + die "Invalid expression $exp in postcripts rules"; + } + } else { + die "Stack underflow in postscripts rules..."; + } +} + + + +1; diff --git a/perl-xCAT-2.0/xCAT/Schema.pm b/perl-xCAT-2.0/xCAT/Schema.pm new file mode 100644 index 000000000..df5425ea6 --- /dev/null +++ b/perl-xCAT-2.0/xCAT/Schema.pm @@ -0,0 +1,347 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT::Schema; + +#Note that the SQL is far from imaginative. Fact of the matter is that +#certain SQL backends don't ascribe meaning to the data types anyway. +#New format, not sql statements, but info enough to describe xcat tables +%tabspec = ( + nodetype => { + cols => [qw(node os arch profile comments disable)], + keys => [qw(node)], + }, + nodepos => { + cols => [qw(node rack u chassis slot room comments disable)], + keys => [qw(node)], + }, + vpd => { + cols => [qw(node serial mtm comments disable)], + keys => [qw(node)], + }, + nodehm => { + cols => [qw(node power mgt cons termserver termport conserver serialspeed serialflow getmac comments disable)], + keys => [qw(node)], + }, + hosts => { + cols => [qw(node ip hostnames comments disable)], + keys => [qw(node)], + }, + mp => { + cols => [qw(node mpa id comments disable)], + keys => [qw(node)], + }, + mpa => { + cols => [qw(mpa username password comments disable)], + keys => [qw(mpa)], + }, + mac => { + cols => [qw(node interface mac comments disable)], + keys => [qw(node interface)], + }, + chain => { + cols => [qw(node currstate currchain chain ondiscover comments disable)], + keys => [qw(node)], + }, + noderes => { + cols => [qw(node servicenode netboot tftpserver nfsserver kernel initrd kcmdline nfsdir serialport installnic primarynic xcatmaster current_osimage next_osimage comments disable)], + keys => [qw(node)], + }, + networks => { + cols => [qw(netname net mask gateway dhcpserver tftpserver nameservers dynamicrange comments disable)], + keys => [qw(net mask)] + }, + switch => { + cols => [qw(node switch vlan port comments disable)], + keys => [qw(node switch port)] + }, + nodelist => { + cols => [qw(node nodetype groups comments disable)], + keys => [qw(node)], + }, + site => { + cols => [qw(key value comments disable)], + keys => [qw(key)] + }, + passwd => { + cols => [qw(key username password comments disable)], + keys => [qw(key)] + }, + ipmi => { + cols => [qw(node bmc username password comments disable )], + keys => [qw(node)] + }, + policy => { + cols => [qw(priority name host commands noderange parameters time rule comments disable)], + keys => [qw(priority)] + }, + notification => { + cols => [qw(filename tables tableops comments disable)], + keys => [qw(filename)], + required => [qw(tables filename)] + } + ); + + + +#################################################### +# +# Data abstraction definitions +# For each table entry added to the database schema, +# a corresponding attribute should be added to one of +# the data objects below, or new data objects should +# be created as needed. +# +# Definition format: +# List of data object hashes: +# => +# {attrs => +# [ {attr_name => '', +# only_if => '=', +# # optional, used to define conditional attributes. +# # is a previously resolved attribute from +# # this data object. +# tabentry => '', +# # where the data is stored in the database +# access_tabentry => '=', +# # how to look up tabentry. For , +# # if "attr:", use a previously resolved +# # attribute value from the data object +# # if "str:" use the value directly +# description => '', +# }, +# {attr_name => , +# ... +# } ], +# attrhash => {}, # internally generated hash of attrs array +# # to allow code direct access to an attr def +# objkey => 'attribute_name' # key attribute for this data object +# } +# +# +#################################################### +%defspec = ( + node => { attrs => [], attrhash => {}, objkey => 'node' }, + osimage => { attrs => [], attrhash => {}, objkey => 'imagename' }, + network => { attrs => [], attrhash => {}, objkey => 'netname' }, + site => { attrs => [], attrhash => {}, objkey => 'master' } +); + +############# +# TODO: Need to figure out how to map the following to data objects: +# nodetype table (does this get moved to the osimage table?) +# --> do we need an arch attr per node that is stored in nodehm? +# mac table (are we going to have an interface object definition?) +# switch table (each interface on a node can have its own switch +# table entry, part of interface object, too?) +# username/password from password, hmc, ivm, mpa, and ipmi tables +# (do we need special encryption and display masking for passwords?) +# chain table (I think this is internal use only, do not abstract?) +# noderes entries for kernel, initrd, kcmdline +# ppc table (waiting on Scot to add to tabspec) +# policy table - ? do we need a data abstraction for this? +# notification - is this handled by Ling's commands? +# site - need to figure out what entries we will have in the +# site table since they are not listed individually in the +# tabspec +# nodelist.groups +# new group table and object +# new osimage table and object +############### +# TODO: need to fill out all the "description" fields +# These will be used for verbose usage with the def* cmds +############## +@{$defspec{node}->{'attrs'}} = ( + {attr_name => 'node', + tabentry => 'nodelist.node', + access_tabentry => 'objkeyvalue'}, +############ +# TODO: The attr name for nodelist.nodetype is in conflict with the existing +# nodetype table. With the osimage table, the nodetype table should go +# away. Will reuse of this name cause confusion for xcat users? +############ + {attr_name => 'nodetype', + tabentry => 'nodelist.nodetype', + access_tabentry => 'nodelist.node=attr:node', + description => 'Type of node: osi,hmc,fsp,mpa,???'}, + {attr_name => 'xcatmaster', + tabentry => 'noderes.xcatmaster', + access_tabentry => 'noderes.node=attr:node'}, + {attr_name => 'servicenode', + tabentry => 'noderes.servicenode', + access_tabentry => 'noderes.node=attr:node'}, + {attr_name => 'mgt', + tabentry => 'nodehm.mgt', + access_tabentry => 'nodehm.node=attr:node'}, + {attr_name => 'power', + tabentry => 'nodehm.power', + access_tabentry => 'nodehm.node=attr:node'}, + {attr_name => 'cons', + tabentry => 'nodehm.cons', + access_tabentry => 'nodehm.node=attr:node'}, + {attr_name => 'termserver', + tabentry => 'nodehm.termserver', + access_tabentry => 'nodehm.node=attr:node'}, + {attr_name => 'termport', + tabentry => 'nodehm.termport', + access_tabentry => 'nodehm.node=attr:node'}, + {attr_name => 'conserver', + tabentry => 'nodehm.conserver', + access_tabentry => 'nodehm.node=attr:node'}, + {attr_name => 'getmac', + tabentry => 'nodehm.getmac', + access_tabentry => 'nodehm.node=attr:node'}, + {attr_name => 'serialport', + tabentry => 'noderes.serialport', + access_tabentry => 'noderes.node=attr:node'}, + {attr_name => 'serialspeed', + tabentry => 'nodehm.serialspeed', + access_tabentry => 'nodehm.node=attr:node'}, + {attr_name => 'serialflow', + tabentry => 'nodehm.serialflow', + access_tabentry => 'nodehm.node=attr:node'}, + {attr_name => 'ip', + tabentry => 'hosts.ip', + access_tabentry => 'hosts.node=attr:node'}, + {attr_name => 'hostnames', + tabentry => 'hosts.hostnames', + access_tabentry => 'hosts.node=attr:node'}, + {attr_name => 'serialnumber', + tabentry => 'vpd.serial', + access_tabentry => 'vpd.node=attr:node'}, + {attr_name => 'mtm', + tabentry => 'vpd.mtm', + access_tabentry => 'vpd.node=attr:node'}, + {attr_name => 'rackloc', + tabentry => 'nodepos.rack', + access_tabentry => 'nodepos.node=attr:node'}, + {attr_name => 'unitloc', + tabentry => 'nodepos.u', + access_tabentry => 'nodepos.node=attr:node'}, + {attr_name => 'chassisloc', + tabentry => 'nodepos.chassis', + access_tabentry => 'nodepos.node=attr:node'}, + {attr_name => 'slotloc', + tabentry => 'nodepos.slot', + access_tabentry => 'nodepos.node=attr:node'}, + {attr_name => 'roomloc', + tabentry => 'nodepos.room', + access_tabentry => 'nodepos.node=attr:node'}, + + # Conditional attributes: + # OSI node attributes: + {attr_name => 'tftpserver', + only_if => 'nodetype=osi', + tabentry => 'noderes.tftpserver', + access_tabentry => 'noderes.node=attr:node'}, + {attr_name => 'nfsserver', + only_if => 'nodetype=osi', + tabentry => 'noderes.nfsserver', + access_tabentry => 'noderes.node=attr:node'}, + {attr_name => 'nfsdir', + only_if => 'nodetype=osi', + tabentry => 'noderes.nfsdir', + access_tabentry => 'noderes.node=attr:node'}, + {attr_name => 'primarynic', + only_if => 'nodetype=osi', + tabentry => 'noderes.primarynic', + access_tabentry => 'noderes.node=attr:node'}, + {attr_name => 'installnic', + only_if => 'nodetype=osi', + tabentry => 'noderes.installnic', + access_tabentry => 'noderes.node=attr:node'}, + {attr_name => 'netboot', + only_if => 'nodetype=osi', + tabentry => 'noderes.netboot', + access_tabentry => 'noderes.node=attr:node'}, + {attr_name => 'current_osimage', + only_if => 'nodetype=osi', + tabentry => 'noderes.current_osimage', + access_tabentry => 'noderes.node=attr:node'}, + {attr_name => 'next_osimage', + only_if => 'nodetype=osi', + tabentry => 'noderes.next_osimage', + access_tabentry => 'noderes.node=attr:node'}, + + # Hardware Control node attributes: + {attr_name => hdwctrlpoint, + only_if => 'mgtmethod=mp', + tabentry => 'mp.mpa', + access_tabentry => 'mp.node=attr:node'}, +# {attr_name => hdwctrlpoint, +# only_if => 'mgtmethod=hmc', +# tabentry => 'ppc.hcp', +# access_tabentry => 'ppc.node=attr:node'}, + {attr_name => hdwctrlpoint, + only_if => 'mgtmethod=ipmi', + tabentry => 'ipmi.bmc', + access_tabentry => 'ipmi.node=attr:node'}, + {attr_name => hdwctrlnodeid, + only_if => 'mgtmethod=mp', + tabentry => 'mp.id', + access_tabentry => 'mp.node=attr:node'}, +# {attr_name => hdwctrlnodeid, +# only_if => 'mgtmethod=hmc', +# tabentry => 'ppc.id', +# access_tabentry => 'ppc.node=attr:node'} + ); + +@{$defspec{osimage}->{'attrs'}} = ( + {attr_name => 'imagename', + tabentry => 'osimage.imagename', + access_tabentry => 'objkeyvalue', + description => 'Name of OS image'}, + {attr_name => 'osdistro', + tabentry => 'osimage.osdistro', + access_tabentry => 'osimage.objname=attr:imagename'}, + ); + +@{$defspec{network}->{'attrs'}} = ( + {attr_name => 'netname', + tabentry => 'networks.netname', + access_tabentry => 'objkeyvalue', + description => 'Name to identify the network'}, + {attr_name => 'net', + tabentry => 'networks.net', + access_tabentry => 'networks.netname=attr:netname'}, + {attr_name => 'mask', + tabentry => 'networks.mask', + access_tabentry => 'networks.netname=attr:netname'}, + {attr_name => 'gateway', + tabentry => 'networks.gateway', + access_tabentry => 'networks.netname=attr:netname'}, + {attr_name => 'dhcpserver', + tabentry => 'networks.dhcpserver', + access_tabentry => 'networks.netname=attr:netname'}, + {attr_name => 'tftpserver', + tabentry => 'networks.tftpserver', + access_tabentry => 'networks.netname=attr:netname'}, + {attr_name => 'nameservers', + tabentry => 'networks.nameservers', + access_tabentry => 'networks.netname=attr:netname'}, + {attr_name => 'dynamicrange', + tabentry => 'networks.dynamicrange', + access_tabentry => 'networks.netname=attr:netname'}, + ); + +############## +# TODO: need to figure out how to handle a key for the site table. +# since this is really implemented differently than all the other +# data objects, it doesn't map as cleanly. +# change format of site table so each column is an attr and there +# is only a single row in the table keyed by xcatmaster name? +############# +@{$defspec{site}->{'attrs'}} = ( + {attr_name => 'master', + tabentry => 'site.value', + access_tabentry => 'site.key=str:master', + description => 'The management node'}, + {attr_name => 'installdir', + tabentry => 'site.value', + access_tabentry => 'site.key=str:installdir', + description => 'The installation directory'}, + {attr_name => 'xcatdport', + tabentry => 'site.value', + access_tabentry => 'site.key=str:xcatdport', + description => 'Port used by xcatd daemon on master'}, + ); + + diff --git a/perl-xCAT-2.0/xCAT/Table.pm b/perl-xCAT-2.0/xCAT/Table.pm new file mode 100644 index 000000000..2b0895cb7 --- /dev/null +++ b/perl-xCAT-2.0/xCAT/Table.pm @@ -0,0 +1,1470 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#Note that at the moment it only implements SQLite. This will probably be extended. +#Also, ugly perl errors/and warnings are not currently wrapped. This probably will be cleaned +#up +#Some known weird behaviors +#creating new sqlite db files when only requested to read non-existant table, easy to fix, +#but going for prototype +#class xcattable +package xCAT::Table; + +use DBI; + +#use strict; +use Data::Dumper; +use xCAT::Schema; +use xCAT::NodeRange; +use Text::Balanced qw(extract_bracketed); +use xCAT::NotifHandler; + +#-------------------------------------------------------------------------------- + +=head1 xCAT::Table + +xCAT::Table - Perl module for xCAT configuration access + +=head2 SYNOPSIS + +use xCAT::Table; +my $table = xCAT::Table->new("tablename"); + +my $hashref=$table->getNodeAttribs("nodename","columname1","columname2"); +printf $hashref->{columname1}; + + +=head2 DESCRIPTION + +This module provides convenience methods that abstract the backend specific configuration to a common API. + +Currently implements the preferred SQLite backend, as well as a CSV backend, using their respective perl DBD modules. + +NOTES + +The CSV backend is really slow at scale. Room for optimization is likely, but in general DBD::CSV is slow, relative to xCAT 1.2.x. +The SQLite backend, on the other hand, is significantly faster on reads than the xCAT 1.2.x way, so it is recommended. + +BUGS + +This module is not thread-safe, due to underlying DBD thread issues. Specifically in testing, SQLite DBD leaks scalars if a thread +where a Table object exists spawns a child and that child exits. The recommended workaround for now is to spawn a thread to contain +all Table objects if you intend to spawn threads from your main thread. As long as no thread in which the new method is called spawns +child threads, it seems to work fine. + +AUTHOR + +Jarrod Johnson + +xCAT::Table is released under an IBM license.... + + +=cut + +#-------------------------------------------------------------------------- + +=head2 Subroutines + +=cut + +#-------------------------------------------------------------------------- + +=head3 buildcreatestmt + + Description: Build create table statement ( see new) + + Arguments: + Table name + Table schema ( hash of column names) + Returns: + Table creation SQL + Globals: + + Error: + + Example: + + my $str = + buildcreatestmt($self->{tabname}, + $xCAT::Schema::tabspec{$self->{tabname}}); + +=cut + +#-------------------------------------------------------------------------------- +sub buildcreatestmt +{ + my $tabn = shift; + my $descr = shift; + my $retv = "CREATE TABLE $tabn (\n "; + my $col; + foreach $col (@{$descr->{cols}}) + { + $retv .= "$col TEXT"; + + if (grep /^$col$/, @{$descr->{required}}) + { + $retv .= " NOT NULL"; + } + $retv .= ",\n "; + } + $retv .= "PRIMARY KEY ("; + foreach (@{$descr->{keys}}) + { + $retv .= "$_,"; + } + $retv =~ s/,$/)\n)/; + return $retv; +} + +#-------------------------------------------------------------------------- + +=head3 new + + Description: Constructor: Connects to or Creates Database Table + + + Arguments: Table name + 0 = Connect to table + 1 = Create table + Returns: + Hash: Database Handle, Statement Handle, nodelist + Globals: + + Error: + + Example: + $nodelisttab = xCAT::Table->new("nodelist"); + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub new +{ + + #Constructor takes table name as argument + #Also takes a true/false value, or assumes 0. If something true is passed, create table + #is requested + my $self = {}; + my $proto = shift; + $self->{tabname} = shift; + unless (defined($xCAT::Schema::tabspec{$self->{tabname}})) { return undef; } + $self->{schema} = $xCAT::Schema::tabspec{$self->{tabname}}; + $self->{colnames} = \@{$self->{schema}->{cols}}; + my %otherargs = @_; + my $create = $otherargs{'-create'}; #(scalar(@_) == 1 ? shift : 0); + my $autocommit = $otherargs{'-autocommit'}; + + unless (defined($autocommit)) + { + $autocommit = 1; + } + + my $class = ref($proto) || $proto; + + my $xcatcfg = (defined $ENV{'XCATCFG'} ? $ENV{'XCATCFG'} : ''); + if ($xcatcfg =~ /^$/) + { + if (-d "/opt/xcat/cfg") + { + $xcatcfg = "SQLite:/opt/xcat/cfg"; + } + else + { + if (-d "/etc/xcat") + { + $xcatcfg = "SQLite:/etc/xcat"; + } + } + } + ($xcatcfg =~ /^$/) && die "Can't locate xCAT configuration"; + unless ($xcatcfg =~ /:/) + { + $xcatcfg = "SQLite:" . $xcatcfg; + } + if ($xcatcfg =~ /^SQLite:/) + { + $self->{backend_type} = 'sqlite'; + my @path = split(':', $xcatcfg, 2); + unless (-e $path[1] . "/" . $self->{tabname} . ".sqlite" || $create) + { + return undef; + } + $self->{connstring} = + "dbi:" . $xcatcfg . "/" . $self->{tabname} . ".sqlite"; + } + elsif ($xcatcfg =~ /^CSV:/) + { + $self->{backend_type} = 'csv'; + $xcatcfg =~ m/^.*?:(.*)$/; + my $path = $1; + $self->{connstring} = "dbi:CSV:f_dir=" . $path; + } + else + { + return undef; + } + $self->{dbh} = + DBI->connect($self->{connstring}, "", "", {AutoCommit => $autocommit}); + if ($xcatcfg =~ /^SQLite:/) + { + my $dbexistq = + "SELECT name from sqlite_master WHERE type='table' and name = ?"; + my $sth = $self->{dbh}->prepare($dbexistq); + $sth->execute($self->{tabname}); + my $result = $sth->fetchrow(); + $sth->finish; + unless (defined $result) + { + if ($create) + { + my $str = + buildcreatestmt($self->{tabname}, + $xCAT::Schema::tabspec{$self->{tabname}}); + $self->{dbh}->do($str); + } + else { return undef; } + } + } + elsif ($xcatcfg =~ /^CSV:/) + { + $self->{dbh}->{'csv_tables'}->{$self->{tabname}} = + {'file' => $self->{tabname} . ".csv"}; + $xcatcfg =~ m/^.*?:(.*)$/; + my $path = $1; + if (!-e $path . "/" . $self->{tabname} . ".csv") + { + unless ($create) + { + return undef; + } + my $str = + buildcreatestmt($self->{tabname}, + $xCAT::Schema::tabspec{$self->{tabname}}); + $self->{dbh}->do($str); + } + } + updateschema($self); + if ($self->{tabname} eq 'nodelist') + { + $self->{nodelist} = $self; + } + else + { + $self->{nodelist} = xCAT::Table->new('nodelist',-create=>1); + } + bless($self, $class); + return $self; +} + +#-------------------------------------------------------------------------- + +=head3 updateschema + + Description: Alters table schema + + Arguments: Hash containing Database and Table Handle and schema + + Returns: None + + Globals: + + Error: + + Example: + $self->{tabname} = shift; + $self->{schema} = $xCAT::Schema::tabspec{$self->{tabname}}; + $self->{colnames} = \@{$self->{schema}->{cols}}; + updateschema($self); + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub updateschema +{ + + #This determines alter table statements required.. + my $self = shift; + if ($self->{backend_type} eq 'sqlite') + { + my $dbexistq = + "SELECT sql from sqlite_master WHERE type='table' and name = ?"; + my $sth = $self->{dbh}->prepare($dbexistq); + $sth->execute($self->{tabname}); + my $cstmt = $sth->fetchrow(); + $sth->finish; + + #my $cstmt = $result->{sql}; + $cstmt =~ s/.*\(//; + $cstmt =~ s/\)$//; + my @entries = split /,/, $cstmt; + my @columns; + foreach (@entries) + { + unless (/\(/) + { #Filter out the PRIMARY KEY statement, but not if on a col + my $colname = $_; + $colname =~ s/^\s*(\S+)\s+.*\s*$/$1/ + ; #I don't understand why it won't work otherwise for " colname TEXT " + push @columns, $colname; + } + } + + #Now @columns reflects the *actual* columns in the database + my $dcol; + foreach $dcol (@{$self->{colnames}}) + { + unless (grep /^$dcol$/, @columns) + { + + #TODO: log/notify of schema upgrade? + my $stmt = + "ALTER TABLE " . $self->{tabname} . " ADD $dcol TEXT"; + $self->{dbh}->do($stmt); + } + } + } +} + +#-------------------------------------------------------------------------- + +=head3 setNodeAttribs + + Description: Set attributes values on the node input to the routine + + Arguments: + Hash: Database Handle, Statement Handle, nodelist + Node name + Attribute hash + Returns: + + Globals: + + Error: + + Example: + my $mactab = xCAT::Table->new('mac',-create=>1); + $mactab->setNodeAttribs($node,{mac=>$mac}); + $mactab->close(); + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub setNodeAttribs +{ + my $self = shift; + my $node = shift; + return $self->setAttribs({'node' => $node}, @_); +} + +#-------------------------------------------------------------------------- + +=head3 addNodeAttribs + + Description: Add new attributes input to the routine to the nodes + + Arguments: + Hash of new attributes + Returns: + + Globals: + + Error: + + Example: + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub addNodeAttribs +{ + my $self = shift; + return $self->addAttribs('node', @_); +} + +#-------------------------------------------------------------------------- + +=head3 addAttribs + + Description: add new attributes + + Arguments: + Hash: Database Handle, Statement Handle, nodelist + Key name + Key value + Hash reference of column-value pairs to set + Returns: + + Globals: + + Error: + + Example: + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub addAttribs +{ + my $self = shift; + my $key = shift; + my $keyval = shift; + my $elems = shift; + my $cols = ""; + my @bind = (); + @bind = ($keyval); + $cols = "$key,"; + + for my $col (keys %$elems) + { + $cols = $cols . $col . ","; + if (ref($$elems{$col})) + { + push @bind, ${$elems}{$col}->[0]; + } + else + { + push @bind, $$elems{$col}; + } + } + chop($cols); + my $qstring = 'INSERT INTO ' . $self->{tabname} . " ($cols) VALUES ("; + for (@bind) + { + $qstring = $qstring . "?,"; + } + $qstring =~ s/,$/)/; + my $sth = $self->{dbh}->prepare($qstring); + $sth->execute(@bind); + + #$self->{dbh}->commit; + + #notify the interested parties + my $notif = xCAT::NotifHandler->needToNotify($self->{tabname}, 'a'); + if ($notif == 1) + { + my %new_notif_data; + $new_notif_data{$key} = $keyval; + foreach (keys %$elems) + { + $new_notif_data{$_} = $$elems{$_}; + } + xCAT::NotifHandler->notify("a", $self->{tabname}, [0], + \%new_notif_data); + } + +} + +#-------------------------------------------------------------------------- + +=head3 rollback + + Description: rollback changes + + Arguments: + Database Handle + Returns: + none + Globals: + + Error: + + Example: + + my $tab = xCAT::Table->new($table,-create =>1,-autocommit=>0); + $tab->rollback(); + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub rollback +{ + my $self = shift; + $self->{dbh}->rollback; +} + +#-------------------------------------------------------------------------- + +=head3 commit + + Description: + Commit changes + Arguments: + Database Handle + Returns: + none + Globals: + + Error: + + Example: + my $tab = xCAT::Table->new($table,-create =>1,-autocommit=>0); + $tab->commit(); + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub commit +{ + my $self = shift; + $self->{dbh}->commit; +} + +#-------------------------------------------------------------------------- + +=head3 setAttribs + + Description: + + Arguments: + Key name + Key value + Hash reference of column-value pairs to set + + Returns: + None + Globals: + + Error: + + Example: + my $tab = xCAT::Table->new( 'ppc', -create=>1, -autocommit=>0 ); + $keyhash{'node'} = $name; + $updates{'type'} = lc($type); + $updates{'id'} = $lparid; + $updates{'hcp'} = $server; + $updates{'profile'} = $prof; + $updates{'frame'} = $frame; + $updates{'mtms'} = "$model*$serial"; + $tab->setAttribs( \%keyhash,\%updates ); + $tab->commit; + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub setAttribs +{ + + #Takes three arguments: + #-Key name + #-Key value + #-Hash reference of column-value pairs to set + my $self = shift; + my %keypairs = %{shift()}; + + #my $key = shift; + #my $keyval=shift; + my $elems = shift; + my $cols = ""; + my @bind = (); + my $action; + my @notif_data; + my $qstring = "SELECT * FROM " . $self->{tabname} . " WHERE "; + my @qargs = (); + foreach (keys %keypairs) + { + $qstring .= "$_ = ? AND "; + push @qargs, $keypairs{$_}; + } + $qstring =~ s/ AND \z//; + my $query = $self->{dbh}->prepare($qstring); + $query->execute(@qargs); + + #get the first row + my $data = $query->fetchrow_arrayref(); + if (defined $data) + { + $action = "u"; + } + else + { + $action = "a"; + } + + #prepare the notification data + my $notif = + xCAT::NotifHandler->needToNotify($self->{tabname}, $action); + if ($notif == 1) + { + if ($action eq "u") + { + + #put the column names at the very front + push(@notif_data, $query->{NAME}); + + #copy the data out because fetchall_arrayref overrides the data. + my @first_row = @$data; + push(@notif_data, \@first_row); + + #get the rest of the rows + my $temp_data = $query->fetchall_arrayref(); + foreach (@$temp_data) + { + push(@notif_data, $_); + } + } + } + + $query->finish(); + + if ($action eq "u") + { + + #update the rows + $action = "u"; + for my $col (keys %$elems) + { + $cols = $cols . $col . " = ?,"; + push @bind, (($$elems{$col} =~ /NULL/) ? undef: $$elems{$col}); + } + chop($cols); + my $cmd = "UPDATE " . $self->{tabname} . " set $cols where "; + foreach (keys %keypairs) + { + if (ref($keypairs{$_})) + { + $cmd .= $_ . " = '" . $keypairs{$_}->[0] . "' AND "; + } + else + { + $cmd .= $_ . " = '" . $keypairs{$_} . "' AND "; + } + } + $cmd =~ s/ AND \z//; + my $sth = $self->{dbh}->prepare($cmd); + my $err = $sth->execute(@bind); + if (not defined($err)) + { + return (undef, $sth->errstr); + } + } + else + { + + #insert the rows + $action = "a"; + @bind = (); + $cols = ""; + foreach (keys %keypairs) + { + $cols .= $_ . ","; + push @bind, $keypairs{$_}; + } + for my $col (keys %$elems) + { + $cols = $cols . $col . ","; + push @bind, $$elems{$col}; + } + chop($cols); + my $qstring = 'INSERT INTO ' . $self->{tabname} . " ($cols) VALUES ("; + for (@bind) + { + $qstring = $qstring . "?,"; + } + $qstring =~ s/,$/)/; + my $sth = $self->{dbh}->prepare($qstring); + my $err = $sth->execute(@bind); + if (not defined($err)) + { + return (undef, $sth->errstr); + } + } + + #notify the interested parties + if ($notif == 1) + { + + #create new data ref + my %new_notif_data = %keypairs; + foreach (keys %$elems) + { + $new_notif_data{$_} = $$elems{$_}; + } + xCAT::NotifHandler->notify($action, $self->{tabname}, + \@notif_data, \%new_notif_data); + } + return 0; +} + +#-------------------------------------------------------------------------- + +=head3 getNodeAttribs + + Description: Retrieves the requested attribute + + Arguments: + Table handle + Noderange + Attribute type array + Returns: + + Attribute hash ( key attribute type) + Globals: + + Error: + + Example: + my $ostab = xCAT::Table->new('nodetype'); + my $ent = $ostab->getNodeAttribs($node,['profile','os','arch']); + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub getNodeAttribs +{ + my $self = shift; + my $node = shift; + my @attribs = @{shift()}; + my ($data, $extra) = $self->getNodeAttribs_nosub($node, \@attribs); + if ($extra) { return undef; } # return (undef,"Ambiguous query"); } + defined($data) + || return undef; #(undef,"No matching entry found in configuration"); + my $attrib; + foreach $attrib (@attribs) + { + + if ($data->{$attrib} =~ /^\/.*\/.*\//) + { + my $exp = substr($data->{$attrib}, 1); + chop $exp; + my @parts = split('/', $exp, 2); + $node =~ s/$parts[0]/$parts[1]/; + $data->{$attrib} = $node; + } + elsif ($data->{$attrib} =~ /^\|.*\|.*\|$/) + { + + #Perform arithmetic and only arithmetic operations in bracketed issues on the right. + #Tricky part: don't allow potentially dangerous code, only eval if + #to-be-evaled expression is only made up of ()\d+-/%$ + #Futher paranoia? use Safe module to make sure I'm good + my $exp = substr($data->{$attrib}, 1); + chop $exp; + my @parts = split('\|', $exp, 2); + my $curr; + my $next; + my $prev; + my $retval = $parts[1]; + ($curr, $next, $prev) = + extract_bracketed($retval, '()', qr/[^()]*/); + + while ($curr) + { + + #my $next = $comps[0]; + if ($curr =~ /^[\{\}()\-\+\/\%\*\$\d]+$/) + { + use integer + ; #We only allow integer operations, they are the ones that make sense for the application + my $value = $node; + $value =~ s/$parts[0]/$curr/ee; + $retval = $prev . $value . $next; + } + else + { + print "$curr is bad\n"; + } + ($curr, $next, $prev) = + extract_bracketed($retval, '()', qr/[^()]*/); + } + $data->{$attrib} = $retval; + + #print Dumper(extract_bracketed($parts[1],'()',qr/[^()]*/)); + #use text::balanced extract_bracketed to parse earch atom, make sure nothing but arith operators, parans, and numbers are in it to guard against code execution + } + } + return $data; +} + +#-------------------------------------------------------------------------- + +=head3 getNodeAttribs_nosub + + Description: + + Arguments: + + Returns: + + Globals: + + Error: + + Example: + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub getNodeAttribs_nosub +{ + my $self = shift; + my $node = shift; + my $attref = shift; + my $data; + my $tent; + my $return = 0; + foreach (@$attref) + { + ($tent) = $self->getNodeAttribs_nosub_returnany($node, [$_]); + if ($tent and defined($tent->{$_})) + { + $return = 1; + $data->{$_} = $tent->{$_}; + } + } + if ($return) + { + return $data; + } + else + { + return undef; + } +} + +#-------------------------------------------------------------------------- + +=head3 getNodeAttribs_nosub_returnany + + Description: + + Arguments: + + Returns: + + Globals: + + Error: + + Example: + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub getNodeAttribs_nosub_returnany +{ #This is the original function + my $self = shift; + my $node = shift; + my @attribs = @{shift()}; + + #my $recurse = ((scalar(@_) == 1) ? shift : 1); + my ($data, $extra) = $self->getAttribs({node => $node}, @attribs); + if (!defined($data)) + { + my ($nodeghash) = + $self->{nodelist}->getAttribs({node => $node}, 'groups'); + unless (defined($nodeghash) && defined($nodeghash->{groups})) + { + return undef; + } + my @nodegroups = split(/,/, $nodeghash->{groups}); + my $group; + foreach $group (@nodegroups) + { + ($data, $extra) = $self->getAttribs({node => $group}, @attribs); + if ($data != undef) + { + if ($data->{node}) { $data->{node} = $node; } + return ($data, $extra); + } + } + } + else + { + + #Don't need to 'correct' node attribute, considering result of the if that governs this code block? + return ($data, $extra); + } + return undef; #Made it here, config has no good answer +} + +#-------------------------------------------------------------------------- + +=head3 getAllEntries + + Description: Read entire table + + Arguments: + Table handle + Returns: + Hash containing all rows in table + Globals: + + Error: + + Example: + + my $tabh = xCAT::Table->new($table); + my $recs=$tabh->getAllEntries(); + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub getAllEntries +{ + my $self = shift; + my @rets; + my $query = $self->{dbh}->prepare('SELECT * FROM ' . $self->{tabname}); + $query->execute(); + while (my $data = $query->fetchrow_hashref()) + { + foreach (keys %$data) + { + if ($data->{$_} =~ /^$/) + { + $data->{$_} = undef; + } + } + push @rets, $data; + } + $query->finish(); + return \@rets; +} + +#-------------------------------------------------------------------------- + +=head3 getAllAttribsWhere + + Description: Get all attributes with "where" clause + + Arguments: + Database Handle + Where clause + Returns: + Array of attributes + Globals: + + Error: + + Example: + $nodelist->getAllAttribsWhere("groups like '%".$atom."%'",'node','group'); + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub getAllAttribsWhere +{ + + #Takes a list of attributes, returns all records in the table. + my $self = shift; + my $whereclause = shift; + my @attribs = @_; + my @results = (); + my $query = + $self->{dbh}->prepare('SELECT * FROM ' + . $self->{tabname} + . ' WHERE (' + . $whereclause + . ") and (disable is NULL or disable in ('0','no','NO','no'))"); + $query->execute(); + while (my $data = $query->fetchrow_hashref()) + { + my %newrow = (); + foreach (@attribs) + { + unless ($data->{$_} =~ /^$/ || !defined($data->{$_})) + { #The reason we do this is to undef fields in rows that may still be returned.. + $newrow{$_} = $data->{$_}; + } + } + if (keys %newrow) + { + push(@results, \%newrow); + } + } + $query->finish(); + return @results; +} + +#-------------------------------------------------------------------------- + +=head3 getAllNodeAttribs + + Description: Get all the node attributes values for the input table on the + attribute list + + Arguments: + Table handle + Attribute list + Returns: + Array of attribute values + Globals: + + Error: + + Example: + my @entries = $self->{switchtab}->getAllNodeAttribs(['port','switch']); + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub getAllNodeAttribs +{ + + #Extract and substitute every node record, expanding groups and substituting as getNodeAttribs does + my $self = shift; + my $attribq = shift; + my @results = (); + my %donenodes + ; #Remember those that have been done once to not return same node multiple times + my $query = + $self->{dbh}->prepare('SELECT node FROM ' + . $self->{tabname} + . " WHERE disable is NULL or disable in ('','0','no','NO','no')"); + $query->execute(); + while (my $data = $query->fetchrow_hashref()) + { + + unless ($data->{node} =~ /^$/ || !defined($data->{node})) + { #ignore records without node attrib, not possible? + my @nodes = + noderange($data->{node}) + ; #expand node entry, to make groups expand + foreach (@nodes) + { + if ($donenodes{$_}) { next; } + my $attrs; + my $nde = $_; + + #if ($self->{giveand}) { #software requests each attribute be independently inherited + # foreach (@attribs) { + # my $attr = $self->getNodeAttribs($nde,$_); + # $attrs->{$_}=$attr->{$_}; + # } + #} else { + $attrs = + $self->getNodeAttribs($_, $attribq) + ; #Logic moves to getNodeAttribs + #} + #populate node attribute by default, this sort of expansion essentially requires it. + $attrs->{node} = $_; + $donenodes{$_} = 1; + push @results, $attrs; #$self->getNodeAttribs($_,@attribs); + } + } + } + $query->finish(); + return @results; +} + +#-------------------------------------------------------------------------- + +=head3 getAllAttribs + + Description: Returns a list of records in the input table for the input + list of attributes. + + Arguments: + Table handle + List of attributes + Returns: + Array of attribute values + Globals: + + Error: + + Example: + $nodelisttab = xCAT::Table->new("nodelist"); + my @attribs = ("node"); + @nodes = $nodelisttab->getAllAttribs(@attribs); + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub getAllAttribs +{ + + #Takes a list of attributes, returns all records in the table. + my $self = shift; + my @attribs = @_; + my @results = (); + my $query = + $self->{dbh}->prepare('SELECT * FROM ' + . $self->{tabname} + . " WHERE disable is NULL or disable in ('','0','no','NO','no')"); + $query->execute(); + while (my $data = $query->fetchrow_hashref()) + { + my %newrow = (); + foreach (@attribs) + { + unless ($data->{$_} =~ /^$/ || !defined($data->{$_})) + { #The reason we do this is to undef fields in rows that may still be returned.. + $newrow{$_} = $data->{$_}; + } + } + if (keys %newrow) + { + push(@results, \%newrow); + } + } + $query->finish(); + return @results; +} + +#-------------------------------------------------------------------------- + +=head3 delEntries + + Description: Delete table entries + + Arguments: + Table Handle + Entry to delete + Returns: + + Globals: + + Error: + + Example: + my $table=xCAT::Table->new("notification", -create => 1,-autocommit => 0); + my %key_col = (filename=>$fname); + $table->delEntries(\%key_col); + $table->commit; + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub delEntries +{ + my $self = shift; + my $keyref = shift; + my %keypairs; + if ($keyref) + { + %keypairs = %{$keyref}; + } + + my $notif = xCAT::NotifHandler->needToNotify($self->{tabname}, 'd'); + my @notif_data; + if ($notif == 1) + { + my $qstring = "SELECT * FROM " . $self->{tabname}; + if ($keyref) { $qstring .= " WHERE "; } + my @qargs = (); + foreach (keys %keypairs) + { + $qstring .= "$_ = ? AND "; + push @qargs, $keypairs{$_}; + } + $qstring =~ s/ AND \z//; + my $query = $self->{dbh}->prepare($qstring); + $query->execute(@qargs); + + #prepare the notification data + #put the column names at the very front + push(@notif_data, $query->{NAME}); + my $temp_data = $query->fetchall_arrayref(); + foreach (@$temp_data) + { + push(@notif_data, $_); + } + $query->finish(); + } + + my @stargs = (); + my $delstring = 'DELETE FROM ' . $self->{tabname}; + if ($keyref) { $delstring .= ' WHERE '; } + foreach (keys %keypairs) + { + $delstring .= $_ . ' = ? AND '; + if (ref($keypairs{$_})) + { #XML transformed data may come in mangled unreasonably into listrefs + push @stargs, $keypairs{$_}->[0]; + } + else + { + push @stargs, $keypairs{$_}; + } + } + $delstring =~ s/ AND \z//; + my $stmt = $self->{dbh}->prepare($delstring); + $stmt->execute(@stargs); + $stmt->finish; + + #notify the interested parties + if ($notif == 1) + { + xCAT::NotifHandler->notify("d", $self->{tabname}, \@notif_data, + {}); + } +} + +#-------------------------------------------------------------------------- + +=head3 getAttribs + + Description: + + Arguments: + key + List of attributes + Returns: + Hash of requested attributes + Globals: + + Error: + + Example: + $table = xCAT::Table->new('passwd'); + $tmp=$table->getAttribs({'key'=>'ipmi'},['username','password']; + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub getAttribs +{ + + #Takes two arguments: + #-Node name (will be compared against the 'Node' column) + #-List reference of attributes for which calling code wants at least one of defined + # (recurse argument intended only for internal use.) + # Returns a hash reference with requested attributes defined. + my $self = shift; + + #my $key = shift; + #my $keyval = shift; + my %keypairs = %{shift()}; + my @attribs = @_; + my @return; + my $statement = 'SELECT * FROM ' . $self->{tabname} . ' WHERE '; + my @exeargs; + foreach (keys %keypairs) + { + + if ($keypairs{$_}) + { + $statement .= $_ . " = ? and "; + if (ref($keypairs{$_})) + { #correct for XML process mangling if occurred + push @exeargs, $keypairs{$_}->[0]; + } + else + { + push @exeargs, $keypairs{$_}; + } + } + else + { + $statement .= "$_ is NULL and "; + } + } + $statement .= "(disable is NULL or disable in ('0','no','NO','No','nO'))"; + my $query = $self->{dbh}->prepare($statement); + $query->execute(@exeargs); + my $data; + while ($data = $query->fetchrow_hashref()) + { + my $attrib; + my %rethash; + foreach $attrib (@attribs) + { + unless ($data->{$attrib} =~ /^$/ || !defined($data->{$attrib})) + { #To undef fields in rows that may still be returned + $rethash{$attrib} = $data->{$attrib}; + } + } + if (keys %rethash) + { + push @return, \%rethash; + } + } + $query->finish(); + if (@return) + { + return @return; + } + return undef; +} + +#-------------------------------------------------------------------------- + +=head3 getTable + + Description: Read entire Table + + Arguments: + Table Handle + + Returns: + Array of table rows + Globals: + + Error: + + Example: + my $table=xCAT::Table->new("notification", -create =>0); + my @row_array= $table->getTable; + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub getTable +{ + + # Get contents of table + # Takes no arguments + # Returns an array of hashes containing the entire contents of this + # table. Each array entry contains a pointer to a hash which is + # one row of the table. The row hash is keyed by attribute name. + my $self = shift; + my @return; + my $statement = 'SELECT * FROM ' . $self->{tabname}; + my $query = $self->{dbh}->prepare($statement); + $query->execute(); + my $data; + while ($data = $query->fetchrow_hashref()) + { + my $attrib; + my %rethash; + foreach $attrib (keys %{$data}) + { + $rethash{$attrib} = $data->{$attrib}; + } + if (keys %rethash) + { + push @return, \%rethash; + } + } + $query->finish(); + if (@return) + { + return @return; + } + return undef; +} + +#-------------------------------------------------------------------------- + +=head3 close + + Description: Close out Table transaction + + Arguments: + Table Handle + Returns: + + Globals: + + Error: + + Example: + my $mactab = xCAT::Table->new('mac'); + $mactab->setNodeAttribs($macmap{$mac},{mac=>$mac}); + $mactab->close(); + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub close +{ + my $self = shift; + if ($self->{dbh}) { $self->{dbh}->disconnect(); } +} + +#-------------------------------------------------------------------------- + +=head3 open + + Description: Connect to Database + + Arguments: + Empty Hash + Returns: + Data Base Handle + Globals: + + Error: + + Example: + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub open +{ + my $self = shift; + $self->{dbh} = DBI->connect($self->{connstring}, "", ""); +} + +#-------------------------------------------------------------------------- + +=head3 DESTROY + + Description: Disconnect from Database + + Arguments: + Database Handle + Returns: + + Globals: + + Error: + + Example: + + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub DESTROY +{ + my $self = shift; + if ($self->{dbh}) { $self->{dbh}->disconnect(); } + undef $self->{nodelist}; #Could be circular +} + +1; + diff --git a/perl-xCAT-2.0/xCAT/Template.pm b/perl-xCAT-2.0/xCAT/Template.pm new file mode 100755 index 000000000..fcad4447f --- /dev/null +++ b/perl-xCAT-2.0/xCAT/Template.pm @@ -0,0 +1,213 @@ +#!/usr/bin/perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html + +package xCAT::Template; +use strict; +use xCAT::Table; +use File::Basename; +use File::Path; +use Data::Dumper; +use Sys::Syslog; + +my $table; +my $key; +my $field; +my $idir; +my $node; + +sub subvars { + my $self = shift; + my $inf = shift; + my $outf = shift; + $node = shift; + my $outh; + my $inh; + $idir = dirname($inf); + open($inh,"<",$inf); + unless ($inh) { + return undef; + } + mkpath(dirname($outf)); + open($outh,">",$outf); + unless($outh) { + return undef; + } + my $inc; + #First load input into memory.. + while (<$inh>) { + $inc.=$_; + } + close($inh); + my $master; + my $sitetab = xCAT::Table->new('site'); + my $noderestab = xCAT::Table->new('noderes'); + (my $et) = $sitetab->getAttribs({key=>"master"},'value'); + if ($et and $et->{value}) { + $master = $et->{value}; + } + $et = $noderestab->getNodeAttribs($node,['xcatmaster']); + if ($et and $et->{'xcatmaster'}) { + $master = $et->{'xcatmaster'}; + } + unless ($master) { + die "Unable to identify master for $node"; + } + $ENV{XCATMASTER}=$master; + #FIRST, do *all* includes, recursive and all + my $doneincludes=0; + while (not $doneincludes) { + $doneincludes=1; + if ($inc =~ /#INCLUDE:[^#]+#/) { + $doneincludes=0; + $inc =~ s/#INCLUDE:([^#]+)#/includefile($1)/eg; + } + } + #ok, now do everything else.. + $inc =~ s/#COMMAND:([^#]+)#/command($1)/eg; + $inc =~ s/#TABLE:([^:]+):([^:]+):([^#]+)#/tabdb($1,$2,$3)/eg; + $inc =~ s/#CRYPT:([^:]+):([^:]+):([^#]+)#/crydb($1,$2,$3)/eg; + $inc =~ s/#XCATVAR:([^#]+)#/envvar($1)/eg; + $inc =~ s/#ENV:([^#]+)#/envvar($1)/eg; + print $outh $inc; + close($outh); + return 1; +} +sub includefile +{ + my $file = shift; + my $text = ""; + unless ($file =~ /^\//) { + $file = $idir."/".$file; + } + + open(INCLUDE,$file) || \ + return "#INCLUDEBAD:cannot open $file#"; + + while() { + $text .= "$_"; + } + + close(INCLUDE); + + chomp($text); + return($text); +} + +sub command +{ + my $command = shift; + my $r; + +# if(($r = `$command`) == 0) { +# chomp($r); +# return($r); +# } +# else { +# return("#$command: failed $r#"); +# } + + $r = `$command`; + chomp($r); + return($r); +} + +sub envvar +{ + my $envvar = shift; + + if($envvar =~ /^\$/) { + $envvar =~ s/^\$//; + } + + return($ENV{$envvar}); +} + +sub genpassword { +#Generate a pseudo-random password of specified length + my $length = shift; + my $password=''; + my $characters= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890'; + srand; #have to reseed, rand is not rand otherwise + while (length($password) < $length) { + $password .= substr($characters,int(rand 63),1); + } + return $password; +} + +sub crydb +{ + my $result = tabdb(@_); + unless ($result =~ /^\$1\$/) { + $result = crypt($result,'$1$'.genpassword(8)); + } + return $result; +} +sub tabdb +{ + my $table = shift; + my $key = shift; + my $field = shift; + my $tabh = xCAT::Table->new($table); + my $ent; + if ($key eq "THISNODE" or $key eq '$NODE') { + $ent = $tabh->getNodeAttribs($node,[$field]); + } else { + my %kp; + foreach (split /,/,$key) { + my $key; + my $val; + ($key,$val) = split /=/,$_; + $kp{$key}=$val; + } + ($ent) = $tabh->getAttribs(\%kp,$field); + } + $tabh->close; + unless($ent and $ent->{$field}) { + return "#TABLEBAD:$table:field $field not found#"; + } + return $ent->{$field}; + + + #if($key =~ /^\$/) { + # $key =~ s/^\$//; + # $key = $ENV{$key}; + #} + #if($field =~ /^\$/) { + # $field =~ s/^\$//; + # $field = $ENV{$field}; + #} + #if($field == '*') { + # $field = 1; + # $all = 1; + #} + + #--$field; + + #if($field < 0) { + # return "#TABLE:field not found#" + #} + + #open(TAB,$table) || \ + # return "#TABLE:cannot open $table#"; + + #while() { + # if(/^$key(\t|,| )/) { + # m/^$key(\t|,| )+(.*)/; + # if($all == 1) { + # return "$2"; + # } + # @fields = split(',',$2); + # if(defined $fields[$field]) { + # return "$fields[$field]"; + # } + # else { + # return "#TABLE:field not found#" + # } + # } + #} + + #close(TAB); + #return "#TABLE:key not found#" +} + +1; diff --git a/perl-xCAT-2.0/xCAT/Utils.pm b/perl-xCAT-2.0/xCAT/Utils.pm new file mode 100644 index 000000000..2f7439f06 --- /dev/null +++ b/perl-xCAT-2.0/xCAT/Utils.pm @@ -0,0 +1,411 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT::Utils; +use xCAT::Table; +use xCAT::Schema; +use Data::Dumper; +use xCAT::NodeRange; + +#-------------------------------------------------------------------------------- + +=head1 xCAT::Utils + +=head2 Package Description + +This program module file, is a set of utilities used by xCAT commands. + + + +=cut + +#-------------------------------------------------------------------------------- + +=head3 quote + + Quote a string, taking into account embedded quotes. This function is most + useful when passing string through the shell to another cmd. It handles one + level of embedded double quotes, single quotes, and dollar signs. + + Arguments: + string to quote + Returns: + quoted string + Globals: + none + Error: + none + Example: + if (defined($$opthashref{'WhereStr'})) { + $where = xCAT::Utils->quote($$opthashref{'WhereStr'}); + } + Comments: + none + +=cut + +#-------------------------------------------------------------------------------- +sub quote +{ + my ($class, $str) = @_; + + # if the value has imbedded double quotes, use single quotes. If it also has + # single quotes, escape the double quotes. + if (!($str =~ /\"/)) # no embedded double quotes + { + $str =~ s/\$/\\\$/sg; # escape the dollar signs + $str =~ s/\`/\\\`/sg; + $str = qq("$str"); + } + elsif (!($str =~ /\'/)) + { + $str = qq('$str'); + } # no embedded single quotes + else # has both embedded double and single quotes + { + + # Escape the double quotes. (Escaping single quotes does not seem to work + # in the shells.) + $str =~ s/\"/\\\"/sg; #" this comment helps formating + $str =~ s/\$/\\\$/sg; # escape the dollar signs + $str =~ s/\`/\\\`/sg; + $str = qq("$str"); + } +} + +#------------------------------------------------------------------------------- + +=head3 isAIX + + returns 1 if localHost is AIX + + Arguments: + none + Returns: + 1 - localHost is AIX + 0 - localHost is some other platform + Globals: + none + Error: + none + Example: + if (xCAT::Utils->isAIX()) { blah; } + Comments: + none + +=cut + +#------------------------------------------------------------------------------- + +sub isAIX +{ + if ($^O =~ /^aix/i) { return 1; } + else { return 0; } +} + +#------------------------------------------------------------------------------- + +=head3 isLinux + + returns 1 if localHost is Linux + + Arguments: + none + Returns: + 1 - localHost is Linux + 0 - localHost is some other platform + Globals: + none + Error: + none + Example: + if (xCAT::Utils->isLinux()) { blah; } + Comments: + none + +=cut + +#------------------------------------------------------------------------------- + +sub isLinux +{ + if ($^O =~ /^linux/i) { return 1; } + else { return 0; } +} + +#------------------------------------------------------------------------------- + +=head3 make_node_list_file + + Makes a node list file. + + Arguments: + (\@list_of_nodes) - reference to an arrary of nodes. + Returns: + $file_name and sets the global var: $::NODE_LIST_FILE + Globals: + the ENV vars: DSH_LIST, RPOWER_LIST, RCONSOLE_LIST + Error: + None documented + Example: + xCAT::Utils->make_node_list_file(\@nodelist); + + Comments: + IMPORTANT: + Make sure to cleanup afterwards with: + + xCAT::Utils->close_delete_file($file_handle, $file_name) + +=cut + +#-------------------------------------------------------------------------------- + +sub make_node_list_file +{ + my ($class, $ref_node_list) = @_; + my @node_list = @$ref_node_list; + srand(time | $$); #random number generator start + + my $file = "/tmp/csm_$$"; + while (-e $file) + { + $file = xCAT::Utils->CreateRandomName($file); + } + + open($::NODE_LIST_FILE, ">$file") + or MsgUtils->message("E", "Cannot write to file: $file\n"); + foreach my $node (@node_list) + { + print $::NODE_LIST_FILE "$node\n"; + } + return $file; +} + +#-------------------------------------------------------------------------------- + +=head3 CreateRandomName + + Create a randome file name. + Arguments: + Prefix of name + Returns: + Prefix with 8 random letters appended + Error: + none + Example: + $file = xCAT::Utils->CreateRandomName($namePrefix); + Comments: + None + =cut + +#------------------------------------------------------------------------------- +sub CreateRandomName +{ +my ($class, $name) = @_; + +my $nI; +for ($nI = 0 ; $nI < 8 ; $nI++) +{ + my $char = ('a' .. 'z', 'A' .. 'Z')[int(rand(52)) + 1]; + $name .= $char; +} + $name; +} + +#----------------------------------------------------------------------- + +=head3 +close_delete_file. + + Arguments: + file handle,filename + Returns: + none + Globals: + none + Error: + undef + Example: + xCAT::Utils->close_delete_file($file_handle, $file_name); + Comments: + none + +=cut + +#------------------------------------------------------------------------ +sub close_delete_file +{ + my ($class, $file_handle, $file_name) = @_; + close $file_handle; + + unlink($file_name); +} + +#----------------------------------------------------------------------- + +=head3 + list_all_nodes + + Arguments: + + Returns: + an array of all define nodes from the nodelist table + Globals: + none + Error: + undef + Example: + @nodes=xCAT::Utils->list_all_nodes; + Comments: + none + +=cut + +#------------------------------------------------------------------------ +sub list_all_nodes +{ + my @nodes; + my @nodelist; + my $nodelisttab; + if ($nodelisttab = xCAT::Table->new("nodelist")) + { + my @attribs = ("node"); + @nodes = $nodelisttab->getAllAttribs(@attribs); + foreach my $node (@nodes) + { + push @nodelist, $node->{node}; + } + } + else + { + xCAT::MsgUtils->message("E", " Could not read the nodelist table\n"); + } + return @nodelist; +} + +#----------------------------------------------------------------------- + +=head3 + list_all_nodegroups + + Arguments: + + Returns: + an array of all define node groups from the nodelist table + Globals: + none + Error: + undef + Example: + @nodegrps=xCAT::Utils->list_all_nodegroups; + Comments: + none + +=cut + +#------------------------------------------------------------------------ +sub list_all_node_groups +{ + my @grouplist; + my @grouplist2; + my @distinctgroups; + my $nodelisttab; + if ($nodelisttab = xCAT::Table->new("nodelist")) + { + my @attribs = ("groups"); + @grouplist = $nodelisttab->getAllAttribs(@attribs); + + # build a distinct list of unique group names + foreach my $group (@grouplist) + { + my $gnames = $group->{groups}; + my @groupnames = split ",", $gnames; + foreach my $groupname (@groupnames) + { + if (!grep(/$groupname/, @distinctgroups)) + { # not already in list + push @distinctgroups, $groupname; + } + } + } + } + else + { + xCAT::MsgUtils->message("E", " Could not read the nodelist table\n"); + } + return @distinctgroups; +} + +#----------------------------------------------------------------------- + +=head3 + list_nodes_in_nodegroup + + Arguments: nodegroup + + Returns: + an array of all define nodes in the node group + + Globals: + none + Error: + undef + Example: + @nodes=xCAT::Utils->list_nodes_in_nodegroup($group); + Comments: + none + +=cut + +#------------------------------------------------------------------------ +sub list_nodes_in_nodegroups +{ + my ($class, $group) = @_; + $req->{noderange}->[0] = $group; + my @nodes = noderange($req->{noderange}->[0]); + return @nodes; +} + +#----------------------------------------------------------------------- + +=head3 + get_site_attribute + + Arguments: + + Returns: + The value of the attribute requested from the site table + Globals: + none + Error: + undef + Example: + @attr=xCAT::Utils->get_site_attribute($attribute); + Comments: + none + +=cut + +#------------------------------------------------------------------------ +sub get_site_attribute +{ + my ($class, $attr) = @_; + my $values; + + my $sitetab = xCAT::Table->new('site'); + if ($sitetab) + { + (my $ref) = $sitetab->getAttribs({key => $attr}, value); + if ($ref and $ref->{value}) + { + $values = $ref->{value}; + } + } + else + { + xCAT::MsgUtils->message("E", " Could not read the site table\n"); + + } + + return $values; +} +1; diff --git a/perl-xCAT-2.0/xCAT/data/ibmleds.pm b/perl-xCAT-2.0/xCAT/data/ibmleds.pm new file mode 100644 index 000000000..0a7b12c8f --- /dev/null +++ b/perl-xCAT-2.0/xCAT/data/ibmleds.pm @@ -0,0 +1,89 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#ibmleds,tab, mfg,prod_id,led_id,desc +package xCAT::data::ibmleds; + +my %x3755_leds = ( +0x0065=>'Dimm 6', +0x0011=>'VRM', +0x00d9=>'Alert', +0x0030=>'CPU1', +0x0032=>'CPU3', +0x00ca=>'Dimm 27', +0x0075=>'PCI 6', +0x00c1=>'Dimm 18', +0x0019=>'NMI', +0x0040=>'UNKNOWN', +0x0067=>'Dimm 8', +0x000f=>'RAID', +0x00c4=>'Dimm 21', +0x0057=>'Fan 8', +0x00d0=>'ServeRAID 8k Batt', +0x0069=>'Dimm 10', +0x000e=>'BOARD', +0x00c3=>'Dimm 20', +0x00cf=>'Dimm 32', +0x006f=>'Dimm 16', +0x0054=>'Fan 5', +0x00cd=>'Dimm 30', +0x0000=>'FAULT', +0x0014=>'FAN', +0x00c8=>'Dimm 25', +0x006c=>'Dimm 13', +0x0061=>'Dimm 2', +0x00c2=>'Dimm 19', +0x0074=>'PCI 5', +0x006a=>'Dimm 11', +0x00b9=>'CPU2_BOARD', +0x0047=>'UNKNOWN', +0x0050=>'Fan 1', +0x0072=>'PCI 3', +0x00c5=>'Dimm 22', +0x00c0=>'Dimm 17', +0x0070=>'PCI 1', +0x00c9=>'Dimm 26', +0x0006=>'CNFG', +0x006b=>'Dimm 12', +0x00d8=>'BK_Blue', +0x0068=>'Dimm 9', +0x00bb=>'CPU4_BOARD', +0x00c6=>'Dimm 23', +0x0031=>'CPU2', +0x0010=>'CPU', +0x00b8=>'CPU1_BOARD', +0x0056=>'Fan 7', +0x0063=>'Dimm 4', +0x00b0=>'HTX', +0x0001=>'LOCATION', +0x000b=>'SEER', +0x0013=>'DASD', +0x00cb=>'Dimm 28', +0x0052=>'Fan 3', +0x0064=>'Dimm 5', +0x001c=>'TEMP', +0x00c7=>'Dimm 24', +0x0060=>'Dimm 1', +0x00d1=>'ServeRAID 8k Err', +0x0073=>'PCI 4', +0x0015=>'MEM', +0x0003=>'INFO', +0x006e=>'Dimm 15', +0x0071=>'PCI 2', +0x00ba=>'CPU3_BOARD', +0x00ce=>'Dimm 31', +0x001b=>'OVERSPEC', +0x0041=>'UNKNOWN', +0x006d=>'Dimm 14', +0x0051=>'Fan 2', +0x001e=>'SP', +0x0066=>'Dimm 7', +0x0053=>'Fan 4', +0x0055=>'Fan 6', +0x0033=>'CPU4', +0x00cc=>'Dimm 29', +0x0020=>'PCI', +0x0062=>'Dimm 3', +); + +%leds = ( + "2,14" => \%x3755_leds, +); diff --git a/perl-xCAT-2.0/xCAT/data/ipmigenericevents.pm b/perl-xCAT-2.0/xCAT/data/ipmigenericevents.pm new file mode 100644 index 000000000..955541a92 --- /dev/null +++ b/perl-xCAT-2.0/xCAT/data/ipmigenericevents.pm @@ -0,0 +1,66 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#ipmigenericevent.tab", +#DO NOT EDIT", +#", +#",,,", +package xCAT::data::ipmigenericevents; + +%ipmigenericevents = ( +"01h,00h"=>"Threshold,Lower Non-critical - going low", +"01h,01h"=>"Threshold,Lower Non-critical - going high", +"01h,02h"=>"Threshold,Lower Critical - going low", +"01h,03h"=>"Threshold,Lower Critical - going high", +"01h,04h"=>"Threshold,Lower Non-recoverable - going low", +"01h,05h"=>"Threshold,Lower Non-recoverable - going high", +"01h,06h"=>"Threshold,Upper Non-critical - going low", +"01h,07h"=>"Threshold,Upper Non-critical - going high", +"01h,08h"=>"Threshold,Upper Critical - going low", +"01h,09h"=>"Threshold,Upper Critical - going high", +"01h,0Ah"=>"Threshold,Upper Non-recoverable - going low", +"01h,0Bh"=>"Threshold,Upper Non-recoverable - going high", +"02h,00h"=>"Discrete,Transition to Idle", +"02h,01h"=>"Discrete,Transition to Active", +"02h,02h"=>"Discrete,Transition to Busy", +"03h,00h"=>"'digital' Discrete,State Deasserted", +"03h,01h"=>"'digital' Discrete,State Asserted", +"04h,00h"=>"'digital' Discrete,Predictive Failure Deasserted", +"04h,01h"=>"'digital' Discrete,Predictive Failure Asserted", +"05h,00h"=>"'digital' Discrete,Limit Not Exceeded", +"05h,01h"=>"'digital' Discrete,Limit Exceeded", +"06h,00h"=>"'digital' Discrete,Performance Met", +"06h,01h"=>"'digital' Discrete,Performance Lags", +"07h,00h"=>"Discrete,transition to OK", +"07h,01h"=>"Discrete,transition to Non-Critical from OK", +"07h,02h"=>"Discrete,transition to Critical from Less Severe", +"07h,03h"=>"Discrete,transition to Non-recoverable from Less Severe", +"07h,04h"=>"Discrete,transition to Non-Critical from More Severe", +"07h,05h"=>"Discrete,transition to Critical from Non-recoverable", +"07h,06h"=>"Discrete,transition to Non-recoverable", +"07h,07h"=>"Discrete,Monitor", +"07h,08h"=>"Discrete,Informational", +"08h,00h"=>"'digital' Discrete,Device Removed / Device Absent", +"08h,01h"=>"'digital' Discrete,Device Inserted / Device Present", +"09h,00h"=>"'digital' Discrete,Device Disabled", +"09h,01h"=>"'digital' Discrete,Device Enabled", +"0Ah,00h"=>"Discrete,transition to Running", +"0Ah,01h"=>"Discrete,transition to In Test", +"0Ah,02h"=>"Discrete,transition to Power Off", +"0Ah,03h"=>"Discrete,transition to On Line", +"0Ah,04h"=>"Discrete,transition to Off Line", +"0Ah,05h"=>"Discrete,transition to Off Duty", +"0Ah,06h"=>"Discrete,transition to Degraded", +"0Ah,07h"=>"Discrete,transition to Power Save", +"0Ah,08h"=>"Discrete,Install Error", +"0Bh,00h"=>"Discrete,Redundancy Regained", +"0Bh,01h"=>"Discrete,Redundancy Lost", +"0Bh,02h"=>"Discrete,Redundancy Degraded", +"0Bh,03h"=>"Discrete,Non-redundant:Sufficient resources", +"0Bh,04h"=>"Discrete,Non-redundant:Sufficient from insufficient resources", +"0Bh,05h"=>"Discrete,Non-redundant:Insufficient resources", +"0Bh,06h"=>"Discrete,Redundancy Degraded from Fully Reduntant", +"0Bh,06h"=>"Discrete,Redundancy Degraded from Non-Reduntant", +"0Ch,00h"=>"Discrete,D0 Power State", +"0Ch,01h"=>"Discrete,D1 Power State", +"0Ch,02h"=>"Discrete,D2 Power State", +"0Ch,03h"=>"Discrete,D3 Power State", +); diff --git a/perl-xCAT-2.0/xCAT/data/ipmisensorevents.pm b/perl-xCAT-2.0/xCAT/data/ipmisensorevents.pm new file mode 100644 index 000000000..237076f7c --- /dev/null +++ b/perl-xCAT-2.0/xCAT/data/ipmisensorevents.pm @@ -0,0 +1,169 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#ipmisensorevent.tab", +#DO NOT EDIT", +#", +#",,,", +package xCAT::data::ipmisensorevents; +%ipmisensorevents = ( +"00h,-"=>"reserved,Reserved", +"01h,-"=>"Temperature,Temperature", +"02h,-"=>"Voltage,Voltage", +"03h,-"=>"Current,Current", +"04h,-"=>"Fan,Fan", +"05h,00h"=>"Physical Security,General Chassis Intrusion", +"05h,01h"=>"Physical Security,Drive Bay Intrusion", +"05h,02h"=>"Physical Security,I/O Card area Intrusion", +"05h,03h"=>"Physical Security,Processor area Intrusion", +"05h,04h"=>"Physical Security,LAN Leash Lost (system has been unplugged from LAN)", +"05h,05h"=>"Physical Security,Unauthorized Dock/Undock", +"05h,06h"=>"Physical Security,FAN area intrusion", +"06h,00h"=>"Platform Security Violation Attempt,Secure Mode Violation Attempt", +"06h,01h"=>"Platform Security Violation Attempt,Pre-boot Password Violation - user password", +"06h,02h"=>"Platform Security Violation Attempt,Pre-boot Password Violation Attempt - setup password", +"06h,03h"=>"Platform Security Violation Attempt,Pre-boot Password Violation - network boot password", +"06h,04h"=>"Platform Security Violation Attempt,Other pre-boot Password Violation", +"06h,05h"=>"Platform Security Violation Attempt,Out-of-band Access Password Violation", +"07h,00h"=>"Processor,IERR", +"07h,01h"=>"Processor,Thermal Trip", +"07h,02h"=>"Processor,FRB1/BIST Failure", +"07h,03h"=>"Processor,FRB2/Hang in POST Failure", +"07h,04h"=>"Processor,FRB3/Processor Startup/Initialization failure (CPU didn't start)", +"07h,05h"=>"Processor,Configuration Error (for DMI)", +"07h,06h"=>"Processor,SM BIOS Uncorrectable CPU-complex Error", +"07h,07h"=>"Processor,Processor Presence Detected", +"07h,08h"=>"Processor,Processor Disabled", +"07h,09h"=>"Processor,Terminator Presence Detected", +"08h,00h"=>"Power Supply,Presence Detected", +"08h,01h"=>"Power Supply,Power Supply Failure Detected", +"08h,02h"=>"Power Supply,Predictive Failure Asserted", +"08h,03h"=>"Power Supply,Power Supply AC lost", +"08h,04h"=>"Power Supply,AC lost or out-of-range", +"08h,05h"=>"Power Supply,AC out-of-range, but present", +"09h,00h"=>"Power Unit,Power Off / Power Down", +"09h,01h"=>"Power Unit,Power Cycle", +"09h,02h"=>"Power Unit,240VA Power Down", +"09h,03h"=>"Power Unit,Interlock Power Down", +"09h,04h"=>"Power Unit,A/C Lost", +"09h,05h"=>"Power Unit,Soft Power Control Failure", +"09h,06h"=>"Power Unit,Power Unit Failure Detected", +"09h,07h"=>"Power Unit,Predictive Failure", +"0Ah,-"=>"Cooling Device,-", +"0Bh,-"=>"Other Units-based Sensor,-", +"0Ch,00h"=>"Memory,Correctable ECC", +"0Ch,01h"=>"Memory,Uncorrectable ECC", +"0Ch,02h"=>"Memory,Parity", +"0Ch,03h"=>"Memory,Memory Scrub Failed (stuck bit)", +"0Ch,04h"=>"Memory,Memory Device Disabled", +"0Ch,05h"=>"Memory,Correctable ECC (loggin limit reached)", +"0Dh,-"=>"Drive Slot,-", +"0Eh,-"=>"POST Memory Resize,-", +"0Fh,00h"=>"System Firmware Progress,POST Error", +"0Fh,01h"=>"System Firmware Progress,System Firmware Hung", +"0Fh,02h"=>"System Firmware Progress,System Firmware Progress", +"10h,00h"=>"Event Logging Disabled,Correctable Memory Error Logging Disabled", +"10h,01h"=>"Event Logging Disabled,Event 'Type' Logging Disabled", +"10h,02h"=>"Event Logging Disabled,Log Area Reset/Cleared", +"10h,03h"=>"Event Logging Disabled,All Event Logging Disabled", +"11h,00h"=>"Watchdog 1,BIOS Watchdog Reset", +"11h,01h"=>"Watchdog 1,OS Watchdog Reset", +"11h,02h"=>"Watchdog 1,OS Watchdog Shut Down", +"11h,03h"=>"Watchdog 1,OS Watchdog Power Down", +"11h,04h"=>"Watchdog 1,OS Watchdog Power Cycle", +"11h,05h"=>"Watchdog 1,OS Watchdog NMI", +"11h,06h"=>"Watchdog 1,OS Watchdog Expired, status only", +"11h,07h"=>"Watchdog 1,OS Watchdog Pre-timeout Interrupt, non-NMI", +"12h,00h"=>"System Event,System Reconfigured", +"12h,01h"=>"System Event,Boot", +"12h,02h"=>"System Event,Undetermined system hardware failure", +"12h,03h"=>"System Event,Entry added to Auxiliary Log", +"12h,04h"=>"System Event,PEF Action", +"13h,00h"=>"Critical Interrupt,Front Panel NMI ", +"13h,01h"=>"Critical Interrupt,Bus Timeout", +"13h,02h"=>"Critical Interrupt,I/O Channel Check NMI", +"13h,03h"=>"Critical Interrupt,Software NMI", +"13h,04h"=>"Critical Interrupt,PCI PERR", +"13h,05h"=>"Critical Interrupt,PCI SERR", +"13h,06h"=>"Critical Interrupt,EISA Fail Safe Timeout", +"13h,07h"=>"Critical Interrupt,Bus Correctable Error", +"13h,08h"=>"Critical Interrupt,Bus Uncorrectable Error", +"13h,09h"=>"Critical Interrupt,Fatal NMI (port 61h, bit 7)", +"14h,00h"=>"Button,Power Button pressed", +"14h,01h"=>"Button,Sleep Button pressed", +"14h,02h"=>"Button,Reset Button pressed", +"15h,-"=>"Module / Board,-", +"16h,-"=>"Microcontroller / Coprocessor,-", +"17h,-"=>"Add-in Card,-", +"18h,-"=>"Chassis,-", +"19h,-"=>"Chip Set,-", +"1Ah,-"=>"Other FRU,-", +"1Bh,-"=>"Cable / Interconnect,-", +"1Ch,-"=>"Terminator,-", +"1Dh,00h"=>"System Boot Initiated,Initiated by power up", +"1Dh,01h"=>"System Boot Initiated,Initiated by hard reset", +"1Dh,02h"=>"System Boot Initiated,Initiated by warm reset", +"1Dh,03h"=>"System Boot Initiated,User requested PXE boot", +"1Dh,04h"=>"System Boot Initiated,Automatic boot to diagnostic", +"1Eh,00h"=>"Boot Error,No bootable media", +"1Eh,01h"=>"Boot Error,Non-bootable diskette left in drive", +"1Eh,02h"=>"Boot Error,PXE Server not found", +"1Eh,03h"=>"Boot Error,Invalid boot sector", +"1Eh,04h"=>"Boot Error,Timeout waiting for user selection of boot source", +"1Fh,00h"=>"OS Boot,A: boot completed", +"1Fh,01h"=>"OS Boot,C: boot completed", +"1Fh,02h"=>"OS Boot,PXE boot completed", +"1Fh,03h"=>"OS Boot,Diagnostic boot completed", +"1Fh,04h"=>"OS Boot,CD-ROM boot completed", +"1Fh,05h"=>"OS Boot,ROM boot completed", +"1Fh,06h"=>"OS Boot,Boot completed - boot device not specified", +"20h,00h"=>"OS Critical Stop,Stop during OS load / initialization", +"20h,01h"=>"OS Critical Stop,Run-time Stop", +"21h,00h"=>"Slot / Connector,Fault Status asserted", +"21h,01h"=>"Slot / Connector,Identify Status asserted", +"21h,02h"=>"Slot / Connector,Slot / Connector Device installed/attached", +"21h,03h"=>"Slot / Connector,Slot / Connector Ready for Device Installation", +"21h,04h"=>"Slot / Connector,Slot/Connector Ready for Device Removal", +"21h,05h"=>"Slot / Connector,Slot Power is Off", +"21h,06h"=>"Slot / Connector,Slot / Connector Device Removal Request", +"21h,07h"=>"Slot / Connector,Interlock asserted", +"21h,08h"=>"Slot / Connector,Slot is Disabled", +"22h,00h"=>"System ACPI Power State,S0 / G0 working", +"22h,01h"=>"System ACPI Power State,S1 sleeping with system h/w & processor context maintained", +"22h,02h"=>"System ACPI Power State,S2 sleeping, processor context lost", +"22h,03h"=>"System ACPI Power State,S3 sleeping, processor & h/w context lost, memory retained.", +"22h,04h"=>"System ACPI Power State,S4 non-volatile sleep / suspend-to disk", +"22h,05h"=>"System ACPI Power State,S5 / G2 soft-off", +"22h,06h"=>"System ACPI Power State,S4 / S5 soft-off, particular S4 / S5 state cannot be determined", +"22h,07h"=>"System ACPI Power State,G3 / Mechanical Off", +"22h,08h"=>"System ACPI Power State,Sleeping in an S1, S2, or S3 states", +"22h,09h"=>"System ACPI Power State,G1 sleeping", +"22h,0Ah"=>"System ACPI Power State,S5 entered by override", +"22h,0Bh"=>"System ACPI Power State,Legacy ON state", +"22h,0Ch"=>"System ACPI Power State,Legacy OFF state", +"22h,0Eh"=>"System ACPI Power State,Unknown", +"23h,00h"=>"Watchdog 2,Timer expired, status only", +"23h,01h"=>"Watchdog 2,Hard Reset", +"23h,02h"=>"Watchdog 2,Power Down", +"23h,03h"=>"Watchdog 2,Power Cycle", +"23h,04h"=>"Watchdog 2,reserved", +"23h,05h"=>"Watchdog 2,reserved", +"23h,06h"=>"Watchdog 2,reserved", +"23h,07h"=>"Watchdog 2,reserved", +"23h,08h"=>"Watchdog 2,Timer interrupt", +"24h,00h"=>"Platform Alert,Platform generated page", +"24h,01h"=>"Platform Alert,Platform generated LAN alert", +"24h,02h"=>"Platform Alert,Platform Event Trap generated, formatted per IPMI PET specification", +"24h,03h"=>"Platform Alert,Platform generated SNMP trap, OEM format", +"25h,00h"=>"Entity Presence,Entity Present", +"25h,01h"=>"Entity Presence,Entity Absent", +"25h,02h"=>"Entity Presence,Entity Disabled", +"26h,-"=>"Monitor ASIC / IC,-", +"27h,00h"=>"LAN,LAN Heartbeat Lost", +"27h,01h"=>"LAN,LAN Heartbeat", +"28h,00h"=>"Management Subsystem Health,sensor access degraded or unavailable", +"28h,01h"=>"Management Subsystem Health,controller access degraded or unavailable", +"28h,02h"=>"Management Subsystem Health,management controller off-line", +"28h,03h"=>"Management Subsystem Health,management controller unavailable", +"29h,00h"=>"Battery,battery low (predictive failure)", +"29h,01h"=>"Battery,battery failed", +"29h,02h"=>"Battery,battery presense detected", +); diff --git a/svn-commit.2.tmp b/svn-commit.2.tmp new file mode 100644 index 000000000..d32dadd56 --- /dev/null +++ b/svn-commit.2.tmp @@ -0,0 +1,4 @@ +xCAT 2.0 core import +--This line, and those below, will be ignored-- + +A . diff --git a/svn-commit.tmp b/svn-commit.tmp new file mode 100644 index 000000000..629529e33 --- /dev/null +++ b/svn-commit.tmp @@ -0,0 +1,4 @@ +Import xCAT 2.0 tree as of 10/26 +--This line, and those below, will be ignored-- + +A . diff --git a/xCAT-client-2.0/LICENSE.html b/xCAT-client-2.0/LICENSE.html new file mode 100644 index 000000000..83d0eebb0 --- /dev/null +++ b/xCAT-client-2.0/LICENSE.html @@ -0,0 +1,326 @@ + + + + + + + +Eclipse Public License - Version 1.0 + + + + + + +
+ +

Eclipse Public License - v 1.0 +

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER +THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, +REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE +OF THIS AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) +in the case of the initial Contributor, the initial code and documentation +distributed under this Agreement, and
+b) in the case of each subsequent Contributor:

+ +

i) +changes to the Program, and

+ +

ii) +additions to the Program;

+ +

where +such changes and/or additions to the Program originate from and are distributed +by that particular Contributor. A Contribution 'originates' from a Contributor +if it was added to the Program by such Contributor itself or anyone acting on +such Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in conjunction +with the Program under their own license agreement, and (ii) are not derivative +works of the Program.

+ +

"Contributor" means any person or +entity that distributes the Program.

+ +

"Licensed Patents " mean patent +claims licensable by a Contributor which are necessarily infringed by the use +or sale of its Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions +distributed in accordance with this Agreement.

+ +

"Recipient" means anyone who +receives the Program under this Agreement, including all Contributors.

+ +

2. GRANT OF RIGHTS

+ +

a) +Subject to the terms of this Agreement, each Contributor hereby grants Recipient +a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly +display, publicly perform, distribute and sublicense the Contribution of such +Contributor, if any, and such derivative works, in source code and object code +form.

+ +

b) +Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free +patent license under Licensed Patents to make, use, sell, offer to sell, import +and otherwise transfer the Contribution of such Contributor, if any, in source +code and object code form. This patent license shall apply to the combination +of the Contribution and the Program if, at the time the Contribution is added +by the Contributor, such addition of the Contribution causes such combination +to be covered by the Licensed Patents. The patent license shall not apply to +any other combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) +Recipient understands that although each Contributor grants the licenses to its +Contributions set forth herein, no assurances are provided by any Contributor +that the Program does not infringe the patent or other intellectual property +rights of any other entity. Each Contributor disclaims any liability to Recipient +for claims brought by any other entity based on infringement of intellectual +property rights or otherwise. As a condition to exercising the rights and +licenses granted hereunder, each Recipient hereby assumes sole responsibility +to secure any other intellectual property rights needed, if any. For example, +if a third party patent license is required to allow Recipient to distribute +the Program, it is Recipient's responsibility to acquire that license before +distributing the Program.

+ +

d) +Each Contributor represents that to its knowledge it has sufficient copyright +rights in its Contribution, if any, to grant the copyright license set forth in +this Agreement.

+ +

3. REQUIREMENTS

+ +

A Contributor may choose to distribute the +Program in object code form under its own license agreement, provided that: +

+ +

a) +it complies with the terms and conditions of this Agreement; and

+ +

b) +its license agreement:

+ +

i) +effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose;

+ +

ii) +effectively excludes on behalf of all Contributors all liability for damages, +including direct, indirect, special, incidental and consequential damages, such +as lost profits;

+ +

iii) +states that any provisions which differ from this Agreement are offered by that +Contributor alone and not by any other party; and

+ +

iv) +states that source code for the Program is available from such Contributor, and +informs licensees how to obtain it in a reasonable manner on or through a +medium customarily used for software exchange.

+ +

When the Program is made available in source +code form:

+ +

a) +it must be made available under this Agreement; and

+ +

b) a +copy of this Agreement must be included with each copy of the Program.

+ +

Contributors may not remove or alter any +copyright notices contained within the Program.

+ +

Each Contributor must identify itself as the +originator of its Contribution, if any, in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

Commercial distributors of software may +accept certain responsibilities with respect to end users, business partners +and the like. While this license is intended to facilitate the commercial use +of the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes the +Program in a commercial product offering, such Contributor ("Commercial +Contributor") hereby agrees to defend and indemnify every other +Contributor ("Indemnified Contributor") against any losses, damages and +costs (collectively "Losses") arising from claims, lawsuits and other +legal actions brought by a third party against the Indemnified Contributor to +the extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may participate +in any such claim at its own expense.

+ +

For example, a Contributor might include the +Program in a commercial product offering, Product X. That Contributor is then a +Commercial Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance claims and +warranties are such Commercial Contributor's responsibility alone. Under this +section, the Commercial Contributor would have to defend claims against the +other Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, +WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and distributing the +Program and assumes all risks associated with its exercise of rights under this +Agreement , including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs or +equipment, and unavailability or interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF +THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES.

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid +or unenforceable under applicable law, it shall not affect the validity or +enforceability of the remainder of the terms of this Agreement, and without +further action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Program itself (excluding combinations of the Program with +other software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the date +such litigation is filed.

+ +

All Recipient's rights under this Agreement +shall terminate if it fails to comply with any of the material terms or +conditions of this Agreement and does not cure such failure in a reasonable +period of time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute +copies of this Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The Agreement +Steward reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement Steward has +the right to modify this Agreement. The Eclipse Foundation is the initial +Agreement Steward. The Eclipse Foundation may assign the responsibility to +serve as the Agreement Steward to a suitable separate entity. Each new version +of the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version of +the Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly stated +in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to +the intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved.

+ +

This Agreement is governed by the laws of the +State of New York and the intellectual property laws of the United States of +America. No party to this Agreement will bring a legal action under this +Agreement more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation.

+ +

 

+ +
+ + + + diff --git a/xCAT-client-2.0/README b/xCAT-client-2.0/README new file mode 100644 index 000000000..6469a99f8 --- /dev/null +++ b/xCAT-client-2.0/README @@ -0,0 +1,8 @@ +This is the README for xCAT - Extreme Cluster Administration Toolkit, +Version 2.0 alpha release. xCAT is a toolkit for the deployment and administration of clusters. +xCAT 2.0 is offered OSS under a EPL license. + +See http://www.eclipse.org/legal/epl-v10.html for license details. + +The xCAT 2.0 code and documentation can be downloaded from http://xcat.org. +For support, go to http://xcat.org for instructions on joining the mailing list. diff --git a/xCAT-client-2.0/usr/bin/getnodecfg b/xCAT-client-2.0/usr/bin/getnodecfg new file mode 100755 index 000000000..c81787f14 --- /dev/null +++ b/xCAT-client-2.0/usr/bin/getnodecfg @@ -0,0 +1,67 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +use xCAT::Table; +use xCAT::NodeRange; +use IO::Socket::SSL; +use XML::Simple; +use Data::Dumper; +use strict; +my $xcathost='localhost:3001'; +if ($ENV{XCATHOST}) { + $xcathost=$ENV{XCATHOST}; +} + +my $server = IO::Socket::SSL->new( + PeerAddr=>$xcathost, + SSL_key_file=>$ENV{HOME}."/.xcat/client-key.pem", + SSL_cert_file=>$ENV{HOME}."/.xcat/client-cert.pem", + SSL_ca_file => $ENV{HOME}."/.xcat/ca.pem", + SSL_use_cert => 1, + #SSL_verify_mode => 1, + ); +die "Connection failure: $!\n" unless ($server); +my $target=shift(@ARGV); +my %request=(); #Start building the hash to XML-ify +$request{command}='getnodeattributes'; +$request{noderange}=$target; + +for (@ARGV) { + my $temp; + my $table; + my $column; + my $value; + ($table,$column) = split('\.',$_,2); + $request{table}=$table; #build/reuse request specific elements + $request{attribute}=$column; + print $server XMLout(\%request,RootName => 'xcatrequest',NoAttr => 1); + alarm(30); + my $response; + while (<$server>) { + alarm(0); + $response .= $_; + if ($response =~ m/<\/xcatresponse>/) { + my $reply=XMLin($response); + $response=""; + if ($reply->{error}) { + printf "ERROR: ".$reply->{error}."\n"; + } + if ($reply->{warning}) { + printf "Warning: ".$reply->{warning}."\n"; + } + if ($reply->{attributes}{$column}) { + printf "$table.$column:".$reply->{attributes}{$column}."\n"; + } + if ($reply->{serverdone}) { + last; + } + } + } +} + + + + #my $tab = xCAT::Table->new($table,-create => 1); + #my $rec = $tab->getAttribs($key,$val,$column); + +# if ($rec->{$column}) { printf $rec->{$column}."\n"; } +#} diff --git a/xCAT-client-2.0/usr/bin/pping b/xCAT-client-2.0/usr/bin/pping new file mode 100755 index 000000000..d2361ef00 --- /dev/null +++ b/xCAT-client-2.0/usr/bin/pping @@ -0,0 +1,73 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#Note, this pping still frontends fping. I think it would be possible to write a perl equivalent, but +#I've not had the time. Net::Ping shows perl code I could see being adapted for a somewhat +#asynchronous ICMP ping (the tcp syn is interesting, but far too limited, and that is currently the only async +#method Net::Ping provides. +use IO::Socket::SSL; +use XML::Simple; +use Data::Dumper; +use IO::Handle; +use IO::Select; +use xCAT::Utils; +use Getopt::Long; +my $interface; +GetOptions("interface=s" => \$interface); +my $xcathost='localhost:3001'; +if ($ENV{XCATHOST}) { + $xcathost=$ENV{XCATHOST}; +} + +unless (@ARGV) { + print "Usage: pping [-i suffix] \n"; + exit(1); +} +my $noderange = $ARGV[0]; +my $client = IO::Socket::SSL->new( + PeerAddr=>$xcathost, + SSL_key_file=>$ENV{HOME}."/.xcat/client-key.pem", + SSL_cert_file=>$ENV{HOME}."/.xcat/client-cert.pem", + SSL_ca_file => $ENV{HOME}."/.xcat/ca.pem", + SSL_use_cert => 1, + #SSL_verify_mode => 1, + ); +die "Connection failure: $!\n" unless ($client); +my %cmdref = (command => 'noderange', noderange => $noderange); +$SIG{ALRM} = sub { die "No response getting noderange" }; +alarm(15); +print $client XMLout(\%cmdref,RootName=>'xcatrequest', NoAttr=>1, KeyAttr => []); +alarm(15); +my $response=""; +my @nodes=(); +while (<$client>) { + alarm(0); + $response .= $_; + if ($response =~ m/<\/xcatresponse>/) { + $rsp=XMLin($response, ForceArray => ['node']); + $response=''; + if ($rsp->{warning}) { + printf "Warning: ".$rsp->{warning}."\n"; + } + if ($rsp->{error}) { + die ("ERROR: ".$rsp->{error}."\n"); + } elsif ($rsp->{node}) { + @nodes=@{$rsp->{node}}; + } + if ($rsp->{serverdone}) { + last; + } + } +} +close($client); +my $children = 0; +my $inputs = new IO::Select; +$SIG{CHLD} = sub { while (waitpid(-1,WNOHANG) > 0) { $children--; } }; +if ($interface) { + foreach (@nodes) { + s/$/-$interface/; + } +} + +exec "fping ".join(' ',@nodes). " 2> /dev/null"; + + diff --git a/xCAT-client-2.0/usr/bin/psh b/xCAT-client-2.0/usr/bin/psh new file mode 100755 index 000000000..feffbf476 --- /dev/null +++ b/xCAT-client-2.0/usr/bin/psh @@ -0,0 +1,108 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +use IO::Socket::SSL; +use XML::Simple; +use Data::Dumper; +use IO::Handle; +use IO::Select; +use xCAT::Utils; +use Getopt::Long; +use POSIX qw(:signal_h :errno_h :sys_wait_h); +my $interface; +Getopt::Long::Configure("require_order"); +GetOptions( + "interface=s" => \$interface, + ); +my %nodehdl; +my $xcathost='localhost:3001'; +if ($ENV{XCATHOST}) { + $xcathost=$ENV{XCATHOST}; +} + +my $pshmaxp = 64; #TODO: should this be server dictated or local conf? +unless (@ARGV) { + print "Usage: psh [-i \n"; +} +my $noderange = $ARGV[0]; +my $client = IO::Socket::SSL->new( + PeerAddr=>$xcathost, + SSL_key_file=>$ENV{HOME}."/.xcat/client-key.pem", + SSL_cert_file=>$ENV{HOME}."/.xcat/client-cert.pem", + SSL_ca_file => $ENV{HOME}."/.xcat/ca.pem", + SSL_use_cert => 1, + #SSL_verify_mode => 1, + ); +die "Connection failure: $!\n" unless ($client); +my %cmdref = (command => 'noderange', noderange => $noderange); +$SIG{ALRM} = sub { die "No response getting noderange" }; +alarm(15); +print $client XMLout(\%cmdref,RootName=>'xcatrequest', NoAttr=>1, KeyAttr => []); +alarm(15); +my $response=""; +my @nodes=(); +while (<$client>) { + alarm(0); + $response .= $_; + if ($response =~ m/<\/xcatresponse>/) { + $rsp=XMLin($response, ForceArray => ['node']); + $response=''; + if ($rsp->{warning}) { + printf "Warning: ".$rsp->{warning}."\n"; + } + if ($rsp->{error}) { + die ("ERROR: ".$rsp->{error}."\n"); + } elsif ($rsp->{node}) { + @nodes=@{$rsp->{node}}; + } + if ($rsp->{serverdone}) { + last; + } + } +} +close($client); +my $children = 0; +my $inputs = new IO::Select; +$SIG{CHLD} = sub { while (waitpid(-1,WNOHANG) > 0) { $children--; } }; +if ($interface) { + foreach (@nodes) { + s/$/-$interface/; + } +} +foreach (@nodes) { + my $node=$_; + while ($children > $pshmaxp) { processoutput($inputs); } + my $child; + $children++; + sshnode(\$child,$node,@ARGV[1 .. $#ARGV]); + $inputs->add($child); + $nodehdl{$child} = $node; +} +while ($children) { + processoutput($inputs); +} +while (processoutput($inputs)) {}; +exit(0); + +sub processoutput { #This way, one arbiter handles output, no interrupting + my $inputs = shift; + my @readyins = $inputs->can_read(1); + my $rc = @readyins; + my $readyh; + foreach $readyh (@readyins) { + my $line = <$readyh>; + unless ($line) { + $inputs->remove($readyh); + close($readyh); + next; + } + print $nodehdl{$readyh}.": ".$line; + } + return $rc; +} +sub sshnode { + my $out = shift; + my $node = shift; + my $in; + my $args = join(" ",@_); + open($$out,"ssh -o BatchMode=yes $node " . xCAT::Utils->quote($args) . " 2>&1 |"); +} diff --git a/xCAT-client-2.0/usr/bin/xcatDBcmds b/xCAT-client-2.0/usr/bin/xcatDBcmds new file mode 100755 index 000000000..73ef9f47a --- /dev/null +++ b/xCAT-client-2.0/usr/bin/xcatDBcmds @@ -0,0 +1,241 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html + +use File::Basename; +use xCAT::Client submit_request; + +my $bname = basename($0); + +######################################### +# Main +# +# xcatDBcmds +# +# Handles xCAT DB object management commands: +# xdefmk, xdefch, xdefls, & xdefrm. +# +# Builds hash and submits request. +# +# Note: The subroutines that implement these commands +# are in /usr/lib/xcat/plugins/DBobjectdefs.pm +# on the xCAT management node. +# +######################################### + +my $cmdref; + +# set the command name to pass to the plugin +if ($bname =~ /xcatDBcmds/) +{ + $cmdref->{command}->[0] = shift @ARGV; +} +elsif ($bname =~ /^(.*$)/) +{ + $cmdref->{command}->[0] = $1; +} +else +{ + printf("Bad usage\n"); + exit 1; +} + +print "The command \'$cmdref->{command}->[0]\' is not yet available.\n"; +exit 0; + +# add all the cmd line args to the hash - to pass to the plugin subroutine +foreach (@ARGV) +{ + push(@{$cmdref->{arg}}, $_); +} + +if (!($bname =~ /xdefmk/)) +{ + + # + # We need to see if a noderange was provided on the command line + # - the command line could be flags(options) -> noderange -> attr=val pairs + # - the noderange is used by xCAT remote access support infrastructure + # so we need to set it now + # + + use Getopt::Long; + + # Allows opts to be grouped (e.g. -avx) + Getopt::Long::Configure("bundling"); + + # parse the options + if ( + !GetOptions( + 'all|a' => \$::opt_a, + 'dynamic|d' => \$::opt_d, + 'f=s' => \$::opt_f, + 'i=s' => \$::opt_i, + 'help|h' => \$::opt_h, + 'long|l' => \$::opt_l, + 'm|minus' => \$::opt_m, + 'o=s' => \$::opt_o, + 'r|relace' => \$::opt_r, + 't=s' => \$::opt_t, + 'verbose|V' => \$::opt_V, + 'version|v' => \$::opt_v, + 'w=s' => \$::opt_w, + 'x=s' => \$::opt_x, + 'z=s' => \$::opt_z + ) + ) + { + return 1; + } + + # a node range would be the next arg - but not if it contains an "=" sign + # - then it would be an attr=val operand + my $arg = shift(@ARGV); + if (!($arg =~ /=/)) + { + $cmdref->{noderange}->[0] = $arg; + + # could check if valid noderange at this point??? + } + +} + +xCAT::Client::submit_request($cmdref, \&handle_response); + +print "\n"; + +exit 0; + +# may want to modify handle_response at some point!!!!!!! + +########################################## +# handle_response is the callback that is +# invoked to print out the data returned by +# the plugin. +# +# Format of the response hash: +# {data => [ 'data str1', 'data str2', '...' ] } +# +# Results are printed as: +# data str1 +# data str2 +# +# or: +# {data => [ {desc => [ 'desc1' ], +# contents => [ 'contents1' ] }, +# {desc => [ 'desc2 ], +# contents => [ 'contents2' ] } +# : +# ] } +# NOTE: In this format, only the data array can have more than one +# element. All other arrays are assumed to be a single element. +# Results are printed as: +# desc1: contents1 +# desc2: contents2 +# +# or: +# {node => [ {name => ['node1'], +# data => [ {desc => [ 'node1 desc' ], +# contents => [ 'node1 contents' ] } ] }, +# {name => ['node2'], +# data => [ {desc => [ 'node2 desc' ], +# contents => [ 'node2 contents' ] } ] }, +# : +# ] } +# NOTE: Only the node array can have more than one element. +# All other arrays are assumed to be a single element. +# +# This was generated from the corresponding HTML: +# +# +# node1 +# +# node1 desc +# node1 contents +# +# +# +# node2 +# +# node2 desc +# node2 contents +# +# +# +# +# Results are printed as: +# node_name: desc: contents +########################################## +sub handle_response +{ + my $rsp = shift; + + # Handle {node} structure + if ($rsp->{node}) + { + my $nodes = ($rsp->{node}); + my $node; + foreach $node (@$nodes) + { + my $desc = $node->{name}->[0]; + if ($node->{data}) + { + if (ref(\($node->{data}->[0])) eq 'SCALAR') + { + $desc = $desc . ": " . $node->{data}->[0]; + } + else + { + if ($node->{data}->[0]->{desc}) + { + $desc = $desc . ": " . $node->{data}->[0]->{desc}->[0]; + } + if ($node->{data}->[0]->{contents}) + { + $desc = "$desc: " . $node->{data}->[0]->{contents}->[0]; + } + } + } + if ($desc) + { + print "$desc\n"; + } + } + } + + # Handle {data} structure with no nodes + if ($rsp->{data}) + { + my $data = ($rsp->{data}); + my $data_entry; + foreach $data_entry (@$data) + { + my $desc; + if (ref(\($data_entry)) eq 'SCALAR') + { + $desc = $data_entry; + } + else + { + if ($data_entry->{desc}) + { + $desc = $data_entry->{desc}->[0]; + } + if ($data_entry->{contents}) + { + if ($desc) + { + $desc = "$desc: " . $data_entry->{contents}->[0]; + } + else + { + $desc = $data_entry->{contents}->[0]; + } + } + } + if ($desc) + { + print "$desc\n"; + } + } + } +} diff --git a/xCAT-client-2.0/usr/bin/xcatclient b/xCAT-client-2.0/usr/bin/xcatclient new file mode 100755 index 000000000..f3a988fd2 --- /dev/null +++ b/xCAT-client-2.0/usr/bin/xcatclient @@ -0,0 +1,168 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +use IO::Socket::SSL; +use IO::Socket::INET; +use File::Basename; +use Data::Dumper; +use xCAT::Client submit_request; +my $bname = basename($0); + +######################################### +# Main +# Build hash and submit request +######################################### + +my $exitcode = 0; #var to store exit code that results from responses +my $cmdref; +if($bname =~ /xcatclient/) { + $cmdref->{command}->[0]=shift @ARGV; +} elsif ($bname =~/^(.*$)/) { + $cmdref->{command}->[0] = $1; +} else { + printf("Bad usage\n"); + exit(1); +} + +if (-p STDIN) { + my $data; + while ( ) { + $data.=$_; + } + $cmdref->{stdin}->[0]=$data; +} + +my $arg=shift(@ARGV); +while ($arg =~ /^-/) { + push (@{$cmdref->{arg}}, $arg); + $arg=shift(@ARGV); +} +if ($arg ne "NO_NODE_RANGE") { + $cmdref->{noderange}->[0]=$arg; +} +foreach (@ARGV) { push (@{$cmdref->{arg}}, $_ ); } +#$cmdref->{arg}=\@ARGV; +xCAT::Client::submit_request($cmdref,\&handle_response); +exit $exitcode; + + +########################################## +# handle_response is the callback that is +# invoked to print out the data returned by +# the plugin. +# +# Format of the response hash: +# {data => [ 'data str1', 'data str2', '...' ] } +# +# Results are printed as: +# data str1 +# data str2 +# +# or: +# {data => [ {desc => [ 'desc1' ], +# contents => [ 'contents1' ] }, +# {desc => [ 'desc2 ], +# contents => [ 'contents2' ] } +# : +# ] } +# NOTE: In this format, only the data array can have more than one +# element. All other arrays are assumed to be a single element. +# Results are printed as: +# desc1: contents1 +# desc2: contents2 +# +# or: +# {node => [ {name => ['node1'], +# data => [ {desc => [ 'node1 desc' ], +# contents => [ 'node1 contents' ] } ] }, +# {name => ['node2'], +# data => [ {desc => [ 'node2 desc' ], +# contents => [ 'node2 contents' ] } ] }, +# : +# ] } +# NOTE: Only the node array can have more than one element. +# All other arrays are assumed to be a single element. +# +# This was generated from the corresponding HTML: +# +# +# node1 +# +# node1 desc +# node1 contents +# +# +# +# node2 +# +# node2 desc +# node2 contents +# +# +# +# +# Results are printed as: +# node_name: desc: contents +########################################## +sub handle_response { + my $rsp = shift; + # Handle {node} structure + if ($rsp->{errorcode}) { + foreach my $ecode (@{$rsp->{errorcode}}) { + $exitcode |= $ecode; + } + } + if ($rsp->{node}) { + my $nodes=($rsp->{node}); + my $node; + foreach $node (@$nodes) { + my $desc=$node->{name}->[0]; + if ($node->{errorcode}) { + foreach my $ecode (@{$node->{errorcode}}) { + $exitcode |= $ecode; + } + } + if ($node->{data}) { + if (ref(\($node->{data}->[0])) eq 'SCALAR') { + $desc=$desc.": ".$node->{data}->[0]; + } else { + if ($node->{data}->[0]->{desc}) { + $desc=$desc.": ".$node->{data}->[0]->{desc}->[0]; + } + if ($node->{data}->[0]->{contents}) { + $desc="$desc: ".$node->{data}->[0]->{contents}->[0]; + } + } + } + if ($desc) { + print "$desc\n"; + } + } + } + + # Handle {data} structure with no nodes + if ($rsp->{data}) { + my $data=($rsp->{data}); + my $data_entry; + foreach $data_entry (@$data) { + my $desc; + if (ref(\($data_entry)) eq 'SCALAR') { + $desc=$data_entry; + } else { + if ($data_entry->{desc}) { + $desc=$data_entry->{desc}->[0]; + } + if ($data_entry->{contents}) { + if ($desc) { + $desc="$desc: ".$data_entry->{contents}->[0]; + } else { + $desc=$data_entry->{contents}->[0]; + } + } + } + if ($desc) { + print "$desc\n"; + } + } + } +} + diff --git a/xCAT-client-2.0/usr/bin/xcatclientnnr b/xCAT-client-2.0/usr/bin/xcatclientnnr new file mode 100755 index 000000000..e539a87a0 --- /dev/null +++ b/xCAT-client-2.0/usr/bin/xcatclientnnr @@ -0,0 +1,25 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +use File::Basename; + +####################################### +# It handles the commands without noderanges. +####################################### +my $bname = basename($0); +my $cmd; +if($bname =~ /xcatclientnnr/) { + $cmd = shift @ARGV; +} elsif ($bname =~/^(.*$)/) { + $cmd = $1; +} else { + printf("Bad usage\n"); + exit(1); +} +exec("/usr/bin/xcatclient $cmd NO_NODE_RANGE @ARGV 2>&1"); +#if (system("/usr/bin/xcatclient $cmd NO_NODE_RANGE @ARGV 2>&1") != 0) { +# print "$cmd failed: $?\n"; +#} + +#exit; + + diff --git a/xCAT-client-2.0/usr/bin/xcoll b/xCAT-client-2.0/usr/bin/xcoll new file mode 100755 index 000000000..51040583d --- /dev/null +++ b/xCAT-client-2.0/usr/bin/xcoll @@ -0,0 +1,27 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +use Data::Dumper; +my %output; +while () { + my $node; + my $output; + if (/:/) { + ($node,$output) = split /:/,$_,2; + } else { + $node= "UNKNOWN"; + $output = $_; + } + $output =~ s/^ //; + $output{$node}.=$output; +} +my %collated; +foreach (keys %output) { + $collated{$output{$_}}->{$_}=1; +} +foreach (keys %collated) { + my $nodes = join(',',sort (keys %{$collated{$_}})); + print "====================================\n"; + print "$nodes\n"; + print "====================================\n"; + print $_; +} diff --git a/xCAT-client-2.0/usr/bin/xdsh b/xCAT-client-2.0/usr/bin/xdsh new file mode 100644 index 000000000..a50316ec8 --- /dev/null +++ b/xCAT-client-2.0/usr/bin/xdsh @@ -0,0 +1,446 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +use IO::Socket::SSL; +use IO::Socket::INET; +use File::Basename; +use Data::Dumper; +use Getopt::Long; +use xCAT::MsgUtils; +use xCAT::Client submit_request; +my $bname = basename($0); + +#----------------------------------------------------------------------------- + +=head1 xdsh/xdcp + +This program is the client interface for xdsh/xdcp. + + + xdsh/xdcp command + This is the client interface for the xdsh/xdcp command + Call parse_args to verify input + Build hash and submit request + Note: xdsh/xdcp (dsh/dcp for xCAT) only supports a subset of the options + supported by dsh/dcp. See help and man page of each command for the + differences. +=cut + +#----------------------------------------------------------------------------- +# Main + +#$ENV{XCATBYPASS} = "yes"; # set bypass mode +my $rc = 0; + +# check for help and Environment Variables +if ($bname eq "xdsh") +{ + &parse_args_xdsh; +} +else +{ # xdcp + &parse_args_xdcp; +} + +# report unsupported dsh exports +&check_invalid_exports; + +my $cmdref; +$cmdref->{command}->[0] = $bname; # save my command name + +my $arg = shift(@ARGV); +while ($arg =~ /^-/) +{ + push(@{$cmdref->{arg}}, $arg); # save command arguments + $arg = shift(@ARGV); +} +$cmdref->{noderange}->[0] = $arg; # save nodes + +# +# add flgs for all the valid dsh/dcp Env Variable set +# +# The first ones are common to dsh/dcp +# if nodes from file and none on the command line +if ($ENV{'DSH_NODE_RSH'}) +{ + my $value = $ENV{'DSH_NODE_RSH'}; + push(@{$cmdref->{arg}}, "-r $value "); + +} +if ($ENV{'DSH_NODE_OPTS'}) +{ + my $value = $ENV{'DSH_NODE_OPTS'}; + push(@{$cmdref->{arg}}, "-o $value "); + +} +if ($ENV{'DSH_FANOUT'}) +{ + my $value = $ENV{'DSH_FANOUT'}; + push(@{$cmdref->{arg}}, "-f $value "); + +} +if ($ENV{'DSH_TIMEOUT'}) +{ + my $value = $ENV{'DSH_TIMEOUT'}; + push(@{$cmdref->{arg}}, "-t $value "); + +} +if ($bname eq "xdsh") +{ + if ($ENV{'DSH_REPORT'}) + { + my $value = $ENV{'DSH_REPORT'}; + push(@{$cmdref->{arg}}, "--report $value "); + + } +} +## set XCAT Context always ## +push(@{$cmdref->{arg}}, "-C XCAT"); + +foreach (@ARGV) { push(@{$cmdref->{arg}}, $_); } + +xCAT::Client::submit_request($cmdref, \&handle_response); +exit $rc; + +#----------------------------------------------------------------------------- + +=head3 parse_args_xdsh + + Parses for dsh input + Check if the command ask for help and display usage +=cut + +#----------------------------------------------------------------------------- +sub parse_args_xdsh +{ + + my $usagemsg1 = + " xdsh -h \n xdsh -q \n xdsh -v \n xdsh [noderange] [group]\n"; + my $usagemsg2 = + " [-C context] [-c] [-e] [-E environment_file] [-f fanout]\n"; + my $usagemsg3 = + " [-F output_file] [-i] [-l user_ID] [-L] [--log log_file]\n"; + my $usagemsg4 = " [-m] [-o options] [-Q] [-r remote_shell] \n"; + my $usagemsg5 = + " [-s] [-S ksh | csh] [-t timeout] [-T] [-X environment variables] [-v] [-z]\n"; + my $usagemsg6 = + " [--report report_path] [--report-name report_name]\n"; + my $usagemsg7 = " [command_list]\n"; + my $usagemsg8 = "Note:Context is always set to XCAT\n"; + my $usagemsg .= $usagemsg1 .= $usagemsg2 .= $usagemsg3 .= $usagemsg4 .= + $usagemsg5 .= $usagemsg6 .= $usagemsg7; + + Getopt::Long::Configure("posix_default"); + Getopt::Long::Configure("no_gnu_compat"); + Getopt::Long::Configure("bundling"); + + if ( + !GetOptions( + 'e|execute' => \$::EXECUTE, + 'f|fanout=i' => \$::FANOUT, + 'h|help' => \$::HELP, + 'i|notify' => \$::NOTIFY, + 'l|user=s' => \$::USER, + 'm|monitor' => \$::MONITOR, + 'o|node-options=s' => \$::NODEOPTS, + 'q|show-config' => \$::SHOWCFG, + 'r|node-rsh=s' => \$::NODERSH, + 's|stream' => \$::STREAM, + 't|timeout=i' => \$::TIMEOUT, + 'v|verify' => \$::VERIFY, + 'z|exit-status' => \$::EXITSTAT, + 'E|environment=s' => \$::ENV, + 'F|output-file=s' => \$::OUTPUTFILE, + 'I|ignore-sig|ignoresig=s' => \$::IGNORESIG, + 'L|no-locale' => \$::NOLOCALE, + 'Q|silent' => \$::SILENT, + 'S|syntax=s' => \$::SYNTAX, + 'T|trace' => \$::TRACE, + 'V|version' => \$::VERSION, + 'command-name|commandName=s' => \$::COMMANDNAME, + 'command-description|commandDescription=s' => \$::COMMANDDES, + 'log=s' => \$::LOG, + 'report=s' => \$::REPORT, + 'R|reports=s' => \$::REPORTS, + 'report-name|reportName=s' => \$::REPORTNAME, + 'W|noFileWriting' => \$::NOFILEWRITE, + + 'w|wcoll=s' => \$::WCOLL, + 'X:s' => \$::IGNORENV + + ) + ) + { + xCAT::MsgUtils->message("I", "$usagemsg"); + exit 1; + } + + if ($::HELP) + { + xCAT::MsgUtils->message("I", "$usagemsg"); + exit 0; + } + +} + +#----------------------------------------------------------------------------- + +=head3 parse_args_xdcp + + Parses for dcp input + Check if the command ask for help and display usage +=cut + +#----------------------------------------------------------------------------- +sub parse_args_xdcp +{ + my $usagemsg1 = + " xdcp -h \n xdcp -q \n xdcp -v \n xdcp [noderange] [group]\n"; + my $usagemsg2 = " [-C context] [-c] [-f fanout] [-l user_ID]\n"; + my $usagemsg3 = + " [-o options] [-s] [-p] [-P] [-Q] [-r node_remote_copy]\n"; + my $usagemsg4 = + " [-R] [-t timeout] [-T] [-X environment variables] [-v] \n"; + my $usagemsg5 = " source_file... target_path\n"; + my $usagemsg6 = "Note:Context is always set to XCAT\n"; + my $usagemsg .= $usagemsg1 .= $usagemsg2 .= $usagemsg3 .= $usagemsg4 .= + $usagemsg5 .= $usagemsg6; + + Getopt::Long::Configure("posix_default"); + Getopt::Long::Configure("no_gnu_compat"); + Getopt::Long::Configure("bundling"); + + if ( + !GetOptions( + 'f|fanout=i' => \$::FANOUT, + 'h|help' => \$::HELP, + 'l|user=s' => \$::USER, + 'o|node-options=s' => \$::NODEOPTS, + 'p|preserver' => \$::PRESERVE, + 'q|show-config' => \$::SHOWCFG, + 'r|c|node-rcp=s' => \$::NODERCP, + 's' => \$::RSYNC, + 't|timeout=i' => \$::TIMEOUT, + 'v|verify' => \$::VERIFY, + 'Q|silent' => \$::SILENT, + 'P|pull' => \$::PULL, + 'R|recursive' => \$::RECURSIVE, + 'T|trace' => \$::TRACE, + 'V|version' => \$::VERSION, + 'w|wcoll=s' => \$::WCOLL, + 'X:s' => \$::IGNORENV + + ) + ) + { + xCAT::MsgUtils->message("I", "$usagemsg"); + exit 1; + } + + if ($::HELP) + { + xCAT::MsgUtils->message("I", "$usagemsg"); + exit 0; + } +} + +#----------------------------------------------------------------------------- + +=head3 check_invalid_exports + Check for unsupported dsh exports and warns + +=cut + +#----------------------------------------------------------------------------- +sub check_invalid_exports + +{ + ## + # Check for unsupported Environment Variables + # DSH_DEVICE_LIST, DSH_DEVICE_OPTS, DSH_DEVICE_RCP,DSH_DEVICE_RSH, + # DSH_NODEGROUP_PATH + ## + if ($ENV{'DSH_NODE_LIST'}) + { + xCAT::MsgUtils->message( + "I", + "DSH_NODE_LIST is set but is not supported. It will be ignored.\n" + ); + } + if ($ENV{'DSH_DEVICE_LIST'}) + { + xCAT::MsgUtils->message( + "I", + "DSH_DEVICE_LIST is set but is not supported. It will be ignored.\n" + ); + } + if ($ENV{'DSH_DEVICE_OPTS'}) + { + xCAT::MsgUtils->message( + "I", + "DSH_DEVICE_OPTS is set but is not supported. It will be ignored.\n" + ); + } + if ($ENV{'DSH_DEVICE_RCP'}) + { + xCAT::MsgUtils->message( + "I", + "DSH_DEVICE_RCP is set but is not supported. It will be ignored.\n" + ); + } + if ($ENV{'DSH_DEVICE_RSH'}) + { + xCAT::MsgUtils->message( + "I", + "DSH_DEVICE_RSH is set but is not supported. It will be ignored.\n" + ); + } + if ($ENV{'DSH_NODEGROUP_PATH'}) + { + xCAT::MsgUtils->message( + "I", + "DSH_NODEGROUP_PATH is set but is not supported. It will be ignored.\n" + ); + } + if ($ENV{'RSYNC_RSH'}) + { + xCAT::MsgUtils->message("I", + " RSYNC_RSH is set but is not supported. It will be ignored.\n"); + } +} + +#----------------------------------------------------------------------------- + +=head3 handle_response + handle_response is the callback that is + invoked to print out the data returned by + the plugin. + + Format of the response hash: + {data => [ 'data str1', 'data str2', '...' ] } + + Results are printed as: + data str1 + data str2 + + or: + {data => [ {desc => [ 'desc1' ], + contents => [ 'contents1' ] }, + {desc => [ 'desc2 ], + contents => [ 'contents2' ] } + : + ] } + NOTE: In this format, only the data array can have more than one + element. All other arrays are assumed to be a single element. + Results are printed as: + desc1: contents1 + desc2: contents2 + + or: + {node => [ {name => ['node1'], + data => [ {desc => [ 'node1 desc' ], + contents => [ 'node1 contents' ] } ] }, + {name => ['node2'], + data => [ {desc => [ 'node2 desc' ], + contents => [ 'node2 contents' ] } ] }, + : + ] } + NOTE: Only the node array can have more than one element. + All other arrays are assumed to be a single element. + + This was generated from the corresponding HTML: + + + node1 + + node1 desc + node1 contents + + + + node2 + + node2 desc + node2 contents + + + + + Results are printed as: + node_name: desc: contents +=cut + +#----------------------------------------------------------------------------- +sub handle_response +{ + my $rsp = shift; + + # Handle {node} structure + if ($rsp->{node}) + { + my $nodes = ($rsp->{node}); + my $node; + foreach $node (@$nodes) + { + my $desc = $node->{name}->[0]; + if ($node->{data}) + { + if (ref(\($node->{data}->[0])) eq 'SCALAR') + { + $desc = $desc . ": " . $node->{data}->[0]; + } + else + { + if ($node->{data}->[0]->{desc}) + { + $desc = $desc . ": " . $node->{data}->[0]->{desc}->[0]; + } + if ($node->{data}->[0]->{contents}) + { + $desc = "$desc: " . $node->{data}->[0]->{contents}->[0]; + } + } + } + if ($desc) + { + print "$desc\n"; + } + } + } + + # Handle {data} structure with no nodes + if ($rsp->{data}) + { + my $data = ($rsp->{data}); + my $data_entry; + foreach $data_entry (@$data) + { + my $desc; + if (ref(\($data_entry)) eq 'SCALAR') + { + $desc = $data_entry; + } + else + { + if ($data_entry->{desc}) + { + $desc = $data_entry->{desc}->[0]; + } + if ($data_entry->{contents}) + { + if ($desc) + { + $desc = "$desc: " . $data_entry->{contents}->[0]; + } + else + { + $desc = $data_entry->{contents}->[0]; + } + } + } + if ($desc) + { + print "$desc\n"; + } + } + } +} diff --git a/xCAT-client-2.0/usr/sbin/tabedit b/xCAT-client-2.0/usr/sbin/tabedit new file mode 100755 index 000000000..cc5d5a70c --- /dev/null +++ b/xCAT-client-2.0/usr/sbin/tabedit @@ -0,0 +1,42 @@ +#!/bin/bash +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +function cexit { + if [ -d /tmp/tabedit.$$ ]; then + rm -rf /tmp/tabedit.$$; + fi + exit +} +trap cexit 2 15 + +TABLE=$1 +if [ -z "$EDITOR" ]; then + echo "Define EDITOR environment variable before running this command" + exit 1 +fi +if [ -z "$TABLE" ]; then + echo "Usage: tabedit "; + exit 1 +fi +mkdir -p /tmp/tabedit.$$/ +xcatclientnnr tabdump $1 > /tmp/tabedit.$$/$1.csv +#use md5sum to check if it actually changes.. +SUM=`md5sum /tmp/tabedit.$$/$1.csv` +EXIT=0 +while [ $EXIT -eq 0 ]; do + cd /tmp/tabedit.$$ + "$EDITOR" $1.csv + cd - + NEWSUM=`md5sum /tmp/tabedit.$$/$1.csv` + if [ "$NEWSUM" == "$SUM" ]; then + echo "No file modifications detected, not restoring." + break; + fi + if `dirname $0`/tabrestore /tmp/tabedit.$$/$1.csv; then + break; + else + echo "Above errors occured, hit enter to edit, or ctrl-c to abort" + read JNK + fi +done +cexit + diff --git a/xCAT-client-2.0/usr/sbin/tabrestore b/xCAT-client-2.0/usr/sbin/tabrestore new file mode 100755 index 000000000..433815a50 --- /dev/null +++ b/xCAT-client-2.0/usr/sbin/tabrestore @@ -0,0 +1,161 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#Just like xcatclient, but needs to read a file in and pass it as $request->data +use IO::Socket::SSL; +use IO::Socket::INET; +use File::Basename; +use Data::Dumper; +use xCAT::Client submit_request; +my $bname = basename($0); + +######################################### +# Main +# Build hash and submit request +######################################### + +my $cmdref; +my $exitcode=0; +$cmdref->{command}->[0] = "tabrestore"; + +my $arg=shift(@ARGV); +while ($arg =~ /^-/) { + push (@{$cmdref->{arg}}, $arg); + $arg=shift(@ARGV); +} +unless ($arg) { + printf("Usage: tabrestore [tablename].csv\n"); + exit(1); +} +my $filename = $arg; +unless (-r $filename) { + printf("Unable to open $arg for reading.\n"); + exit(1); +} +my $tabname = basename($filename); +$tabname =~ s/\..*//; +$cmdref->{table}->[0] = $tabname; +my $fh; +open($fh,$filename); +while (<$fh>) { + push @{$cmdref->{data}},$_; +} + +foreach (@ARGV) { push (@{$cmdref->{arg}}, $_ ); } +#$cmdref->{arg}=\@ARGV; +xCAT::Client::submit_request($cmdref,\&handle_response); +exit $exitcode; + + +########################################## +# handle_response is the callback that is +# invoked to print out the data returned by +# the plugin. +# +# Format of the response hash: +# {data => [ 'data str1', 'data str2', '...' ] } +# +# Results are printed as: +# data str1 +# data str2 +# +# or: +# {data => [ {desc => [ 'desc1' ], +# contents => [ 'contents1' ] }, +# {desc => [ 'desc2 ], +# contents => [ 'contents2' ] } +# : +# ] } +# NOTE: In this format, only the data array can have more than one +# element. All other arrays are assumed to be a single element. +# Results are printed as: +# desc1: contents1 +# desc2: contents2 +# +# or: +# {node => [ {name => ['node1'], +# data => [ {desc => [ 'node1 desc' ], +# contents => [ 'node1 contents' ] } ] }, +# {name => ['node2'], +# data => [ {desc => [ 'node2 desc' ], +# contents => [ 'node2 contents' ] } ] }, +# : +# ] } +# NOTE: Only the node array can have more than one element. +# All other arrays are assumed to be a single element. +# +# This was generated from the corresponding HTML: +# +# +# node1 +# +# node1 desc +# node1 contents +# +# +# +# node2 +# +# node2 desc +# node2 contents +# +# +# +# +# Results are printed as: +# node_name: desc: contents +########################################## +sub handle_response { + my $rsp = shift; + # Handle {node} structure + if ($rsp->{node}) { + my $nodes=($rsp->{node}); + my $node; + foreach $node (@$nodes) { + my $desc=$node->{name}->[0]; + if ($node->{data}) { + if (ref(\($node->{data}->[0])) eq 'SCALAR') { + $desc=$desc.": ".$node->{data}->[0]; + } else { + if ($node->{data}->[0]->{desc}) { + $desc=$desc.": ".$node->{data}->[0]->{desc}->[0]; + } + if ($node->{data}->[0]->{contents}) { + $desc="$desc: ".$node->{data}->[0]->{contents}->[0]; + } + } + } + if ($desc) { + print "$desc\n"; + } + } + } + + # Handle {data} structure with no nodes + if ($rsp->{error}) { + $exitcode=1; + } + if ($rsp->{data}) { + my $data=($rsp->{data}); + my $data_entry; + foreach $data_entry (@$data) { + my $desc; + if (ref(\($data_entry)) eq 'SCALAR') { + $desc=$data_entry; + } else { + if ($data_entry->{desc}) { + $desc=$data_entry->{desc}->[0]; + } + if ($data_entry->{contents}) { + if ($desc) { + $desc="$desc: ".$data_entry->{contents}->[0]; + } else { + $desc=$data_entry->{contents}->[0]; + } + } + } + if ($desc) { + print "$desc\n"; + } + } + } +} diff --git a/xCAT-client-2.0/usr/svn-commit.tmp b/xCAT-client-2.0/usr/svn-commit.tmp new file mode 100644 index 000000000..1d2490225 --- /dev/null +++ b/xCAT-client-2.0/usr/svn-commit.tmp @@ -0,0 +1,4 @@ +Add a basic xcoll, without reverse noderange, reverse noderange will come post alpha +--This line, and those below, will be ignored-- + +AM bin/xcoll diff --git a/xCAT-client-2.0/xCAT-client.spec b/xCAT-client-2.0/xCAT-client.spec new file mode 100644 index 000000000..ad1a7e2ee --- /dev/null +++ b/xCAT-client-2.0/xCAT-client.spec @@ -0,0 +1,89 @@ +Summary: Core executables and data of the xCAT management project +Name: xCAT-client +Version: 2.0 +Release: snap%(date +"%Y%m%d%H%M") +License: EPL +Group: Applications/System +Source: xCAT-client-2.0.tar.gz +Packager: IBM Corp. +Vendor: IBM Corp. +Distribution: %{?_distribution:%{_distribution}}%{!?_distribution:%{_vendor}} +Prefix: %{_prefix} +BuildRoot: /var/tmp/%{name}-%{version}-%{release}-root + +# AIX will build with an arch of "ppc" +%ifos linux +BuildArch: noarch +%endif + +Provides: xCAT-client = %{version} + +%description +xCAT-client provides the fundamental xCAT commands (chtab, chnode, rpower, etc) helpful in administrating systems at scale, with particular attention paid to large HPC clusters. + +%prep +%setup -q +%build +%install + +rm -rf $RPM_BUILD_ROOT + +mkdir -p $RPM_BUILD_ROOT/usr/bin +mkdir -p $RPM_BUILD_ROOT/usr/sbin +mkdir -p $RPM_BUILD_ROOT/usr/share/xcat/scripts + +cp usr/bin/* $RPM_BUILD_ROOT/usr/bin +chmod 755 $RPM_BUILD_ROOT/usr/bin/* +cp usr/sbin/* $RPM_BUILD_ROOT/usr/sbin +chmod 755 $RPM_BUILD_ROOT/usr/sbin/* + +#cp usr/share/xcat/scripts/setup-local-client.sh $RPM_BUILD_ROOT/usr/share/xcat/scripts/setup-local-client.sh +#chmod 755 $RPM_BUILD_ROOT/usr/share/xcat/scripts/setup-local-client.sh + +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/rpower +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/sbin/makedhcp +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/sbin/makehosts +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/sbin/nodeset +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/sbin/makeconservercf +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/rbeacon +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/rvitals +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/rinv +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/rspreset +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/rsetboot +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/rbootseq +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/reventlog +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/nodels +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/nodech +ln -sf /usr/bin/xcatclient $RPM_BUILD_ROOT/usr/bin/noderm +ln -sf /usr/bin/xcatclientnnr $RPM_BUILD_ROOT/usr/sbin/tabdump +ln -sf /usr/bin/xcatclientnnr $RPM_BUILD_ROOT/usr/sbin/makedns +ln -sf /usr/bin/xcatclientnnr $RPM_BUILD_ROOT/usr/bin/gettab +ln -sf /usr/bin/xcatclientnnr $RPM_BUILD_ROOT/usr/sbin/nodeadd +ln -sf /usr/bin/xcatclientnnr $RPM_BUILD_ROOT/usr/sbin/makenetworks +ln -sf /usr/bin/xcatclientnnr $RPM_BUILD_ROOT/usr/sbin/copycds +ln -sf /usr/bin/xcatclientnnr $RPM_BUILD_ROOT/usr/bin/regnotif +ln -sf /usr/bin/xcatclientnnr $RPM_BUILD_ROOT/usr/bin/unregnotif +ln -sf /usr/bin/xcatDBcmds $RPM_BUILD_ROOT/usr/bin/xdefmk +ln -sf /usr/bin/xcatDBcmds $RPM_BUILD_ROOT/usr/bin/xdefch +ln -sf /usr/bin/xcatDBcmds $RPM_BUILD_ROOT/usr/bin/xdefls +ln -sf /usr/bin/xcatDBcmds $RPM_BUILD_ROOT/usr/bin/xdefrm +ln -sf /usr/bin/xdsh $RPM_BUILD_ROOT/usr/bin/xdcp + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +%doc README +%doc LICENSE.html +/usr/bin/* +/usr/sbin/* +#/usr/share/xcat/scripts/setup-local-client.sh + +%changelog +* Wed May 2 2007 - Norm Nott +- Made changes to make this work on AIX + +* Tue Feb 20 2007 Jarrod Johnson +- Start core rpm for 1.3 work + diff --git a/xCAT-nbroot/LICENSE.html b/xCAT-nbroot/LICENSE.html new file mode 100644 index 000000000..83d0eebb0 --- /dev/null +++ b/xCAT-nbroot/LICENSE.html @@ -0,0 +1,326 @@ + + + + + + + +Eclipse Public License - Version 1.0 + + + + + + +
+ +

Eclipse Public License - v 1.0 +

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER +THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, +REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE +OF THIS AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) +in the case of the initial Contributor, the initial code and documentation +distributed under this Agreement, and
+b) in the case of each subsequent Contributor:

+ +

i) +changes to the Program, and

+ +

ii) +additions to the Program;

+ +

where +such changes and/or additions to the Program originate from and are distributed +by that particular Contributor. A Contribution 'originates' from a Contributor +if it was added to the Program by such Contributor itself or anyone acting on +such Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in conjunction +with the Program under their own license agreement, and (ii) are not derivative +works of the Program.

+ +

"Contributor" means any person or +entity that distributes the Program.

+ +

"Licensed Patents " mean patent +claims licensable by a Contributor which are necessarily infringed by the use +or sale of its Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions +distributed in accordance with this Agreement.

+ +

"Recipient" means anyone who +receives the Program under this Agreement, including all Contributors.

+ +

2. GRANT OF RIGHTS

+ +

a) +Subject to the terms of this Agreement, each Contributor hereby grants Recipient +a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly +display, publicly perform, distribute and sublicense the Contribution of such +Contributor, if any, and such derivative works, in source code and object code +form.

+ +

b) +Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free +patent license under Licensed Patents to make, use, sell, offer to sell, import +and otherwise transfer the Contribution of such Contributor, if any, in source +code and object code form. This patent license shall apply to the combination +of the Contribution and the Program if, at the time the Contribution is added +by the Contributor, such addition of the Contribution causes such combination +to be covered by the Licensed Patents. The patent license shall not apply to +any other combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) +Recipient understands that although each Contributor grants the licenses to its +Contributions set forth herein, no assurances are provided by any Contributor +that the Program does not infringe the patent or other intellectual property +rights of any other entity. Each Contributor disclaims any liability to Recipient +for claims brought by any other entity based on infringement of intellectual +property rights or otherwise. As a condition to exercising the rights and +licenses granted hereunder, each Recipient hereby assumes sole responsibility +to secure any other intellectual property rights needed, if any. For example, +if a third party patent license is required to allow Recipient to distribute +the Program, it is Recipient's responsibility to acquire that license before +distributing the Program.

+ +

d) +Each Contributor represents that to its knowledge it has sufficient copyright +rights in its Contribution, if any, to grant the copyright license set forth in +this Agreement.

+ +

3. REQUIREMENTS

+ +

A Contributor may choose to distribute the +Program in object code form under its own license agreement, provided that: +

+ +

a) +it complies with the terms and conditions of this Agreement; and

+ +

b) +its license agreement:

+ +

i) +effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose;

+ +

ii) +effectively excludes on behalf of all Contributors all liability for damages, +including direct, indirect, special, incidental and consequential damages, such +as lost profits;

+ +

iii) +states that any provisions which differ from this Agreement are offered by that +Contributor alone and not by any other party; and

+ +

iv) +states that source code for the Program is available from such Contributor, and +informs licensees how to obtain it in a reasonable manner on or through a +medium customarily used for software exchange.

+ +

When the Program is made available in source +code form:

+ +

a) +it must be made available under this Agreement; and

+ +

b) a +copy of this Agreement must be included with each copy of the Program.

+ +

Contributors may not remove or alter any +copyright notices contained within the Program.

+ +

Each Contributor must identify itself as the +originator of its Contribution, if any, in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

Commercial distributors of software may +accept certain responsibilities with respect to end users, business partners +and the like. While this license is intended to facilitate the commercial use +of the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes the +Program in a commercial product offering, such Contributor ("Commercial +Contributor") hereby agrees to defend and indemnify every other +Contributor ("Indemnified Contributor") against any losses, damages and +costs (collectively "Losses") arising from claims, lawsuits and other +legal actions brought by a third party against the Indemnified Contributor to +the extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may participate +in any such claim at its own expense.

+ +

For example, a Contributor might include the +Program in a commercial product offering, Product X. That Contributor is then a +Commercial Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance claims and +warranties are such Commercial Contributor's responsibility alone. Under this +section, the Commercial Contributor would have to defend claims against the +other Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, +WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and distributing the +Program and assumes all risks associated with its exercise of rights under this +Agreement , including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs or +equipment, and unavailability or interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF +THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES.

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid +or unenforceable under applicable law, it shall not affect the validity or +enforceability of the remainder of the terms of this Agreement, and without +further action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Program itself (excluding combinations of the Program with +other software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the date +such litigation is filed.

+ +

All Recipient's rights under this Agreement +shall terminate if it fails to comply with any of the material terms or +conditions of this Agreement and does not cure such failure in a reasonable +period of time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute +copies of this Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The Agreement +Steward reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement Steward has +the right to modify this Agreement. The Eclipse Foundation is the initial +Agreement Steward. The Eclipse Foundation may assign the responsibility to +serve as the Agreement Steward to a suitable separate entity. Each new version +of the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version of +the Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly stated +in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to +the intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved.

+ +

This Agreement is governed by the laws of the +State of New York and the intellectual property laws of the United States of +America. No party to this Agreement will bring a legal action under this +Agreement more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation.

+ +

 

+ +
+ + + + diff --git a/xCAT-nbroot/mkrpm b/xCAT-nbroot/mkrpm new file mode 100755 index 000000000..24727db34 --- /dev/null +++ b/xCAT-nbroot/mkrpm @@ -0,0 +1,8 @@ +cd `dirname $0` +tar --exclude .svn -czvf xcat-nbrootoverlay.tar.gz -C overlay/ . +cp *.gz /usr/src/redhat/SOURCES +cp xcat-core-nbroot.spec /usr/src/redhat/SPECS +rm xcat-nbrootoverlay.tar.gz +rpmbuild -ba /usr/src/redhat/SPECS/xcat-core-nbroot.spec --target $1 +cd - + diff --git a/xCAT-nbroot/overlay/bin/bmcsetup b/xCAT-nbroot/overlay/bin/bmcsetup new file mode 100755 index 000000000..27f931871 --- /dev/null +++ b/xCAT-nbroot/overlay/bin/bmcsetup @@ -0,0 +1,28 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +modprobe ipmi_si +modprobe ipmi_devintf +while ! getipmi.awk +do + echo "Retrying retrieval of IPMI settings from server" +done +BMCIP=`grep bmcip /tmp/ipmi.data |awk -F\> '{print $2}'|awk -F\< '{print $1}'` +BMCGW=`grep gateway /tmp/ipmi.data |awk -F\> '{print $2}'|awk -F\< '{print $1}'` +BMCNM=`grep netmask /tmp/ipmi.data |awk -F\> '{print $2}'|awk -F\< '{print $1}'` +BMCUS=`grep username /tmp/ipmi.data |awk -F\> '{print $2}'|awk -F\< '{print $1}'` +BMCPW=`grep password /tmp/ipmi.data |awk -F\> '{print $2}'|awk -F\< '{print $1}'` +ipmitool lan set 1 ipsrc static +ipmitool lan set 1 ipaddr $BMCIP +ipmitool lan set 1 netmask $BMCNM +if [ ! -z "$BMCGW" ]; then + ipmitool lan set 1 defgw ipaddr $BMCGW +fi +ipmitool user disable 1 +ipmitool user disable 3 +ipmitool user disable 4 +ipmitool user enable 2 +ipmitool user priv 2 4 1 +ipmitool user set name 2 $BMCUS +ipmitool user set password 2 $BMCPW +echo "Set up following user table: " +ipmitool user list 1 + diff --git a/xCAT-nbroot/overlay/bin/dodestiny b/xCAT-nbroot/overlay/bin/dodestiny new file mode 100755 index 000000000..5ab243460 --- /dev/null +++ b/xCAT-nbroot/overlay/bin/dodestiny @@ -0,0 +1,113 @@ +#!/bin/sh +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +for parm in `cat /proc/cmdline`; do + key=`echo $parm|awk -F= '{print $1}'` + if [ "$key" == "xcatd" ]; then + XCATDEST=`echo $parm|awk -F= '{print $2}'` + fi +done +export XCATMASTER=`echo $XCATDEST | awk -F: '{print $1}'` +export XCATPORT=`echo $XCATDEST | awk -F: '{print $2}'` + + +while :; do + DESTINY=`grep destiny /tmp/destiny | awk -F'>' '{print $2}'|awk -F'<' '{print $1}'` + DEST=`echo $DESTINY|awk -F= '{print $1}'` #No bash, no tricks + TARG=`echo $DESTINY|awk -F= '{print $2}'` #No bash, no tricks + DESTINY=`echo $DESTINY|awk '{print $1}'` #No bash, no tricks + if [ $DESTINY == "standby" ]; then + echo "Server notified us of standby condition, please check chain table". + echo "Retrying destiny in 15 seconds" + usleep 15000000 # something may be transiently wrong, check back in 15 seconds + while ! getdestiny.awk; do + echo "Retrying destiny retrieval" + done + exec $0 + fi + if [ $DESTINY == "shell" ]; then + echo "Server notified us to stay in shell state, stopping destiny requests" + exit + fi + if [ $DESTINY == "discover" ]; then + echo "MAC discovery begins" + minixcatd.awk & + while [ ! -r /restart ]; do + ifconfig -a|grep HWaddr|grep -v sit|awk '{print $1 "|" $5}' + MTM="unknown" + SERIAL="unknown" + if [ -x /bin/vpddecode ]; then + MTM=`(/bin/vpddecode|grep Type || echo "unknown unknown: unknown")|awk '{print $3}'` + SERIAL=`(/bin/vpddecode|grep "Box Serial" || echo "unknown unknown unknown: unknown")|awk '{print $4}'` + fi + if [ "$MTM" == "unknown" -a -x /bin/dmidecode ]; then #This gets a bit hackish... iDataplex + MTM=`dmidecode |grep -A4 "^System Information"|grep "Product Name"|awk -F'[' '{print $2}'|awk -F']' '{print $1}'` + SERIAL=`dmidecode |grep -A4 "^System Information"|grep "Serial Number"|awk -F: '{print $2}'` + fi + if [ -r /proc/device-tree/model ]; then + MTM=`cat /proc/device-tree/model |awk -F, '{print $2}'` + fi + ( + echo "" + echo "findme" + echo ""`uname -m`"" + for i in `ifconfig -a|grep HWaddr|grep -v sit|awk '{print $1 "|" $5}'`; do + IFACE=`echo $i|awk -F'|' '{print $1}'` + DRIVER=`ethtool -i $IFACE|grep ^driver|awk '{print $2}'` + echo "$DRIVER|$i" + done + modprobe ipmi_devintf + if modprobe ipmi_si; then + echo "bmc|bmc|"`ipmitool lan print 1|grep ^MAC|awk '{print $4}'`"" + fi + rmmod ipmi_si + rmmod ipmi_devintf + if [ "$MTM" != "unknown" ]; then + echo "$MTM" + fi + if [ "$SERIAL" != "unknown" ]; then + echo "$SERIAL" + fi + echo "" )| udpcat.awk $XCATMASTER $XCATPORT + usleep 30000000 + done + #Discovery complete, restart requested. + exec /bin/restart + fi + if [ $DESTINY == "reboot" ]; then + while ! nextdestiny.awk ; do + echo "Retrying next destiny..." + done + reboot -f + fi + if [ $DEST == "runcmd" ]; then + while ! nextdestiny.awk ; do + echo "Retrying next destiny..." + done + $TARG + fi + if [ $DESTINY == "install" -o $DESTINY == "netboot" ]; then + IMGSERVER=`grep imgserver /tmp/destiny | awk -F'>' '{print $2}'|awk -F'<' '{print $1}'` + INITRD=`grep initrd /tmp/destiny | awk -F'>' '{print $2}'|awk -F'<' '{print $1}'` + KERNEL=`grep kernel /tmp/destiny | awk -F'>' '{print $2}'|awk -F'<' '{print $1}'` + KCMD=`grep kcmdline /tmp/destiny | awk -F'>' '{print $2}'|awk -F'<' '{print $1}'` + wget http://$IMGSERVER/tftpboot/$KERNEL -O /tmp/kernel + wget http://$IMGSERVER/tftpboot/$INITRD -O /tmp/initrd + #START getting ready for kexec + for mod in `lsmod|awk '{print $1}'|grep -v Module`; do + rmmod $mod + done + kexec -f --append="$KCMD" --initrd=/tmp/initrd /tmp/kernel + reboot -f #If script is here, kexec failed, reboot in case it wasn't a linux kernel and let the boot loader handle it instead + fi + if [ $DEST == "runimage" ]; then + mkdir /tmp/$TARG + cd /tmp/$TARG + wget $TARG + while ! nextdestiny.awk ; do + echo "Retrying next destiny..." + done + tar zxvf $TARG + /tmp/$TARG/runme.sh + cd - + fi +done diff --git a/xCAT-nbroot/overlay/bin/getdestiny.awk b/xCAT-nbroot/overlay/bin/getdestiny.awk new file mode 100755 index 000000000..5b823d8fe --- /dev/null +++ b/xCAT-nbroot/overlay/bin/getdestiny.awk @@ -0,0 +1,22 @@ +#!/usr/bin/awk -f +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +BEGIN { + ns = "/inet/tcp/0/127.0.0.1/301" + + print "" |& ns + print "getdestiny" |& ns + print "" |& ns + + while (1) { + if ((ns |& getline) > 0) { + print $0 > "/tmp/destiny" + if ($0 == "") + break + } else { #Timeout + close(ns) + exit 1 + } + } + close(ns) + exit 0 +} diff --git a/xCAT-nbroot/overlay/bin/getipmi.awk b/xCAT-nbroot/overlay/bin/getipmi.awk new file mode 100755 index 000000000..2968f1c76 --- /dev/null +++ b/xCAT-nbroot/overlay/bin/getipmi.awk @@ -0,0 +1,22 @@ +#!/usr/bin/awk -f +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +BEGIN { + ns = "/inet/tcp/0/127.0.0.1/301" + + print "" |& ns + print "getbmcconfig" |& ns + print "" |& ns + + while (1) { + if ((ns |& getline) > 0) { + print $0 > "/tmp/ipmi.data" + if ($0 == "") + break + } else { + close(ns) + exit 1 + } + } + close(ns) + exit 0 +} diff --git a/xCAT-nbroot/overlay/bin/minixcatd.awk b/xCAT-nbroot/overlay/bin/minixcatd.awk new file mode 100755 index 000000000..51089adc3 --- /dev/null +++ b/xCAT-nbroot/overlay/bin/minixcatd.awk @@ -0,0 +1,20 @@ +#!/usr/bin/awk -f +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +BEGIN { + port = 3001 + listener = "/inet/tcp/" port "/0/0" + quit = "no" + while (match(quit,"no")) { + print quit; + while (match(quit,"no") && (listener |& getline) > 0) { + if (match($0,"restart")) { + print "restarting bootstrap process" |& listener + quit="yes" + system("touch /restart") + system("killall usleep") + close(listener) + } + } + close(listener) + } +} diff --git a/xCAT-nbroot/overlay/bin/nextdestiny.awk b/xCAT-nbroot/overlay/bin/nextdestiny.awk new file mode 100755 index 000000000..992748eb9 --- /dev/null +++ b/xCAT-nbroot/overlay/bin/nextdestiny.awk @@ -0,0 +1,22 @@ +#!/usr/bin/awk -f +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +BEGIN { + ns = "/inet/tcp/0/127.0.0.1/301" + + print "" |& ns + print "nextdestiny" |& ns + print "" |& ns + + while (1) { + if ((ns |& getline) > 0) { + print $0 > "/tmp/destiny" + if ($0 == "") + break + } else { + close(ns) + exit 1 + } + } + close(ns) + exit 0 +} diff --git a/xCAT-nbroot/overlay/bin/restart b/xCAT-nbroot/overlay/bin/restart new file mode 100755 index 000000000..18ccd93d3 --- /dev/null +++ b/xCAT-nbroot/overlay/bin/restart @@ -0,0 +1,6 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#Redhcp, do the xcat part again +rm /restart +killall -12 udhcpc;killall -10 udhcpc +sleep 5 +/etc/init.d/S99xcat diff --git a/xCAT-nbroot/overlay/bin/udpcat.awk b/xCAT-nbroot/overlay/bin/udpcat.awk new file mode 100755 index 000000000..6a3c2c166 --- /dev/null +++ b/xCAT-nbroot/overlay/bin/udpcat.awk @@ -0,0 +1,12 @@ +#!/usr/bin/awk -f +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +BEGIN { + xcatdport = ARGV[2] + xcatdhost = ARGV[1] + delete ARGV[1] + delete ARGV[2] + RS="" +} +END { + print $0 |& "/inet/udp/301/"xcatdhost"/"xcatdport +} diff --git a/xCAT-nbroot/overlay/etc/init.d/S10autodetect b/xCAT-nbroot/overlay/etc/init.d/S10autodetect new file mode 100755 index 000000000..59bb2ef7b --- /dev/null +++ b/xCAT-nbroot/overlay/etc/init.d/S10autodetect @@ -0,0 +1,66 @@ +#!/bin/sh +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +nic=0 +hba=0 + +MOD="" + +#Load common usb drivers +modprobe ohci-hcd +modprobe uhci-hcd +modprobe ehci-hcd +for i in $(lspci -n | awk '{print $1 "%" $3}') +do + PCI=$(echo $i | awk -F% '{print $1}') + VID="0x0000$(echo $i | awk -F% '{print $2}' |awk -F: '{print $1}')" + DID="0x0000$(echo $i | awk -F% '{print $2}' |awk -F: '{print $2}')" + if egrep "^[^ ]*[ ]*$VID[ ]*$DID" /lib/modules/*/modules.pcimap >/dev/null + then + TYPE=$( + lspci | \ + grep "^$PCI " | \ + awk '{print $2}' | \ + tr '[A-Z]' '[a-z]' + ) + DESC=$( + lspci | \ + grep "^$PCI " | \ + awk -F: '{print $3}' | \ + sed 's/^ *//' + ) + MOD=$( + egrep "^[^ ]*[ ]*$VID[ ]*$DID" /lib/modules/*/modules.pcimap | \ + head -1 | \ + awk '{print $1}' | \ + tr -d '"' + ) + case "$TYPE" in + ethernet|network) + echo "Found ($MOD) $DESC" + GOTNIC=1 + if [ "$MOD" = "gm" ] + then + echo "alias myri0 $MOD" + echo "alias myri0 $MOD" >>/etc/modules.conf + echo "alias myri0 $MOD" >>/etc/modprobe.conf + else + modprobe $MOD + udhcpc -i eth$nic -b + nic=$(($nic + 1)) + fi + ;; + scsi|raid) + echo "Found ($MOD) $DESC" + GOTHBA=1 + modprobe $MOD + modprobe sd_mod + modprobe scsi_mod + hba=$(($hba + 1)) + ;; + *) + continue + ;; + esac + fi +done + diff --git a/xCAT-nbroot/overlay/etc/init.d/S11stunnel b/xCAT-nbroot/overlay/etc/init.d/S11stunnel new file mode 100755 index 000000000..8e35ab024 --- /dev/null +++ b/xCAT-nbroot/overlay/etc/init.d/S11stunnel @@ -0,0 +1,19 @@ +#!/bin/sh +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#Stunnel init for xcat: +XCATDEST="" +for parm in `cat /proc/cmdline`; do + key=`echo $parm|awk -F= '{print $1}'` + if [ "$key" == "xcatd" ]; then + XCATDEST=`echo $parm|awk -F= '{print $2}'` + fi +done +mkdir -p /etc/stunnel +echo 'client=yes' > /etc/stunnel/stunnel.conf +echo 'foreground=yes' >> /etc/stunnel/stunnel.conf +echo 'verify=0' >> /etc/stunnel/stunnel.conf +echo '[xcatds]' >> /etc/stunnel/stunnel.conf +echo 'accept=301' >> /etc/stunnel/stunnel.conf +echo 'connect='$XCATDEST >> /etc/stunnel/stunnel.conf +mkdir -p /usr/var/run/stunnel +stunnel & diff --git a/xCAT-nbroot/overlay/etc/init.d/S40network b/xCAT-nbroot/overlay/etc/init.d/S40network new file mode 100644 index 000000000..a81246f91 --- /dev/null +++ b/xCAT-nbroot/overlay/etc/init.d/S40network @@ -0,0 +1,2 @@ +#!/bin/sh +exit diff --git a/xCAT-nbroot/overlay/etc/init.d/S99xcat b/xCAT-nbroot/overlay/etc/init.d/S99xcat new file mode 100755 index 000000000..5c30a3d65 --- /dev/null +++ b/xCAT-nbroot/overlay/etc/init.d/S99xcat @@ -0,0 +1,9 @@ +#!/bin/sh +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +( +while ! /bin/getdestiny.awk +do + echo "Retrying destiny retrieval" +done +/bin/dodestiny +) & diff --git a/xCAT-nbroot/overlay/etc/inittab b/xCAT-nbroot/overlay/etc/inittab new file mode 100644 index 000000000..668a92b87 --- /dev/null +++ b/xCAT-nbroot/overlay/etc/inittab @@ -0,0 +1,54 @@ +# /etc/inittab +# +# Copyright (C) 2001 Erik Andersen +# +# Note: BusyBox init doesn't support runlevels. The runlevels field is +# completely ignored by BusyBox init. If you want runlevels, use +# sysvinit. +# +# Format for each entry: ::: +# +# id == tty to run on, or empty for /dev/console +# runlevels == ignored +# action == one of sysinit, respawn, askfirst, wait, and once +# process == program to run + +# Startup the system +::sysinit:/bin/mount -t proc none /proc +::sysinit:/bin/mount -t sysfs none /sys +::sysinit:/bin/mount -t tmpfs -o size=64k,mode=0755 none /dev +::sysinit:/bin/mkdir /dev/pts +::sysinit:/bin/mount -t devpts devpts /dev/pts +::sysinit:/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug +::sysinit:/sbin/mdev -s +::sysinit:/bin/mount -o remount,rw / +::sysinit:/bin/mount -a +::sysinit:/bin/hostname -F /etc/hostname +::sysinit:/sbin/ifconfig lo 127.0.0.1 up +::sysinit:/sbin/route add -net 127.0.0.0 netmask 255.0.0.0 lo +# now run any rc scripts +::sysinit:/etc/init.d/rcS + +# Set up a couple of getty's +#console::respawn:/sbin/getty 19200 console +tty1::respawn:/sbin/getty 38400 tty1 +tty2::respawn:/sbin/getty 38400 tty2 + +# Put a getty on the serial port +#ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100 + +# Logging junk +null::sysinit:/bin/touch /var/log/messages +#null::respawn:/sbin/syslogd -n +#null::respawn:/sbin/klogd -n +#tty3::respawn:/usr/bin/tail -f /var/log/messages + +# Stuff to do for the 3-finger salute +::ctrlaltdel:/sbin/reboot + +# Stuff to do before rebooting +null::shutdown:/usr/bin/killall klogd +null::shutdown:/usr/bin/killall syslogd +null::shutdown:/bin/umount -a -r +null::shutdown:/sbin/swapoff -a + diff --git a/xCAT-nbroot/overlay/etc/profile b/xCAT-nbroot/overlay/etc/profile new file mode 100644 index 000000000..e69de29bb diff --git a/xCAT-nbroot/overlay/usr/share/udhcpc/default.script b/xCAT-nbroot/overlay/usr/share/udhcpc/default.script new file mode 100755 index 000000000..ea6d89a76 --- /dev/null +++ b/xCAT-nbroot/overlay/usr/share/udhcpc/default.script @@ -0,0 +1,26 @@ +#!/bin/sh +case $1 in +deconfig) + /sbin/ifconfig $interface up + /sbin/ifconfig $interface 0.0.0.0 + ;; +bound|renew) + /sbin/ifconfig $interface $ip netmask $subnet + if [ -n "$router" ] ; then + echo "deleting routers" + while route del default gw 0.0.0.0 dev $interface ; do + : + done + + for i in $router ; do + route add default gw $i dev $interface + done + fi + echo -n > /etc/resolv.conf + [ -n "$domain" ] && echo search $domain >> /etc/resolv.conf + for i in $dns ; do + echo adding dns $i + echo nameserver $i >> /etc/resolv.conf + done + ;; +esac diff --git a/xCAT-nbroot/xcat-core-nbroot.spec b/xCAT-nbroot/xcat-core-nbroot.spec new file mode 100644 index 000000000..83c8a745b --- /dev/null +++ b/xCAT-nbroot/xcat-core-nbroot.spec @@ -0,0 +1,56 @@ +%define epoch 0 +%define version 2.0 +%ifarch i386 i586 i686 +%define tarch x86 +%endif +%ifarch x86_64 +%define tarch x86_64 +%endif +%ifarch ppc ppc64 +%define tarch ppc64 +%endif +BuildArch: noarch +%define name xCAT-nbroot-core-%{tarch} +Release: snap%(date +"%Y%m%d%H%M") +AutoReq: false +AutoProv: false + + + + + + +Name: %{name} +#Epoch: %{epoch} +Version: %{version} +Group: System/Utilities +License: EPL +Vendor: IBM Corp. +Summary: xCAT-nbroot-coreprovides opensource components of the netboot image +URL: http://xcat.org +Source1: xcat-nbrootoverlay.tar.gz + +Buildroot: %{_localstatedir}/tmp/xCAT-nbroot-core +Packager: IBM Corp. + +%Description +xcat-nbroot-core provides the xCAT scripts for the mini-root environment +All files included are as they were downloadable on 4/7/2007 +%Prep + + +%Build + +%Install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/usr/share/xcat/netboot/%{tarch}/nbroot +cd $RPM_BUILD_ROOT/usr/share/xcat/netboot/%{tarch}/nbroot +tar zxvf %{SOURCE1} +cd - + + + +%Files +%defattr(-,root,root) +%doc LICENSE.html +/ diff --git a/xCAT-server-2.0/LICENSE.html b/xCAT-server-2.0/LICENSE.html new file mode 100644 index 000000000..83d0eebb0 --- /dev/null +++ b/xCAT-server-2.0/LICENSE.html @@ -0,0 +1,326 @@ + + + + + + + +Eclipse Public License - Version 1.0 + + + + + + +
+ +

Eclipse Public License - v 1.0 +

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER +THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, +REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE +OF THIS AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) +in the case of the initial Contributor, the initial code and documentation +distributed under this Agreement, and
+b) in the case of each subsequent Contributor:

+ +

i) +changes to the Program, and

+ +

ii) +additions to the Program;

+ +

where +such changes and/or additions to the Program originate from and are distributed +by that particular Contributor. A Contribution 'originates' from a Contributor +if it was added to the Program by such Contributor itself or anyone acting on +such Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in conjunction +with the Program under their own license agreement, and (ii) are not derivative +works of the Program.

+ +

"Contributor" means any person or +entity that distributes the Program.

+ +

"Licensed Patents " mean patent +claims licensable by a Contributor which are necessarily infringed by the use +or sale of its Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions +distributed in accordance with this Agreement.

+ +

"Recipient" means anyone who +receives the Program under this Agreement, including all Contributors.

+ +

2. GRANT OF RIGHTS

+ +

a) +Subject to the terms of this Agreement, each Contributor hereby grants Recipient +a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly +display, publicly perform, distribute and sublicense the Contribution of such +Contributor, if any, and such derivative works, in source code and object code +form.

+ +

b) +Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free +patent license under Licensed Patents to make, use, sell, offer to sell, import +and otherwise transfer the Contribution of such Contributor, if any, in source +code and object code form. This patent license shall apply to the combination +of the Contribution and the Program if, at the time the Contribution is added +by the Contributor, such addition of the Contribution causes such combination +to be covered by the Licensed Patents. The patent license shall not apply to +any other combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) +Recipient understands that although each Contributor grants the licenses to its +Contributions set forth herein, no assurances are provided by any Contributor +that the Program does not infringe the patent or other intellectual property +rights of any other entity. Each Contributor disclaims any liability to Recipient +for claims brought by any other entity based on infringement of intellectual +property rights or otherwise. As a condition to exercising the rights and +licenses granted hereunder, each Recipient hereby assumes sole responsibility +to secure any other intellectual property rights needed, if any. For example, +if a third party patent license is required to allow Recipient to distribute +the Program, it is Recipient's responsibility to acquire that license before +distributing the Program.

+ +

d) +Each Contributor represents that to its knowledge it has sufficient copyright +rights in its Contribution, if any, to grant the copyright license set forth in +this Agreement.

+ +

3. REQUIREMENTS

+ +

A Contributor may choose to distribute the +Program in object code form under its own license agreement, provided that: +

+ +

a) +it complies with the terms and conditions of this Agreement; and

+ +

b) +its license agreement:

+ +

i) +effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose;

+ +

ii) +effectively excludes on behalf of all Contributors all liability for damages, +including direct, indirect, special, incidental and consequential damages, such +as lost profits;

+ +

iii) +states that any provisions which differ from this Agreement are offered by that +Contributor alone and not by any other party; and

+ +

iv) +states that source code for the Program is available from such Contributor, and +informs licensees how to obtain it in a reasonable manner on or through a +medium customarily used for software exchange.

+ +

When the Program is made available in source +code form:

+ +

a) +it must be made available under this Agreement; and

+ +

b) a +copy of this Agreement must be included with each copy of the Program.

+ +

Contributors may not remove or alter any +copyright notices contained within the Program.

+ +

Each Contributor must identify itself as the +originator of its Contribution, if any, in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

Commercial distributors of software may +accept certain responsibilities with respect to end users, business partners +and the like. While this license is intended to facilitate the commercial use +of the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes the +Program in a commercial product offering, such Contributor ("Commercial +Contributor") hereby agrees to defend and indemnify every other +Contributor ("Indemnified Contributor") against any losses, damages and +costs (collectively "Losses") arising from claims, lawsuits and other +legal actions brought by a third party against the Indemnified Contributor to +the extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may participate +in any such claim at its own expense.

+ +

For example, a Contributor might include the +Program in a commercial product offering, Product X. That Contributor is then a +Commercial Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance claims and +warranties are such Commercial Contributor's responsibility alone. Under this +section, the Commercial Contributor would have to defend claims against the +other Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, +WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and distributing the +Program and assumes all risks associated with its exercise of rights under this +Agreement , including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs or +equipment, and unavailability or interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF +THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES.

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid +or unenforceable under applicable law, it shall not affect the validity or +enforceability of the remainder of the terms of this Agreement, and without +further action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Program itself (excluding combinations of the Program with +other software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the date +such litigation is filed.

+ +

All Recipient's rights under this Agreement +shall terminate if it fails to comply with any of the material terms or +conditions of this Agreement and does not cure such failure in a reasonable +period of time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute +copies of this Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The Agreement +Steward reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement Steward has +the right to modify this Agreement. The Eclipse Foundation is the initial +Agreement Steward. The Eclipse Foundation may assign the responsibility to +serve as the Agreement Steward to a suitable separate entity. Each new version +of the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version of +the Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly stated +in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to +the intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved.

+ +

This Agreement is governed by the laws of the +State of New York and the intellectual property laws of the United States of +America. No party to this Agreement will bring a legal action under this +Agreement more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation.

+ +

 

+ +
+ + + + diff --git a/xCAT-server-2.0/README b/xCAT-server-2.0/README new file mode 100644 index 000000000..6469a99f8 --- /dev/null +++ b/xCAT-server-2.0/README @@ -0,0 +1,8 @@ +This is the README for xCAT - Extreme Cluster Administration Toolkit, +Version 2.0 alpha release. xCAT is a toolkit for the deployment and administration of clusters. +xCAT 2.0 is offered OSS under a EPL license. + +See http://www.eclipse.org/legal/epl-v10.html for license details. + +The xCAT 2.0 code and documentation can be downloaded from http://xcat.org. +For support, go to http://xcat.org for instructions on joining the mailing list. diff --git a/xCAT-server-2.0/etc/init.d/xcatd b/xCAT-server-2.0/etc/init.d/xcatd new file mode 100755 index 000000000..020ae4559 --- /dev/null +++ b/xCAT-server-2.0/etc/init.d/xcatd @@ -0,0 +1,98 @@ +#!/bin/bash +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +# chkconfig: 345 85 60 +# description: xCAT management service +# processname: xcatd + +### BEGIN INIT INFO +# Provides: xcatd +# Default-Start: 3 4 5 +# Default-stop: 0 1 2 6 +# Short-Description: xCATd +# Description: xCAT management service +### END INIT INFO + + +MStatus() +{ + ps ax|grep -v grep|grep xcatd: >& /dev/null + if [ "$?" = "0" ]; then + RVAL=0 + echo "xCAT service is running" + else + RVAL=3 + echo "xCAT service is not running" + fi + return $RVAL +} + +if [ -f /lib/lsb/init-functions ]; then + . /lib/lsb/init-functions + START_DAEMON=start_daemon + STATUS=MStatus + LOG_SUCCESS=log_success_msg + LOG_FAILURE=log_failure_msg + LOG_WARNING=log_warning_message +elif [ -f /etc/init.d/functions ]; then + echo RH + . /etc/init.d/functions + START_DAEMON=daemon + STATUS=status + LOG_SUCCESS=success + LOG_FAILURE=failure + LOG_WARNING=passed +else + echo "Error, don't know how to start on this platform" + exit 1 +fi + +case $1 in +restart) + $0 stop + $0 start + ;; +status) + $STATUS + ;; +stop) + echo -n "Stopping xCATd " + $STATUS >& /dev/null + if [ "$?" != "0" ]; then + echo -n "xCATd not running, not stopping " + $LOG_WARNING + exit 1 + fi + kill -TERM -`cat /var/run/xcatd.pid` + let i=0; + while $STATUS >& /dev/null && [ $i -lt 15 ]; do + usleep 100000 + let i=i+1 + done + $STATUS >& /dev/null + if [ "$?" == "0" ]; then + kill -KILL -`cat /var/run/xcatd.pid` + fi + usleep 100000 + $STATUS >& /dev/null + if [ "$?" == "0" ]; then + $LOG_FAILURE + exit 1 + fi + $LOG_SUCCESS + rm /var/run/xcatd.pid + ;; +start) + $STATUS >& /dev/null + if [ "$?" == "0" ]; then + echo -n "xCATd already running " + $LOG_WARNING + exit + fi + echo -n "Starting xCATd " + xcatd -p /var/run/xcatd.pid && $LOG_SUCCESS || $LOG_FAILURE + ;; +esac + + + + diff --git a/xCAT-server-2.0/etc/xcat/postscripts.rules b/xCAT-server-2.0/etc/xcat/postscripts.rules new file mode 100644 index 000000000..e3b37b281 --- /dev/null +++ b/xCAT-server-2.0/etc/xcat/postscripts.rules @@ -0,0 +1,202 @@ +# Post Install Scripts +# +# The Post Install Scripts table is a rules-based system for determining +# which install scripts and in what order to execute them. +# +# Rules and actions are formatted: +# +# RULE { +# script [args] +# script [args] +# script [args] +# ... +# } +# +# Rules are defined as VAR=REGEX. Where VAR may be any exported +# environmental variable, ALL, or NODERANGE (special, see below). +# +# Regular expressions (REGEX or regex) are a powerful way to define a match. +# The perl regex engine is used to parse the rules. Please do not confuse +# regex with shell expansion characters. +# +# e.g. compute* does NOT match anything starting with 'compute' as bash +# would. compute.* is the correct regex for a that match. man perlre for +# regex docs. (BTW, regex compute* matches 'comput' with 0 or more 'e's at +# the end.) +# +# There is no need to place beginning and end markers (^$) in your regex. +# They are added automatically. +# +# Actions are defined as: script [optional args] +# +# A special action/script "STOP" will stop processing all rules and exit. +# +# Scripts are run top-down multiples rules can match. +# +# Nesting with () and operators and, or, and not are supported. +# +# The following VARs are exported by xCAT: +# +# OSVER=(rh62|rh70|rh71|rh72|rh73|rh80|suse81|sles8) +# OSVER is the OS that has just been installed. OSVER may be defined by +# regex. (e.g. rh.* is "anything starting with rh") +# +# ARCH=(x86|ia64) +# ARCH is the hardware architecture (uname -m). ARCH may be defined by +# regex. +# +# NODERANGE=(e.g. node1-node10) +# NODERANGE follows the xCAT noderange format as defined by noderange.5 +# (man noderange). Regex is supported as part of noderange, however it +# must be prefixed with a '@', e.g. (@node.* is "anything starting with +# node"). +# +# ALL (Apply to all) +# +# TABLE:TABLENAME:KEY:FIELD= +# +# The last rule is special and is determined at runtime. +# +# TABLENAME is the name of an xCAT table located in $XCATROOT/etc. You +# may create your own tables. +# +# KEY is the first field of any xCAT table. $NODE is a +# special value for key, usually the key is a fixed name, however many xCAT +# tables start with a node or resource group name. +# +# FIELD is a numeric value for fields associated with KEY. The first field +# is 1. Special names are available (e.g. $nodehm_eth0) and are defined in +# $XCATROOT/lib/functions. Any environmental variable can be used. +# +# e.g.: +# +# TABLE:nodehm.tab:$NODENAME:$nodehm_eth0=e1000 +# +# Would only execute scripts where eth0 was defined as e1000 in nodehm.tab +# for any node. +# +# The script $XCATROOT/bin/postage is the master post installation script +# that parses this table and runs the scripts. You can use the script +# $XCATROOT/bin/postrules NODENAME ARCH OSVER to parse this table to test +# your rules. +# +# e.g. postrules node1 x86 rh73 +# + + +# Setup syslog +OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + syslog +} + +# update/add packages +OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + updaterpms nodeps + otherrpms + forcerpms +} + +# setup NFS mounts +OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + nfsmounts +} + +# sync clock +OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + syncclock + setupntp +} + +# Setup PAM +TABLE:noderes:$NODE:access=Y and +(OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.*) { + setuppam +} + +# Setup PBS +TABLE:noderes:$NODE:pbs=Y and +(OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.*) { + openpbs +} + +# Setup paths +OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + setuppaths +} + +# Setup services +OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + setupservices +} + +# Setup rsh/ssh. +OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + remoteshell +} + +# Enable sysctl +OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + enablesysreq +} + +# Enable debug +OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + enabledebug +} + +# Setup Myrinet MX +TABLE:noderes:$NODE:install_roll=Y { + setupstage +} + +# Re setup syslog, RPM updates may have hosed it. +OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + resyslog +} + + +ALL { + serialconsole +} + +# +# Start custom section +# +# Here is a good place to put custom code +# + +# +# End custom section +# + +# Sync dirs +OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { + syncdirs +} + +# Unmount /post. +ALL { + umountpost +} + +# Update remote boot flag. rsh/ssh must be setup first +# unless xcatd is used. To use rsh or ssh, pass as +# option to updateflag else pass xcatd +# +#2nd arg can be Master IP for multiple master IP support. +# +ALL { + updateflag.awk $MASTER +} + +# hardcode networking +#OSVER=sl[34].* or OSVER=centos.* or OSVER=rh.* or OSVER=sles.* or OSVER=suse.* { +# hardnis +# hardmodopts options e1000 RxIntDelay=0,0 TxIntDelay=0,0 RxAbsIntDelay=0,0 TxAbsIntDelay=0,0 InterruptThrottleRate=0,0 +#} + +# SuSE needs an extra reboot. +OSVER=sles.* or OSVER=suse.* { + reboot +} + diff --git a/xCAT-server-2.0/usr/lib/xcat/dsh/Context/XCAT.pm b/xCAT-server-2.0/usr/lib/xcat/dsh/Context/XCAT.pm new file mode 100644 index 000000000..f5128b793 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/dsh/Context/XCAT.pm @@ -0,0 +1,458 @@ +#!/usr/bin/perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html + +package XCAT; +use base DSHContext; +use Socket; +use xCAT::Utils; +use xCAT::MsgUtils; + +# Define remote shell globals from xCAT + +our $XCAT_RSH_CMD; +our $XCAT_RCP_CMD; + +# +# get the remote command settings +# + +XCAT->get_xcat_remote_cmds; + +# Global Data structures for xCAT context + +our @xcat_node_list = (); +our %xcat_nodegroup_table = (); + +#------------------------------------------------------------------------------- + +=head3 + context_defaults + + Assign default properties for the xCAT context. A default + property for a context will be used if the property is + not user configured in any other way. + + Arguments: + None + + Returns: + A reference to a hash table with the configured + default properties for the xCAT context + + Globals: + $XCAT_RSH_CMD + + Error: + None + + Example: + $default_properties = XCAT->config_defaults; + + Comments: + $defaults hash table contents: + + $defaults{'NodeRemoteShell'} - default remote shell to use for node targets + +=cut + +#------------------------------------------------------------------------------- +sub context_defaults +{ + my %defaults = (); + + $defaults{'NodeRemoteShell'} = $XCAT_RSH_CMD; + + return \%defaults; +} + +#------------------------------------------------------------------------------- + +=head3 + context_properties + + Configure the user specified context properties for the xCAT context. + These properties are configured by the user through environment + variables or external configuration files. + + Arguments: + None + + Returns: + A reference to a hash table of user-configured properties for + the xCAT context. + + Globals: + None + + Error: + None + + Example: + $properties = XCAT->config_properties + + Comments: + +=cut + +#------------------------------------------------------------------------------- +sub context_properties +{ + my %properties = (); + + $properties{'RemoteShell'} = $XCAT_RSH_CMD; + $properties{'RemoteCopyCmd'} = $XCAT_RCP_CMD; + + return \%properties; +} + +#------------------------------------------------------------------------------- + +=head3 + all_devices + + Comments: devices are nodes in the XCAT context. Use node flags + and not device flags. + +=cut + +#------------------------------------------------------------------------------- +sub all_devices +{ + my ($class, $resolved_targets) = @_; + + xCAT::MsgUtils->message( + "E", + " Nodes and Devices are considered nodes in xCAT.\n The -A flag is not supported. Use the all group in XCAT to dsh to all node/devices.\n" + ); + return; +} + +#------------------------------------------------------------------------------- + +=head3 + all_nodes + + Returns an array of all node names in the xCAT context + Note in xCAT everything is a node including devices + + Arguments: + None + + Returns: + An array of node/device names + + Globals: + @xcat_node_list + + Error: + None + + Example: + @nodes = XCAT->get_xcat_node_list; + + Comments: + +=cut + +#------------------------------------------------------------------------------- +sub all_nodes +{ + scalar(@xcat_node_list) || XCAT->get_xcat_node_list; + return @xcat_node_list; +} + +#------------------------------------------------------------------------------- + +=head3 + all_nodegroups + + Returns an array of all node group names in the xCAT context + Note in xCAT everything is a node including devices + + Arguments: + None + + Returns: + An array of node/device group names + + Globals: + %xcat_nodegroup_table + + Error: + None + + Example: + @nodegroups = XCAT->all_nodegroups; + + Comments: + +=cut + +#------------------------------------------------------------------------------- +sub all_nodegroups +{ + scalar(%xcat_nodegroup_table) || XCAT->get_xcat_nodegroup_table; + return keys(%xcat_nodegroup_table); +} + +#------------------------------------------------------------------------------- + +=head3 + nodegroup_members + + Given a node/device group in the xCAT context, this routine expands the + membership of the group and returns a list of its members. + + Arguments: + $nodegroup - node group name + + Returns: + An array of node group members + + Globals: + $nodegroup_path + + Error: + None + + Example: + $members = XCAT->nodegroup_members('MyGroup1'); + + Comments: + +=cut + +#------------------------------------------------------------------------------- +sub nodegroup_members +{ + my ($class, $nodegroup) = @_; + my %node_list = (); + scalar(%xcat_nodegroup_table) || XCAT->get_xcat_nodegroup_table; + !defined($xcat_nodegroup_table{$nodegroup}) && return undef; + + my @nodes = split /,/, $xcat_nodegroup_table{$nodegroup}; + + foreach my $node (@nodes) + { + $node_list{$node}++; + } + + my @members = keys(%node_list); + return \@members; + +} + +#------------------------------------------------------------------------------- + +=head3 + resolve_node + + Within the xCAT context, resolve the name of a given node and + augment the supplied property hash table with xCAT node information. + + Arguments: + $target_properties - basic properties hash table reference for a node + + Returns: + 1 if resolution was successful + undef otherwise + + Globals: + $XCAT_RSH_CMD + $XCAT_RCP_CMD + + Error: + None + + Example: + XCAT->resolve_node($target_properties); + + Comments: + +=cut + +#------------------------------------------------------------------------------- +sub resolve_node +{ + my ($class, $target_properties) = @_; + + $$target_properties{'remote-shell'} = $XCAT_RSH_CMD; + $$target_properties{'remote-copy'} = $XCAT_RCP_CMD; + + return 1; +} + +#------------------------------------------------------------------------------- + +=head3 + get_xcat_remote_cmds + + Using xCAT native commands, store the remote command settings for + the remote shell and remote copy commands as defined in xCAT + site.tab file. + + Arguments: + None + + Returns: + None + + Globals: + $XCAT_RSH_CMD + $XCAT_RCP_CMD + + Error: + None + + Example: + XCAT->get_xcat_remote_cmds + + Comments: + Internal routine only + +=cut + +#------------------------------------------------------------------------------- +sub get_xcat_remote_cmds +{ + $XCAT_RSH_CMD = "/usr/bin/ssh"; # default + $XCAT_RCP_CMD = "/usr/bin/scp"; #default + my @remote_shell = xCAT::Utils->get_site_attribute("rsh"); + if ($remote_shell[0]) + { + $XCAT_RSH_CMD = $remote_shell[0]; + } + my @remote_copy = xCAT::Utils->get_site_attribute("rcp"); + if ($remote_copy[0]) + { + $XCAT_RCP_CMD = $remote_copy[0]; + } + +} + +#------------------------------------------------------------------------------- + +=head3 + get_xcat_node_list + + Using xCAT native commands, this routine builds a cached list of + node/device names defined in the xCAT context + + Arguments: + None + + Returns: + None + + Globals: + %xcat_node_list + + Error: + None + + Example: + XCAT->get_xcat_node_list + + Comments: + Internal routine only + +=cut + +#------------------------------------------------------------------------------- +sub get_xcat_node_list +{ + @xcat_node_list = xCAT::Utils->get_node_list; + chomp(@xcat_node_list); +} + +#------------------------------------------------------------------------------- + +=head3 + get_xcat_nodegroup_table + + Using xCAT native commands, this routine builds a cached list of + node groups and their members defined in the xCAT context + + Arguments: + None + + Returns: + None + + Globals: + %xcat_nodegroup_table + + Error: + None + + Example: + XCAT->get_xcat_nodegroup_table + + Comments: + Internal routine only + +=cut + +#------------------------------------------------------------------------------- +sub get_xcat_nodegroup_table +{ + $node_list = ""; + my @nodegroups = xCAT::Utils->list_all_node_groups; + for my $group (@nodegroups) + { + chomp($group); + my @nodes = `nodels $group`; + while (@nodes) + { + my $nodename = shift @nodes; + chomp($nodename); + $node_list .= $nodename; + $node_list .= ","; + } + chop($node_list); + $xcat_nodegroup_table{$group} = $node_list; + $node_list = ""; + } +} + +sub query_node +{ + my ($class, $node, $flag) = @_; + my @xcat_nodes = all_nodes(); + + $~ = "NODES"; + if ($flag) + { + if (grep(/^$node$/, @xcat_nodes)) + { + print("$node : Valid\n"); + } + else + { + print("$node : Invalid\n"); + } + } + else + { + print("$node : Invalid\n"); + } +} + +sub query_group +{ + + my ($class, $group) = @_; + my @xcat_groups = all_nodegroups(); + + $~ = "GROUPS"; + if (grep(/^$group$/, @xcat_groups)) + { + print("$group : Valid\n"); + } + else + { + print("$group : Invalid\n"); + } +} + +1; #end diff --git a/xCAT-server-2.0/usr/lib/xcat/monitoring/samples/mycode.pm b/xCAT-server-2.0/usr/lib/xcat/monitoring/samples/mycode.pm new file mode 100644 index 000000000..26f346060 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/monitoring/samples/mycode.pm @@ -0,0 +1,49 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_monitoring::mycode; +1; + +# This subroutine get called when new nodes are added into the cluster +# or nodes are removed from the cluster. +# +sub processTableChanges { + my $action=shift; + if ($action =~ /xCAT_monitoring::mycode/){ + $action=shift; + } + my $tablename=shift; + my $old_data=shift; + my $new_data=shift; + + my @nodenames=(); + if ($action eq "a") { #nodes added in the cluster + if ($new_data) { + push(@nodenames, $new_data->{node}); + $noderange=join(',', @nodenames); + open(FILE, ">>/var/log/mycode.log") or dir ("cannot open the file\n"); + print (FILE "new nodes in the cluster are: $noderange\n"); + close(FILE); + } + } + elsif ($action eq "d") { #nodes removed from the cluster + #find out the index of "node" column + if ($old_data->[0]) { + $colnames=$old_data->[0]; + my $i; + for ($i=0; $i<@$colnames; ++$i) { + if ($colnames->[$i] eq "node") {last;} + } + + for (my $j=1; $j<@$old_data; ++$j) { + push(@nodenames, $old_data->[$j]->[$i]); + } + + if (@nodenames > 0) { + $noderange=join(',', @nodenames); + open(FILE, ">>/var/log/mycode.log") or dir ("cannot open the file\n"); + print (FILE "nodes leaving the cluster are: $noderange\n"); + close(FILE); + } + } + } + return 0; +} diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/DBobjectdefs.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/DBobjectdefs.pm new file mode 100644 index 000000000..7b67ea088 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/DBobjectdefs.pm @@ -0,0 +1,1145 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +##################################################### +# +# xCAT plugin package to handle commands that manage the xCAT object +# definitions +# +##################################################### +package xCAT_plugin::DBobjectdefs; +use xCAT::NodeRange; +use xCAT::Schema; +use Data::Dumper; +use Getopt::Long; + +# options can be bundled up like -vV +Getopt::Long::Configure("bundling"); + +# calculate the following sets of attr values for each object definition +# (where appropriate) +# - order of precedence: CLIATTRS overrides FILEATTRS +%::CLIATTRS; # attr=values provided on the command line +%::FILEATTRS; # attr=values provided in a file +%::FINALATTRS; # actual attr=values that are used to define the object + +@::objects; # list of object names to define +$::noderange; # noderange from command line + +#------------------------------------------------------------------------------ + +=head1 DBobjectdefs + +This program module file supports the management of the xCAT data object +definitions. + +Supported xCAT data object commands/subroutines: + defmk - create xCAT data object definitions. + defls - list xCAT data object definitions. + defch - change xCAT data object definitions. + defrm - remove xCAT data object definitions. + +If adding to this file, please take a moment to ensure that: + + 1. Your contrib has a readable pod header describing the purpose and use of + the subroutine. + + 2. Your contrib is under the correct heading and is in alphabetical order + under that heading. + + 3. You have run tidypod on your this file and saved the html file + +=cut + +#------------------------------------------------------------------------------ + +=head2 xCAT data object definition support + +=cut + +#------------------------------------------------------------------------------ + +#---------------------------------------------------------------------------- + +=head3 handled_commands + + Return a list of commands handled by this plugin + +=cut + +#----------------------------------------------------------------------------- + +sub handled_commands +{ + return { + defmk => "DBobjectdefs", + defls => "DBobjectdefs", + defch => "DBobjectdefs", + defrm => "DBobjectdefs" + }; +} + +#---------------------------------------------------------------------------- + +=head3 process_request + + Check for xCAT command and call the appropriate subroutine. + + Arguments: + + Returns: + 0 - OK + 1 - error + Globals: + + Error: + + Example: + + Comments: +=cut + +#----------------------------------------------------------------------------- + +sub process_request +{ + + $::request = shift; + $::callback = shift; + + my $ret; + my $msg; + my %rsp = (); + + # globals used by all subroutines. + $::command = $::request->{command}->[0]; + $::args = $::request->{arg}; + + # figure out which cmd and call the subroutine to process + if ($::command eq "defmk") + { + ($ret, $msg) = &defmk; + } + elsif ($::command eq "defls") + { + ($ret, $msg) = &defls; + } + elsif ($::command eq "defch") + { + ($ret, $msg) = &defch; + } + elsif ($::command eq "defrm") + { + ($ret, $msg) = &defrm; + } + + if ($msg) + { + my %rsp = (); + $rsp->{data}->[0] = $msg; + $::callback->($rsp); + } + return $ret; +} + +#---------------------------------------------------------------------------- + +=head3 processArgs + + Process the command line. Covers all four commands. + + Also - Process any input files provided on cmd line. + + Arguments: + + Returns: + 0 - OK + 1 - just print usage + 2 - error + Globals: + + Error: + + Example: + + Comments: +=cut + +#----------------------------------------------------------------------------- + +sub processArgs +{ + $gotattrs = 0; + + @ARGV = @{$::args}; + + # parse the options - include any option from all 4 cmds + if ( + !GetOptions( + 'a=s' => \$::opt_a, + 'dynamic|d' => \$::opt_d, + 'f=s' => \$::opt_f, + 'i=s' => \$::opt_i, + 'help|h' => \$::opt_h, + 'long|l' => \$::opt_l, + 'm|minus' => \$::opt_m, + 'o=s' => \$::opt_o, + 'r|relace' => \$::opt_r, + 't=s' => \$::opt_t, + 'verbose|V' => \$::opt_V, + 'version|v' => \$::opt_v, + 'w=s' => \$::opt_w, + 'x=s' => \$::opt_x, + 'z=s' => \$::opt_z + ) + ) + { + return 1; + } + + # put attr=val operands in ATTRS hash + while (my $a = shift(@ARGV)) + { + + #print "arg= $a\n"; + + if (!($a =~ /=/)) + { + @::noderange = &noderange($a, 0); + } + else + { + + # if it has an "=" sign its an attr=val - we hope + my ($attr, $value) = $a =~ /^\s*(\S+?)\s*=\s*(\S+.*)$/; + if (!defined($attr) || !defined($value)) + { + print "bad attr=val pair - $a\n"; + } + + $gotattrs = 1; + + my $found = 0; + + # replace the following with check based on schema.pm + + #foreach my $va (@::VALID_ATTRS) + #{ + # if (($attr =~ /^$va$/i) && !$found) + # { + # $::ATTRS{$va} = $value; + # $found = 1; + # } + #} + + # put attr=val in hash + $::ATTRS{$attr} = $value; + + #print "attr=$attr, val= $::ATTRS{$attr} \n"; + $found = 1; + + if (!$found) + { + print "$attr - is not a valid attribute!\n"; + } + } + } + + # Option -h for Help + if (defined($::opt_h)) + { + return 1; + } + + # Option -v for version - do we need this??? + if (defined($::opt_v)) + { + my %rsp; + $rsp->{data}->[0] = "$::command - version 1.0"; + $::callback->($rsp); + return 0; + } + + # Option -V for verbose output + if (defined($::opt_V)) + { + $::verbose = 1; + print "set verbose mode\n"; + } + + # could have comma seperated list of types + if ($::opt_t) + { + + # make a list + # - is this a valid type? - check schema !! + # note: if type is used with file then just do that type even + # if there are others in the file + if ($::opt_t =~ /,/) + { + + # can't have mult types when using attr=val + if ($gotattrs) + { + print + "error - can't have multiple type with attr=value pairs.\n"; + } + else + { + @::clobjtypes = split(',', $::opt_t); + } + } + else + { + push(@::clobjtypes, $::opt_t); + } + } + + # if there is no other input for object names then we need to + # find all the object names for the specified types + if ( + $::opt_t + && !( + $::opt_o + || $::opt_f + || $::opt_x + || $::opt_z + || $::opt_a + || @::noderange + ) + ) + { + foreach my $t (@::clobjtypes) + { + + # look up all objects of this type in the DB ??? + + # add them to the list of objects + #push(@::clobjnames, $); + + } + + } + + # - get object names from the -o option or the noderange + if ($::opt_o) + { + print "object = $::opt_o\n"; + + # make a list + if ($::opt_o =~ /,/) + { + @::clobjnames = split(',', $::opt_o); + } + else + { + push(@::clobjnames, $::opt_o); + } + } + elsif (@::noderange && (grep(/node/, @::clobjtypes))) + { + + # if there's no object list and the type is node then the + # noderange list is assumed to be the object names list + @::clobjnames = @::noderange; + } + + # - does input file exist etc. - read input file - stanza + # - add support for XML files later! + + # check for stanza file + if ($::opt_z) + { + print "filename = $::opt_z\n"; + + if (!-e $::opt_z) + { + print "Error: the file \'$::opt_z\' does not exist!\n"; + print "Errors occurred when processing the command line args.\n"; + return 2; + } + else + { + + # process the file + # create hash of objects/attrs etc. %::FILEATTRS + # &readstanzafile(); + # - %::FILEATTRS{fileobjname}{attr}=val + # set @::fileobjtypes, @::fileobjnames, %::FILEATTRS + } + } + + # check for object list file + if ($::opt_f) + { + print "obj list filename = $::opt_f\n"; + + # read the file and cp the name into @::objfilelist + + } + + # can't have -a with other obj sources + if ($::opt_a + && ($::opt_o || $::opt_f || $::opt_x || $::opt_z || @::noderange)) + { + + # error + # usage + } + + # if -a then get a list of all DB objects + if ($::opt_a) + { + print "all objects \n"; + + # get a list of all objects defined in the DB + # @::clobjnames = whatever + + } + + # must have object type(s) - + if (!@::clobjtypes && !@::fileobjtypes) + { + print "Error - must specify object type on command line or in file!\n"; + return 2; + } + + # must have object name(s) - + if (!@::clobjnames && !@::fileobjnames) + { + print "Error - must specify object name on command line or in file!\n"; + return 2; + } + + # combine object name from file with obj names from cmd line ?? + @::allobjnames = @::clobjnames; + if (@::fileobjnames) + { + push @::allobjnames, @::fileobjnames; + } + + return 0; +} + +#---------------------------------------------------------------------------- + +=head3 defmk + + Support for the xCAT defmk command. + + Arguments: + Returns: + 0 - OK + 1 - error + Globals: + + Error: + + Example: + + Comments: + Object names to create are derived from + -o, -t, w, -z, -x, or noderange! + Attr=val pairs come from cmd line args or -z/-x files +=cut + +#----------------------------------------------------------------------------- + +sub defmk +{ + my $lookup_key; + my $lookup_value; + my $lookup_table; + my $lookup_attr; + my $lookup_type; + my $lookup_data; + + # process the command line + my $rc = &processArgs; + if ($rc != 0) + { + + # rc: 0 - ok, 1 - help, 2 - error + &defmk_usage; + return ($rc - 1); + } + + # !!! can only have one type on cmd line when defining objects!!! + # if # elements in @::clobjtypes is > 1 then error! + + # set $objtype & fill in cmd line hash + if (%::ATTRS) + { + + # if attr=val on cmd line then could only have one type + $::objtype = $::opt_t; + + # + # set cli attrs for each object definition + # + if (&setCLIattrs != 0) + { + print "Could not set command line values for object definitions!\n"; + return 1; + } + } + + # + # Pull all the pieces together for the final hash + # + if (&setFINALattrs != 0) + { + print "Could not determine attribute values for object definitions!\n"; + return 1; + } + + # + # OK - create the tables in the xCAT database + # + foreach my $objname (@::allobjnames) + { + + print + "defmk: object name = $objname, type= $::FINALATTRS{$objname}{objtype} +\n"; + + my $type = $::FINALATTRS{$objname}{objtype}; + + # get the object type decription from Schema.pm + my $datatype = $xCAT::Schema::defspec{$type}; + + # this is the list of valid attr names for this type object + my @validattrs = keys %{$datatype->{'attrs'}}; + + #print "validattrs= \'@validattrs\'\n"; + + # if type is osimage, site, network, dynamic group then all attrs + # go in the same table + # - just need to check for valid attrs then write to table + # - the node type is the only one that needs the special + # processing below + if (($type eq 'site') || ($type eq 'network') || ($type eq 'osimage')) + { + + next; + } + + if (($type eq 'group') && $::opt_d) + { + + # if it's a dynamic group it all goes in the group table + + next; + } + + # for other object types we need to figure out what table to + # store each attr + foreach my $attr (keys %{$::FINALATTRS{$objname}}) + { + if ($attr eq "objtype") + { + + # objtype not stored in object definition + next; + } + + print "attr= $attr , val = $::FINALATTRS{$objname}{$attr}\n"; + + # if valid attr for this type then add to def + if (!grep(/^$attr$/, @validattrs)) + { + print + "\'$attr\' is not a valid attribute for type \'$type\'.\n"; + print "Skipping to the next attribute.\n"; + next; + } + + # check the defspec to see where this attr goes + my @this_attr_array = $datatype->{'attrs'}->{$attr}; + + foreach (@this_attr_array) + { + + # ex. this_attr='node' + my $this_attr = $_->[0]; + + # the table might depend on the value of the attr + # - like if 'mgtmethod=mp' + if (exists($this_attr->{only_if})) + { + my ($check_attr, $check_value) = + split('\=', $this_attr->{only_if}); + + # if my attr value for the attr to check doesn't + # match this then try the next one + # ex. say I want to set hdwctrlpoint, the table + # will depend on the mgtmethod attr - so I need + # to find the 'only_if' that matches the value + # specified for that attr + #print "attr=$check_attr, myval= $::FINALATTRS{$objname}{$check_attr}\n"; + + next + if $::FINALATTRS{$objname}{$check_attr} ne $check_value; + } + + # OK - get the info needed to add to the DB table + + # ex. 'nodelist.node', 'attr:node' + ($lookup_key, $lookup_value) = + split('\=', $this_attr->{access_tabentry}); + + # ex. 'nodelist', 'node' + ($lookup_table, $lookup_attr) = split('\.', $lookup_key); + + # ex. 'attr', 'node' + ($lookup_type, $lookup_data) = split('\:', $lookup_value); + + } + + print + "lookup_table=$lookup_table, lookup_attr=$lookup_attr, lookup_type=$lookup_type\n"; + + # write the attr to the DB table + # - future - may want to gather all attrs for each table + # - maybe reduce DB calls + + } + print "\n"; + + } + + return 0; +} + +#---------------------------------------------------------------------------- + +=head3 setCLIattrs + + get list of object names to define + create hash w/cmd line attrs + %::CLIATTRS{objname}{attr}=val + + Arguments: + + Returns: + 0 - OK + 1 - error + Globals: + + Error: + + Example: + + Comments: +=cut + +#----------------------------------------------------------------------------- + +sub setCLIattrs +{ + + foreach my $objname (@::clobjnames) + { + + # set the objtype attr - if provided + if ($::objtype) + { + $::CLIATTRS{$objname}{objtype} = $::objtype; + } + + # set the attrs from the attr=val pairs + foreach my $attr (keys %::ATTRS) + { + $::CLIATTRS{$objname}{$attr} = $::ATTRS{$attr}; + } + } + + return 0; +} + +#---------------------------------------------------------------------------- + +=head3 setFINALattrs + + create %::FINALATTRS{objname}{attr}=val hash + conbines %::FILEATTRS, and %::CLIATTR + + Arguments: + + Returns: + 0 - OK + 1 - error + Globals: + + Error: + + Example: + + Comments: +=cut + +#----------------------------------------------------------------------------- +sub setFINALattrs +{ + + # set the final hash based on the info from the input file + if (@::fileobjnames) + { + foreach my $objname (@::fileobjnames) + { + + # check if this object is one of the type specified + if (grep(/$::FILEATTRS{$objname}{objtype}/, @::clobtypes)) + { + + # if so then add it to the final hash + foreach my $attr (keys %{$::FILEATTRS{$objname}}) + { + $::FINALATTRS{$objname}{$attr} = + $::FILEATTRS{$objname}{$attr}; + } + } + + } + + } + + # set the final hash based on the info from the cmd line hash + foreach my $objname (@::clobjnames) + { + foreach my $attr (keys %{$::CLIATTRS{$objname}}) + { + $::FINALATTRS{$objname}{$attr} = $::CLIATTRS{$objname}{$attr}; + } + } + + return 0; +} + +#---------------------------------------------------------------------------- + +=head3 defch + + Support for the xCAT defch command. + + Arguments: + + Returns: + 0 - OK + 1 - error + Globals: + + Error: + + Example: + + Comments: + Object names to change are derived from + -o, -t, w, -z, -x, or noderange! + Attrs may be set, added to, replaced(-r), or be + partially rewmoved (-m) +=cut + +#----------------------------------------------------------------------------- + +sub defch +{ + + # process the command line + my $rc = &processArgs; + if ($rc != 0) + { + + # rc: 0 - ok, 1 - help, 2 - error + &defch_usage; + return ($rc - 1); + } + + # + # set cli attrs for each object definition + # + if (%::ATTRS) # if we had any attr=val on the cmd line + { + if (&setCLIattrs != 0) + { + print "Could not set command line values for object definitions!\n"; + return 1; + } + + } + + # + # Pull all the pieces together for the final hash + # - from cmd line and/or file + # + if (&setFINALattrs != 0) + { + print "Could not determine attribute values for object definitions!\n"; + return 1; + } + + # + # get the current object definitions from DB!! + # could have multiple types, objects and attrs + # want to get hash of attr values for each object + # + foreach my $objname (@::allobjnames) + { + + # get values for this object & attrs + my @attrlist = keys %{$::FINALATTRS{$objname}}; + print "get these attrs for object \'$objname\': @attrlist\n"; + + # add the info to the DBdefs hash + foreach my $attr (@attrlist) + { + + # if ($val) { + # $DBdefs{$objname}{$attr}=val + # } + } + } + + # now go throught our objects + foreach my $objname (@::allobjnames) + { + my @attrval; + + # is this a valid object name + #?? if (!grep(/$objname/, @::DBobjnames) error - msg - next + + # go through the new attrs for this object + foreach my $attr (keys %{$::FINALATTRS{$objname}}) + { + if ($::opt_r) + { + + # - just set/replace the attribute + push(@attrval, "$attr=$::FINALATTRS{$objname}{$attr}"); + } + elsif ($::opt_m) + { + + # if value is a list then remove the specified attrs from it + # TBD + } + else + { + + # default behavior + # either set it - if blank + # or add it to attr list - if already set + if ($DBdefs{$objname}{$attr}) + { + + # add the new attr to the old attrs - comma seperated + my $val = + $DBdefs{$objname}{$attr} . "," + . $::FINALATTRS{$objname}{$attr}; + push(@attrval, $val); + } + else + { + + # - just set it + push(@attrval, "$attr=$::FINALATTRS{$objname}{$attr}"); + } + } + } + + # OK - change the tables in the xCAT database + # - write %NEWDEFS to the DB + print "set: $objname, @attrval\n"; + } + + return 0; +} + +#---------------------------------------------------------------------------- + +=head3 defls + + Support for the xCAT defls command. + + Arguments: + Returns: + 0 - OK + 1 - error + Globals: + + Error: + + Example: + + Comments: + Object names derived from -o, -t, w, -a or noderange! + List of attrs to display is given by -i. + Output goes to standard out or a stanza/xml file (-z or -x) + +=cut + +#----------------------------------------------------------------------------- + +sub defls +{ + my $long = 0; + my %DBhash; + + my @objectlist; + + # process the command line + my $rc = &processArgs; + if ($rc != 0) + { + + # rc: 0 - ok, 1 - help, 2 - error + &defls_usage; + return ($rc - 1); + } + + if ($::opt_l) + { + $long = 1; + } + + # get a list of all the object names to display -> $objnames + + # object names provided + # convert -o to list - @objectlist + # if a, t, w, or noderange then error - cannot be combined + # ? assume long?? $long = 1; + # use noderange as $objnames + # convert noderange to list - @objectlist + # if a, t, w, or o then error + # ? assume long?? $long = 1; + # if all - get list of all objects + # get list from DB - for each type check for names + # if o, t, w, or noderange then error + # assume want names only - unless opt_l + # if types - get all objs of that type + # get list of all object names of these types + # if a, o, w, or noderange then error + # assume want names only - unless opt_l + # use where values to gather names of objects + # - need to check all object names - could be time consuming! + # - make local hash - also use it below + # if a, t, o, or noderange then error + # assume want names only - unless opt_l + + # get complete object defs - if need attrs - use @objectlist + # if not already done then + # if ($::opt_i || $long = 1; || $::opt_w) + # foreach my $obj (@objnames) + # get object definition from DB + # put in hash - %DBhash{$objname}{$attr} + + # for each object + + # if (!($::opt_i)) - just display long or short info + #if ( $::opt_l) - display details of obj def + # else - just diplay names + + # else if (%DBhash{$objname}{$attr} =~ /$::opt_i/) + # if the attr is one of the ones I want then add it to the + # output hash + + # display and/or write to output file + + return 0; +} + +#---------------------------------------------------------------------------- + +=head3 defrm + + Support for the xCAT defrm command. + + Arguments: + Returns: + 0 - OK + 1 - error + Globals: + + Error: + + Example: + + Comments: + Object names to remove are derived from -o, -t, w, -a, -f, + or noderange! +=cut + +#----------------------------------------------------------------------------- + +sub defrm +{ + + # process the command line + my $rc = &processArgs; + if ($rc != 0) + { + + # rc: 0 - ok, 1 - help, 2 - error + &defrm_usage; + return ($rc - 1); + } + + # if list of objects provided in a file then add them + if (@::objfilelist) + { + push(@::allobjnames, @::objfilelist); + } + + # foreach objname + foreach my $objname (@::allobjnames) + { + + # remove object + #if ($::FINALATTRS{$objname}{objtype} eq "group") + # if (defined($::opt_d)) + # print "make dynamic groups\n"; + # next; + + # if type= site -> remove site table + # if type=network -> remove entry in network table + # if node -> remove node entries in all node tables + # if group + # - if dynamic -> remove entry from group table + # - if static -> remove group name entries from relevant tables + } + + # what happens to group if all nodes removed??? + # ? - if remove nodes then check for empty groups to clean up?? + # if (@::objtypes =~ /node/) + # - get all group member lists + # - if member list is empty + # - remove group - ie. remove group name entry in all DB tables?? + + return 0; +} + +#---------------------------------------------------------------------------- + +=head3 defmk_usage + + Arguments: + Returns: + Globals: + + Error: + + Example: + + Comments: +=cut + +#----------------------------------------------------------------------------- + +# subroutines to display the usage +sub defmk_usage +{ + my %rsp; + $rsp->{data}->[0] = + "\nUsage: defmk - create xCAT data object definitions.\n"; + $rsp->{data}->[1] = + " defmk [-h | --help ] [-V | --verbose] [-t ]"; + $rsp->{data}->[2] = + " [-o ] [-z ] [-x ]"; + $rsp->{data}->[3] = + " [-w attr=val,[attr=val...]][-d | --dynamic] "; + $rsp->{data}->[4] = " attr=val [attr=val...]\n"; + $::callback->($rsp); +} + +#---------------------------------------------------------------------------- + +=head3 defch_usage + + Arguments: + Returns: + Globals: + + Error: + + Example: + + Comments: +=cut + +#----------------------------------------------------------------------------- + +sub defch_usage +{ + my %rsp; + $rsp->{data}->[0] = + "\nUsage: defch - change xCAT data object definitions.\n"; + $rsp->{data}->[1] = + " defch [-h | --help ] [-V | --verbose] [-t ]"; + $rsp->{data}->[2] = + " [-o ] [-z ] [-x ]"; + $rsp->{data}->[3] = + " [-m | --minus] [-r | --replace] [-w attr=val,[attr=val...] ]"; + $rsp->{data}->[4] = " attr=val [attr=val...]\n"; + $::callback->($rsp); +} + +#---------------------------------------------------------------------------- + +=head3 defls_usage + + Arguments: + Returns: + Globals: + + Error: + + Example: + + Comments: +=cut + +#----------------------------------------------------------------------------- + +sub defls_usage +{ + my %rsp; + $rsp->{data}->[0] = "\nUsage: defls - list xCAT data object definitions.\n"; + $rsp->{data}->[1] = + " defls [-h | --help ] [-V | --verbose] [ -l | --long] [-a | --all]"; + $rsp->{data}->[2] = + " [-t ] [-o ] [-z ]"; + $rsp->{data}->[3] = + " [-x ] [-i attr-list] [-w attr=val,[attr=val...] ]"; + $rsp->{data}->[4] = " \n"; + $::callback->($rsp); +} + +#---------------------------------------------------------------------------- + +=head3 defrm_usage + + Arguments: + Returns: + Globals: + + Error: + + Example: + + Comments: +=cut + +#----------------------------------------------------------------------------- + +sub defrm_usage +{ + my %rsp; + $rsp->{data}->[0] = + "\nUsage: defrm - remove xCAT data object definitions.\n"; + $rsp->{data}->[1] = " defrm [-h | --help ] [-V | --verbose] [-a | --all]"; + $rsp->{data}->[2] = + " [-t ] [-o ] [-f ]"; + $rsp->{data}->[3] = " [-w attr=val,[attr=val...] \n"; + $::callback->($rsp); +} + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/bind.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/bind.pm new file mode 100755 index 000000000..ed14b83e0 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/bind.pm @@ -0,0 +1,1013 @@ +#!/usr/bin/perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#This is ported forward from xCAT 1.3 +#TODO: A lot of stuff was handled by the script portion of makedns, notably: +# db.cache +# forwarders +# chroot +# dnsallowq +# mucking with sysconfig +package xCAT_plugin::bind; +use Sys::Hostname; +use Cwd; +use xCAT::Table; +use Data::Dumper; + +use Sys::Syslog; +sub handled_commands { + return {"makedns" => "bind"}; +} + +#NAME +# +# h2n - Translate host table to name server file format +# $Date: 1999/08/08 17:17:56 $ $Revision: 8.2 $ +# +#SYNOPSIS +# +# h2n -d DOMAIN -n NET [options] + +# Various defaults +my $Host; +my $doaliases = 1; +my $domx = 1; +my $dowks = 0; +my $dotxt = 0; +my $dontdodomains = 0; +my $Bootfile = "/etc/named.conf"; +my $DBDir = "/var/named/"; +my $Domain = ""; +my $Hostfile = "/etc/hosts"; +my $Commentfile = ""; +my $Commentfileread = 0; +my $User = "root"; +my $RespHost = ""; +my $RespUser = ""; +my $DefSerial = 1; +my $DefRefresh = 10800; +my $DefRetry = 3600; +my $DefExpire = 604800; +my $DefTtl = 86400; +my $UseDefSOAValues = 0; +my $DefMxWeight = 10; +my $Defsubnetmask = ""; +my $ForceSerial = -1; +my $UseDateInSerial = 1; +my $DateSerial = 0; +my $Version = 8; +my $request; +my $callback; +my @forwarders; +sub process_request { + $request = shift; + $callback = shift; + $Host = hostname; + $Host =~ s/\..*//; + my $sitetab = xCAT::Table->new('site'); + unless ($sitetab) { + $callback->({error=>["No site table found"],errorcode=>[1]}); + return; + } + my @args = @{$request->{arg}}; + (my $fent) = $sitetab->getAttribs({key=>'forwarders'},'value'); + if ($fent and defined $fent->{value}) { + @forwarders = split /[,:;]/,$fent->{value}; + } + unless (grep /^-d$/,@args) { + (my $dent) = $sitetab->getAttribs({key=>'domain'},'value'); + if ($dent and $dent->{value}) { + push @args,"-d"; + $dent->{value} =~ s/\.$//; + push @args,$dent->{value}; + } + } + unless (grep /^-s$/,@args) { + push @args,"-s"; + push @args,$Host; + } + unless (grep /^-n$/,@args) { + my $nettab = xCAT::Table->new('networks'); + foreach (@{$nettab->getAllEntries()}) { + push @args,"-n"; + push @args,$_->{net}.":".$_->{mask} + } + } + + +push(@bootmsgs_v4, "primary\t0.0.127.IN-ADDR.ARPA db.127.0.0\n"); +push(@bootmsgs_v8, + qq|zone "0.0.127.IN-ADDR.ARPA" in {\n\ttype master;\n\tfile "db.127.0.0";\n\tnotify no;\n};\n\n|); + +&PARSEARGS(@args); +&FIXUP; + +open(HOSTS, $Hostfile) || die "can not open $Hostfile"; + +LINE: while(){ + next if /^[ \t]*#/; # skip comment lines + next if /^$/; # skip empty lines + chop; # remove the trailing newline + tr/A-Z/a-z/; # translate to lower case + + ($data,$comment) = split('#', $_, 2); + ($addr, $names) = split(' ', $data, 2); + if ($names =~ /^[ \t]*$/) { + $callback->({data=>["Bad line in hosts file ignored '$_'"]}); + next LINE; + } + + # Match -e args + foreach $netpat (@elimpats){ + next LINE if (/[.\s]$netpat/); + } + + # Process -c args + foreach $netpat (@cpats){ + if (/\.$netpat/) { + ($canonical, $aliases) = split(' ', $names, 2); + $canonical =~ s/\.$netpat//; + if($Cnames{$canonical} != 1){ + printf DOMAIN "%-20s IN CNAME %s.%s.\n", + $canonical, $canonical, $cpatrel{$netpat}; + $Cnames{$canonical} = 1; + } + next LINE; + } + } + + # Check that the address is in the address list. + $match = 'none'; + foreach $netpat (@Netpatterns){ + $match = $netpat, last if ($addr =~ /^$netpat\./); + } + next if ($match eq 'none'); + + ($canonical, $aliases) = split(' ', $names, 2); # separate out aliases + next if ($dontdodomains && $canonical =~ /\./); # skip domain names + $canonical =~ s/$Domainpattern//; # strip off domain if there is one + $Hosts{$canonical} .= $addr . " "; # index addresses by canonical name + $Aliases{$addr} .= $aliases . " "; # index aliases by address + $Comments{"$canonical-$addr"} = $comment; + + # Print PTR records + $file = $Netfiles{$match}; + printf $file "%-30s\tIN PTR %s.%s.\n", + &REVERSE($addr), $canonical, $Domain; +} + +# +# Go through the list of canonical names. +# If there is more than 1 address associated with the +# name, it is a multi-homed host. For each address +# look up the aliases since the aliases are associated +# with the address, not the canonical name. +# +foreach $canonical (keys %Hosts){ + @addrs = split(' ', $Hosts{$canonical}); + $numaddrs = $#addrs + 1; + foreach $addr (@addrs) { + # + # Print address record for canonical name. + # + if($Cnames{$canonical} != 1){ + printf DOMAIN "%-20s IN A %s\n", $canonical, $addr; + } else { + syslog("local1|err","$canonical - can't create A record because CNAME exists for name.\n"); + } + # + # Print cname or address records for each alias. + # If this is a multi-homed host, print an address + # record for each alias. If this is a single address + # host, print a cname record. + # + if ($doaliases) { + @aliases = split(' ', $Aliases{$addr}); + foreach $alias (@aliases){ + # + # Skip over the alias if the alias and canonical + # name only differ in that one of them has the + # domain appended to it. + # + next if ($dontdodomains && $alias =~ /\./); # skip domain names + $alias =~ s/$Domainpattern//; + if($alias eq $canonical){ + next; + } + + $aliasforallnames = 0; + if($numaddrs > 1){ + # + # If alias exists for *all* addresses of this host, we + # can use a CNAME instead of an address record. + # + $aliasforallnames = 1; + $xalias = $alias . " "; # every alias ends with blank + @xaddrs = split(' ', $Hosts{$canonical}); + foreach $xaddr (@xaddrs) { + if(!($Aliases{$xaddr} =~ /\b$xalias/)) { + $aliasforallnames = 0; + } + } + } + + if(($numaddrs > 1) && !$aliasforallnames){ + printf DOMAIN "%-20s IN A %s\n", $alias, $addr; + } else { + # + # Flag aliases that have already been used + # in CNAME records or have A records. + # + if(($Cnames{$alias} != 1) && (!$Hosts{$alias})){ + printf DOMAIN "%-20s IN CNAME %s.%s.\n", + $alias, $canonical, $Domain; + $Cnames{$alias} = 1; + } else { + syslog "local1|err","$alias - CNAME or A exists already; alias ignored\n"; + } + } + + if($aliasforallnames){ + # + # Since a CNAME record was created, remove this + # name from the alias list so we don't encounter + # it again for the next address of this host. + # + $xalias = $alias . " "; # every alias ends with blank + @xaddrs = split(' ', $Hosts{$canonical}); + foreach $xaddr (@xaddrs) { + $Aliases{$xaddr} =~ s/\b$xalias//; + } + } + } + } + } + if ($domx) { + &MX($canonical, @addrs); + } + if ($dotxt) { + &TXT($canonical, @addrs); + } + if ($Commentfile ne "") { + &DO_COMMENTS($canonical, @addrs); + } +} + +# Deal with spcl's +if (-r "spcl.$Domainfile") { + print DOMAIN "\$INCLUDE spcl.$Domainfile\n"; +} +foreach $n (@Networks) { + if (-r "spcl.$n") { + $file = "DB.$n"; + print $file "\$INCLUDE spcl.$n\n"; + } +} + +# generate boot.* files +&GEN_BOOT; + +} + +# +# Generate resource record data for +# strings from the commment field that +# are found in the comment file (-C). +# +sub DO_COMMENTS { + local($canonical, @addrs) = @_; + local(*F, @c, $c, $a, $comments); + + if (!$Commentfileread) { + open(F, $Commentfile) || die "Unable to open file $Commentfile: $!"; + $Commentfileread++; + while () { + chop; + ($key, $c) = split(':', $_, 2); + $CommentRRs{$key} = $c; + } + close(F); + } + + foreach $a (@addrs) { + $key = "$canonical-$a"; + $comments .= " $Comments{$key}"; + } + + @c = split(' ', $comments); + foreach $c (@c) { + if($CommentRRs{$c}){ + printf DOMAIN "%-20s %s\n", $canonical, $CommentRRs{$c}; + } + } +} + + +# +# Generate MX record data +# +sub MX { + local($canonical, @addrs) = @_; + local($first, $a, $key, $comments); + + if($Cnames{$canonical}){ + syslog "local1|err","$canonical - can't create MX record because CNAME exists for name.\n"; + return; + } + $first = 1; + + foreach $a (@addrs) { + $key = "$canonical-$a"; + $comments .= " $Comments{$key}"; + } + + if ($comments !~ /\[no smtp\]/) { + # Add WKS if requested + if ($dowks) { + foreach $a (@addrs) { + printf DOMAIN "%-20s IN WKS %s TCP SMTP\n", $canonical, $a; + } + } + printf DOMAIN "%-20s IN MX %s %s.%s.\n", $canonical, $DefMxWeight, + $canonical, $Domain; + $first = 0; + } + if ($#Mx >= 0) { + foreach $a (@Mx) { + if ($first) { + printf DOMAIN "%-20s IN MX %s\n", $canonical, $a; + $first = 0; + } else { + printf DOMAIN "%-20s IN MX %s\n", "", $a; + } + } + } + +} + + +# +# Generate TXT record data +# +sub TXT { + local($canonical, @addrs) = @_; + local($a, $key, $comments); + + foreach $a (@addrs) { + $key = "$canonical-$a"; + $comments .= " $Comments{$key}"; + } + $comments =~ s/\[no smtp\]//g; + $comments =~ s/^\s*//; + $comments =~ s/\s*$//; + + if ($comments ne "") { + printf DOMAIN "%s IN TXT \"%s\"\n", $canonical, $comments; + } +} + + +# +# Create the SOA record at the beginning of the file +# +sub MAKE_SOA { + local($fname, $file) = @_; + local($s); + + if ( -s $fname) { + open($file, "$fname") || die "Unable to open $fname: $!"; + $_ = <$file>; + chop; + if (/\($/) { + if (! $soa_warned) { + syslog "local1|err","Converting SOA format to new style.\n"; + $soa_warned++; + } + if ($ForceSerial > 0) { + $Serial = $ForceSerial; + } else { + ($Serial, $junk) = split(' ', <$file>, 2); + $Serial++; + if($UseDateInSerial && ($DateSerial > $Serial)){ + $Serial = $DateSerial; + } + } + ($Refresh, $junk) = split(' ', <$file>, 2); + ($Retry, $junk) = split(' ', <$file>, 2); + ($Expire, $junk) = split(' ', <$file>, 2); + ($Ttl, $junk) = split(' ', <$file>, 2); + } else { + if (/TTL/) { + $_ = <$file>; + } + split(' '); + if ($#_ == 11) { + if ($ForceSerial > 0) { + $Serial = $ForceSerial; + } else { + $Serial = ++@_[6]; + if($UseDateInSerial && ($DateSerial > $Serial)){ + $Serial = $DateSerial; + } + } + $Refresh = @_[7]; + $Retry = @_[8]; + $Expire = @_[9]; + $Ttl = @_[10]; + } else { + syslog "local1|err","Improper format SOA in $fname.\n"; + syslog "local1|err","I give up ... sorry.\n"; + exit(1); + } + } + if($UseDefSOAValues){ + $Refresh = $DefRefresh; + $Retry = $DefRetry; + $Expire = $DefExpire; + $Ttl = $DefTtl; + } + close($file); + } else { + if ($ForceSerial > 0) { + $Serial = $ForceSerial; + } else { + $Serial = $DefSerial; + if($UseDateInSerial && ($DateSerial > $Serial)){ + $Serial = $DateSerial; + } + } + $Refresh = $DefRefresh; + $Retry = $DefRetry; + $Expire = $DefExpire; + $Ttl = $DefTtl; + close($file); + } + + open($file, "> $fname") || die "Unable to open $fname: $!"; + + print $file '$TTL 86400'."\n"; + print $file "\@ IN SOA $RespHost $RespUser "; + print $file "( $Serial $Refresh $Retry $Expire $Ttl )\n"; + foreach $s (@Servers) { + print $file " IN NS $s\n"; + } + print $file "\n"; +} + + +# +# Reverse the octets of an IP address and append +# in-addr.arpa. +# +sub REVERSE { + join('.', reverse(split('\.', $_[0]))) . '.IN-ADDR.ARPA.'; +} + + +# +# Establish what we will be using for SOA records +# +sub FIXUP { + local($s); + + if ($Host =~ /\./) { + $RespHost = "$Host."; + } else { + $RespHost = "$Host.$Domain."; + } + $RespHost =~ s/\.\././g; + + if ($User =~ /@/) { # -u user@... + if ($User =~ /\./) { + $RespUser = "$User."; # -u user@terminator.movie.edu + } else { + $RespUser = "$User.$Domain."; # -u user@terminator + } + $RespUser =~ s/@/./; + } elsif ($User =~ /\./) { + $RespUser = "$User."; # -u user.terminator.movie.edu + } else { + $RespUser = "$User.$RespHost"; # -u user + } + $RespUser =~ s/\.\././g; # Strip any ".."'s to "." + + # Clean up nameservers + if (!defined(@Servers)) { + syslog "local1|err","No -s option specified. Assuming \"-s $Host.$Domain\"\n"; + push(@Servers, "$Host.$Domain."); + } else { + foreach $s (@Servers) { + if ($s !~ /\./) { + $s .= ".$Domain"; + } + if ($s !~ /\.$/) { + $s .= "."; + } + } + } + + # Clean up MX hosts + foreach $s (@Mx) { + $s =~ s/:/ /; + if ($s !~ /\./) { + $s .= ".$Domain"; + } + if ($s !~ /\.$/) { + $s .= "."; + } + } + + # Now open boot file and print saved data + open(BOOT, "> $Bootfile") || die "can not open $Bootfile"; + + # + # Write either the version 4 boot file directives or the + # version 8 boot file directives. + # + + if($Version == 4) { + print BOOT "\ndirectory $DBDir\n"; + foreach $line (@bootmsgs_v4) { + print BOOT $line; + } + print BOOT "cache\t. db.cache\n"; + if (-r "spcl.boot") { + print BOOT "include\tspcl.boot\n"; + } + } else { + print BOOT + qq|\noptions {\n\tdirectory "$DBDir";\n|; + if (-r "spcl.options") { + print BOOT "\t# These options came from the file spcl.options\n"; + # + # Copy the options in since "include" can't be used + # within a statement. + # + open(OPTIONS, ") + { + print BOOT; + } + close(OPTIONS); + } + print BOOT qq|};\n\n|; + foreach $line (@bootmsgs_v8) { + print BOOT $line; + } + #print BOOT qq|zone "." in {\n\ttype hint;\n\tfile "db.cache";\n};\n\n|; + if (-r "spcl.boot") { + print BOOT qq|include "spcl.boot";\n\n|; + } + } + + # Go ahead and start creating files and making SOA's + foreach $i (@makesoa) { + ($x1, $x2) = split(' ', $i); + &MAKE_SOA($x1, $x2); + } + printf DOMAIN "%-20s IN A 127.0.0.1\n", "localhost"; + + $file = "DB.127.0.0.1"; + &MAKE_SOA($DBDir."db.127.0.0", $file); + printf $file "%-30s\tIN PTR localhost.\n", &REVERSE("127.0.0.1"); + close($file); +} + + +sub PARSEARGS { + local(@args) = @_; + local($i, $net, $subnetmask, $option, $tmp1); + local(*F, $file, @newargs, @targs); + local($sec,$min,$hour,$mday,$mon,$year,$rest); + ($sec,$min,$hour,$mday,$mon,$year,$rest) = localtime(time); + $DateSerial = ($mday * 100) + + (($mon + 1) * 10000) + + (($year + 1900) * 1000000); + + $i = 0; + while ($i <= $#args){ + $option = $args[$i]; + if($option eq "-d"){ + if ($Domain ne "") { + syslog "local1|err","Only one -d option allowed.\n"; + syslog "local1|err","I give up ... sorry.\n"; + exit(1); + } + $Domain = $args[++$i]; + $Domainpattern = "." . $Domain; + $Domainpattern =~ s/\./\\./g; # for stripping off domain + + # Add entry to the boot file. + $Domainfile = $Domain; + $Domainfile =~ s/\..*//; + push(@makesoa, $DBDir."db.$Domainfile DOMAIN"); + push(@bootmsgs_v4, "primary\t$Domain db.$Domainfile\n"); + push(@bootmsgs_v8, + qq|zone "$Domain" in {\n\ttype master;\n\tfile "db.$Domainfile";\n};\n\n|); + + } elsif ($option eq "-f"){ + $file = $args[++$i]; + open(F, $file) || die "Unable to open args file $file: $!"; + while () { + next if (/^#/); + next if (/^$/); + chop; + @targs = split(' '); + push(@newargs, @targs); + } + close(F); + &PARSEARGS(@newargs); + + } elsif ($option eq "-z"){ + $Bootsecsaveaddr = $args[++$i]; + if (!defined($Bootsecaddr)) { + $Bootsecaddr = $Bootsecsaveaddr; + } + + } elsif ($option eq "-Z"){ + $Bootsecaddr = $args[++$i]; + if (!defined($Bootsecsaveaddr)) { + $Bootsecsaveaddr = $Bootsecaddr; + } + + } elsif ($option eq "-b"){ + $Bootfile = $args[++$i]; + + } elsif ($option eq "-A"){ + $doaliases = 0; + + } elsif ($option eq "-M"){ + $domx = 0; + + } elsif ($option eq "-w"){ + $dowks = 1; + + } elsif ($option eq "-D"){ + $dontdodomains = 1; + + } elsif ($option eq "-t"){ + $dotxt = 1; + + } elsif ($option eq "-u"){ + $User = $args[++$i]; + + } elsif ($option eq "-s"){ + while ($args[++$i] !~ /^-/ && $i <= $#args) { + push(@Servers, $args[$i]); + } + $i--; + + } elsif ($option eq "-m"){ + if ($args[++$i] !~ /:/) { + syslog "local1|err","Improper format for -m option ignored ($args[$i]).\n"; + } + push(@Mx, $args[$i]); + + } elsif ($option eq "-c"){ + $tmp1 = $args[++$i]; + if ($tmp1 !~ /\./) { + $tmp1 .= ".$Domain"; + } + if ($Domain eq $tmp1) { + syslog "local1|err","Domain for -c option must not match domain for -d option.\n"; + syslog "local1|err","I give up ... sorry.\n"; + exit(1); + } + $tmp2 = $tmp1; + $tmp2 =~ s/\./\\./g; + $cpatrel{$tmp2} = $tmp1; + push(@cpats, $tmp2); + + } elsif ($option eq "-e"){ + $tmp1 = $args[++$i]; + if ($tmp1 !~ /\./) { + $tmp1 .= ".$Domain"; + } + $tmp1 =~ s/\./\\./g; + push(@elimpats, $tmp1); + + } elsif ($option eq "-h"){ + $Host = $args[++$i]; + + } elsif ($option eq "-o"){ + if ( $args[++$i] !~ /^[:\d]*$/ + || split(':', $args[$i]) != 4) { + syslog "local1|err","Improper format for -o ($args[$i]).\n"; + syslog "local1|err","I give up ... sorry.\n"; + exit(1); + } + ($DefRefresh, $DefRetry, $DefExpire, $DefTtl) = split(':', $args[$i]); + $UseDefSOAValues = 1; + + } elsif ($option eq "-i"){ + $ForceSerial = $args[++$i]; + + } elsif ($option eq "-H"){ + $Hostfile = $args[++$i]; + if (! -r $Hostfile || -z $Hostfile) { + syslog "local1|err","Invalid file specified for -H ($Hostfile).\n"; + syslog "local1|err","I give up ... sorry.\n"; + exit(1); + } + + } elsif ($option eq "-C"){ + $Commentfile = $args[++$i]; + if (! -r $Commentfile || -z $Commentfile) { + syslog "local1|err","Invalid file specified for -C ($Commentfile).\n"; + syslog "local1|err","I give up ... sorry.\n"; + exit(1); + } + + } elsif ($option eq "-N"){ + $Defsubnetmask = $args[++$i]; + if ( $Defsubnetmask !~ /^[.\d]*$/ + || split('\.', $Defsubnetmask) != 4) { + syslog "local1|err","Improper subnet mask ($Defsubnetmask).\n"; + syslog "local1|err","I give up ... sorry.\n"; + exit(1); + } + if ($#Networks >= 0) { + syslog "local1|err","Hmm, -N option should probably be specified before any -n options.\n"; + } + + } elsif ($option eq "-n"){ + (my $tnet, $subnetmask) = split(':',$args[++$i]); + $net = ""; + my @netm = split(/\./,$subnetmask); + my @tnets = split(/\./,$tnet); + foreach (0..3) { + my $res = ($tnets[$_]+0) & ($netm[$_]+0); + if ($netm[$_]) { + $net.= $res.'.'; + } + } + $net =~ s/\.$//; + + if ($subnetmask eq "") { + foreach $tmp1 (&SUBNETS($net, $Defsubnetmask)) { + &BUILDNET($tmp1); + } + } else { + if ( $subnetmask !~ /^[.\d]*$/ + || split('\.', $subnetmask) != 4) { + syslog "local1|err","Improper subnet mask ($subnetmask).\n"; + syslog "local1|err","I give up ... sorry.\n"; + exit(1); + } + foreach $tmp1 (&SUBNETS($net, $subnetmask)) { + &BUILDNET($tmp1); + } + } + + } else { + if($option =~ /^-.*/){ + syslog "local1|err","Unknown option: $option ... ignored.\n"; + } + } + $i++; + } + + if (!defined(@Networks) || $Domain eq "") { + syslog "local1|err","Must specify one -d and at least one -n.\n"; + syslog "local1|err","I give up ... sorry.\n"; + exit(1); + } +} + + +sub BUILDNET { + local($net) = @_; + + push(@Networks, $net); + # + # Create pattern to match against. + # The dots must be changed to \. so they + # aren't used as wildcards. + # + $netpat = $net; + $netpat =~ s/\./\\./g; + push(@Netpatterns, $netpat); + + # + # Create db files for PTR records. + # Save the file names in an array for future use. + # + $netfile = "DB.$net"; + $Netfiles{$netpat} = $netfile; + push(@makesoa, $DBDir."db.$net $netfile"); + + # Add entry to the boot file. + $revaddr = &REVERSE($net); + chop($revaddr); # remove trailing dot + push(@bootmsgs_v4, "primary $revaddr db.$net\n"); + push(@bootmsgs_v8, + qq|zone "$revaddr" in {\n\ttype master;\n\tfile "db.$net";\n};\n\n|); +} + + +# +# Calculate all the subnets from a network number and mask. +# This was originally written for awk, not perl. +# +sub SUBNETS { + local($network, $mask) = @_; + local(@ans, @net, @mask, $buf, $number, $i, $j, $howmany); + + @net = split(/\./, $network); + @mask = split(/\./, $mask); + $number = ''; + # + # Only expand bytes 1, 2, or 3 + # for DNS purposes + # + for ($i = 0; $i < 4; $i++) { + if ($mask[$i] == 255) { + $number = $number . $net[$i] . '.'; + } elsif (($mask[$i] == 0) || $mask[$i] eq '') { + push(@ans, $network); + last; + } else { + # + # This should be done as a bit-wise or + # but awk does not have an or symbol + # + $howmany = 255 - $mask[$i]; + for ($j = 0; $j <= $howmany; $j++) { + if ($net[$i] + $j <= 255) { + $buf = sprintf("%s%d", $number, $net[$i] + $j); + push(@ans, $buf); + } + } + last; + } + } + + if ($#ans == -1) { + push(@ans, $network); + } + + @ans; +} + + +sub GEN_BOOT { + local(*F, $revaddr, $n); + + if (! -e "boot.cacheonly") { + # + # Create a boot file for a cache-only server + # + open(F, ">boot.cacheonly") || die "Unable to open boot.cacheonly: $!"; + if($Version == 4) { + print F "directory\t$DBDir\n"; + print F "primary\t\t0.0.127.IN-ADDR.ARPA db.127.0.0\n"; + print F "cache\t\t. db.cache\n"; + if (-r "spcl.cacheonly") { + printf F "include\t\tspcl.cacheonly\n"; + } + close(F); + } else { + print F qq|\noptions {\n\tdirectory "$DBDir";\n|; + if (@forwarders) { + print F qq|\tforwarders {\n|; + foreach (@forwarders) { + print F qq|\t\t$_;\n|; + } + print F qq|\t};\n|; + } + if (-r "spcl.options") { + print F "\t# These options came from the file spcl.options\n"; + # + # Copy the options in since "include" can't be used + # within a statement. + # + open(OPTIONS, ") + { + print F; + } + close(OPTIONS); + } + print F qq|};\n\n|; + print F qq|zone "0.0.127.IN-ADDR.ARPA" in {\n\ttype master;|; + print F qq|\n\tfile "db.127.0.0";|; + print F qq|\n\tnotify no;\n};\n\n|; + #print F qq|zone "." in {\n\ttype hint;\n\tfile "db.cache";\n};\n\n|; + if (-r "spcl.cacheonly") { + print F qq|include "spcl.cacheonly";\n\n|; + } + } + } + + # + # Create a 2 boot files for a secondary (slave) servers. + # One boot file doesn't save the zone data in a file. The + # other boot file does save the zone data in a file. + # + if (defined($Bootsecaddr)) { + open(F, ">boot.sec") || die "Unable to open boot.sec: $!"; + if($Version == 4) { + print F "directory\t$DBDir\n"; + print F "primary\t\t0.0.127.IN-ADDR.ARPA db.127.0.0\n"; + printf F "secondary\t%-23s $Bootsecaddr\n", $Domain; + foreach $n (@Networks) { + $revaddr = &REVERSE($n); + chop($revaddr); + printf F "secondary\t%-23s $Bootsecaddr\n", $revaddr; + } + print F "cache\t\t. db.cache\n"; + if (-r "spcl.boot") { + printf F "include\t\tspcl.boot\n"; + } + } else { + print F qq|\noptions {\n\tdirectory "$DBDir";\n|; + if (-r "spcl.options") { + print F "\t# These options came from the file spcl.options\n"; + # + # Copy the options in since "include" can't be used + # within a statement. + # + open(OPTIONS, ") + { + print F; + } + close(OPTIONS); + } + print F qq|};\n\n|; + print F qq|zone "0.0.127.IN-ADDR.ARPA" in {\n\ttype master;|; + print F qq|\n\tfile "db.127.0.0";|; + print F qq|\n\tnotify no;\n};\n\n|; + print F qq|zone "$Domain" in {\n\ttype slave;\n\tmasters {|; + print F qq| $Bootsecaddr; };\n};\n\n|; + + foreach $n (@Networks) { + $revaddr = &REVERSE($n); + chop($revaddr); + print F qq|zone "$revaddr" in {\n\ttype slave;\n\tmasters {|; + print F qq| $Bootsecaddr; };\n};\n\n|; + } + #print F qq|zone "." in {\n\ttype hint;\n\tfile "db.cache";\n};\n\n|; + if (-r "spcl.boot") { + print F qq|include "spcl.boot";\n\n|; + } + } + close(F); + + open(F, ">boot.sec.save") || die "Unable to open boot.sec.save: $!"; + if($Version == 4) { + print F "directory\t$DBDir\n"; + print F "primary\t\t0.0.127.IN-ADDR.ARPA db.127.0.0\n"; + printf F "secondary\t%-23s $Bootsecsaveaddr db.%s\n", + $Domain, $Domainfile; + foreach $n (@Networks) { + $revaddr = &REVERSE($n); + chop($revaddr); + printf F "secondary\t%-23s $Bootsecsaveaddr db.%s\n", + $revaddr, $n; + } + print F "cache\t\t. db.cache\n"; + if (-r "spcl.boot") { + printf F "include\t\tspcl.boot\n"; + } + } else { + print F + qq|\noptions {\n\tdirectory "$DBDir";\n|; + if (-r "spcl.options") { + print F "\t# These options came from the file spcl.options\n"; + # + # Copy the options in since "include" can't be used + # within a statement. + # + open(OPTIONS, ") + { + print F; + } + close(OPTIONS); + } + print F qq|};\n\n|; + print F qq|zone "0.0.127.IN-ADDR.ARPA" in {\n\ttype master;|; + print F qq|\n\tfile "db.127.0.0";|; + print F qq|\n\tnotify no;\n};\n\n|; + + print F qq|zone "$Domain" in {\n\ttype slave;\n\tfile "db.$Domainfile";|; + print F qq|\n\tmasters { $Bootsecsaveaddr; };\n};\n\n|; + + foreach $n (@Networks) { + $revaddr = &REVERSE($n); + chop($revaddr); + print F + qq|zone "$revaddr" in {\n\ttype slave;\n\tfile "db.$n";\n\tmasters {|; + print F qq| $Bootsecsaveaddr; };\n};\n\n|; + } + + #print F qq|zone "." in {\n\ttype hint;\n\tfile "db.cache";\n};\n\n|; + if (-r "spcl.boot") { + print F qq|include "spcl.boot";\n\n|; + } + } + close(F); + } +} + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/blade.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/blade.pm new file mode 100644 index 000000000..50d0d16f6 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/blade.pm @@ -0,0 +1,757 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::blade; +#use Net::SNMP qw(:snmp INTEGER); +use xCAT::Table; +use IO::Socket; +use SNMP; +use strict; + +use XML::Simple; +use Data::Dumper; +use POSIX "WNOHANG"; +use Storable qw(freeze thaw); +use IO::Select; +use IO::Handle; + + +sub handled_commands { + return { + findme => 'blade', + rpower => 'nodehm:power,mgt', + rvitals => 'nodehm:vitals,mgt', + rinv => 'nodehm:inv,mgt', + rbeacon => 'nodehm:beacon,mgt', + rbootseq => 'nodehm:bootseq,mgt', + reventlog => 'nodehm:eventlog,mgt', + }; +} +my %usage = ( + "rpower" => "Usage: rpower [on|off|reset|stat|boot]", + "rbeacon" => "Usage: rbeacon [on|off|stat]", + "rvitals" => "Usage: rvitals [all|temp|voltage|fanspeed|power|leds]", + "reventlog" => "Usage: reventlog [all|clear|]", + "rinv" => "Usage: rinv [all|model|serial|vpd|mprom|deviceid|uuid]", + "rbootseq" => "Usage: rbootseq [hd0|hd1|hd2|hd3|net|iscsi|usbflash|floppy|none],...", +); +my %macmap; #Store responses from rinv for discovery +my $mmprimoid = '1.3.6.1.4.1.2.3.51.2.22.5.1.1.4';#mmPrimary +my $beaconoid = '1.3.6.1.4.1.2.3.51.2.2.8.2.1.1.11'; #ledBladeIdentity +my $powerstatoid = '1.3.6.1.4.1.2.3.51.2.22.1.5.1.1.4';#bladePowerState +my $powerchangeoid = '1.3.6.1.4.1.2.3.51.2.22.1.6.1.1.7';#powerOnOffBlade +my $powerresetoid = '1.3.6.1.4.1.2.3.51.2.22.1.6.1.1.8';#restartBlade +my $mpresetoid = '1.3.6.1.4.1.2.3.51.2.22.1.6.1.1.9'; #restartBladeSMP +my $bladexistsoid = '1.3.6.1.4.1.2.3.51.2.22.1.5.1.1.3'; #bladeExists +my $bladeserialoid = '1.3.6.1.4.1.2.3.51.2.2.21.4.1.1.6'; #bladeHardwareVpdSerialNumber +my $blademtmoid = '1.3.6.1.4.1.2.3.51.2.2.21.4.1.1.7'; #bladeHardwareVpdMachineType +my $bladempveroid = '1.3.6.1.4.1.2.3.51.2.2.21.5.3.1.7'; #bladeSysMgmtProcVpdRevision +my $bladempaveroid = '1.3.6.1.4.1.2.3.51.2.2.21.3.1.1.4';#mmMainApplVpdRevisonNumber +my $bladempabuildidoid = '1.3.6.1.4.1.2.3.51.2.2.21.3.1.1.3';#mmMainApplVpdBuildId +my $bladempadateoid = '1.3.6.1.4.1.2.3.51.2.2.21.3.1.1.6';#mmMainApplVpdBuildDate +my $bladempbuildidoid = '1.3.6.1.4.1.2.3.51.2.2.21.5.3.1.6'; #bladeSysMgmtProcVpdBuildId +my $bladebiosveroid = '1.3.6.1.4.1.2.3.51.2.2.21.5.1.1.7'; #bladeBiosVpdRevision +my $bladebiosbuildidoid = '1.3.6.1.4.1.2.3.51.2.2.21.5.1.1.6'; #bladeBiosVpdBuildId +my $bladebiosdateoid = '1.3.6.1.4.1.2.3.51.2.2.21.5.1.1.8'; #bladeBiosVpdDate +my $bladediagveroid = '1.3.6.1.4.1.2.3.51.2.2.21.5.2.1.7'; #bladeDiagsVpdRevision +my $bladediagbuildidoid = '1.3.6.1.4.1.2.3.51.2.2.21.5.2.1.6'; #bladeDiagsVpdBuildId +my $bladediagdateoid = '1.3.6.1.4.1.2.3.51.2.2.21.5.2.1.8';#bladeDiagsVpdDate +my $eventlogoid = '1.3.6.1.4.1.2.3.51.2.3.4.2.1.2';#readEventLogString +my $clearlogoid = '.1.3.6.1.4.1.2.3.51.2.3.4.3';#clearEventLog +my @macoids = ( + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.2', #bladeMACAddress1Vpd + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.3', #bladeMACAddress2Vpd + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.4', #bladeMACAddress3Vpd + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.5', #bladeMACAddress4Vpd +); +my @dcmacoids = ( + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.10', #bladeDaughterCard1MACAddress1Vpd + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.11', #bladeDaughterCard1MACAddress2Vpd + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.12', #bladeDaughterCard1MACAddress3Vpd + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.13', #bladeDaughterCard1MACAddress4Vpd +); +my @hsdcmacoids = ( + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.100', #bladeHSDaughterCard1MACAddress1Vpd + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.101', #bladeHSDaughterCard1MACAddress2Vpd + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.102', #bladeHSDaughterCard1MACAddress3Vpd + '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.103', #bladeHSDaughterCard1MACAddress4Vpd +); +my @bootseqoids = ( + '1.3.6.1.4.1.2.3.51.2.22.1.3.1.7', #bootSequence1 + '1.3.6.1.4.1.2.3.51.2.22.1.3.1.8', #bootSequence2 + '1.3.6.1.4.1.2.3.51.2.22.1.3.1.9', #bootSequence3 + '1.3.6.1.4.1.2.3.51.2.22.1.3.1.10', #bootSequence4 + ); +my %bootdevices = ( + 0 => 'none', + 1 => 'floppy', + 2 => 'cdrom', + 3 => 'hd0', + 4 => 'hd1', + 5 => 'hd2', + 6 => 'hd3', + 7 => 'net', + 8 => 'iscsi', + 9 => 'iscsicrit', + 10 => 'hd4', + 11 => 'usbflash' +); +my %bootnumbers = ( + 'none' => 0, + 'f' => 1, + 'floppy' => 1, + 'c' => 2, + 'cd' => 2, + 'dvd' => 2, + 'cdrom' => 2, + 'dvdrom' => 2, + 'h' => 3, #in absence of an index, presuming hd0 intended + 'hd' => 3, + 'hardisk' => 3, + 'hd0' => 3, + 'harddisk0' => 3, + 'hd1' => 4, + 'harddisk1' => 4, + 'hd2' => 5, + 'harddisk2' => 5, + 'hd3' => 6, + 'harddisk3' => 6, + 'n' => 7, + 'network' => 7, + 'net' => 7, + 'iscsi' => 8, + 'iscsicrit' => 9, + 'hd4' => 10, + 'harddisk4' => 10, + 'usbflash' => 11, + 'flash' => 11, + 'usb' => 11 +); + +my $session; +my $slot; +my $didchassis = 0; +my @eventlog_array = (); +my $activemm; +my %mpahash; +my $mpa; +my $allinchassis=0; + +sub fillresps { + my $response = shift; + my $mac = $response->{node}->[0]->{data}->[0]->{contents}->[0]; + my $node = $response->{node}->[0]->{name}->[0]; + $macmap{$mac} = $node; + #$macmap{$response->{node}->[0]->{data}->{contents}->[0]}=$response->{node}->[0]->{name}; +} +sub isallchassis { + my $bladesinchassis = 0; + if ($allinchassis) { + return 1; + } + foreach (1..14) { + my $tmp = $session->get([$bladexistsoid.".$_"]); + if ($tmp eq 1) { $bladesinchassis++ } + } + my $count = keys %{$mpahash{$mpa}->{nodes}}; + if ($count >= $bladesinchassis) { $allinchassis++; return 1 }; #commands that affect entire are okayed, i.e eventlog clear + return 0; +} +sub resetmp { + my $data; + my $stat; + my $rc; + $data = $session->set($mpresetoid.".$slot", 1); + #if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + if ($data->{$mpresetoid.".$slot"} == 1) { + return (0, "mpreset"); + } else { + return (1,"error"); + } +} + +sub walkelog { + my $session = shift; + my $oid = shift; + unless ($oid =~ /^\./) { + $oid = '.'.$oid; + } + my $retmap = undef; + my $current = 1; + my @bindlist; + my $varbind; + do { + foreach ($current..$current+31) { #Attempt to retrive 32 ents at a time, seems to be working... + push @bindlist,[$oid,$_]; + } + $current+=32; + $varbind = new SNMP::VarList( + @bindlist + ); + $session->get($varbind); + foreach(@$varbind) { + unless (${_}->[2]) {last;} + if( ${_}->[2] =~ /NOSUCHINSTANCE/) {last;} + $retmap->{$_->[1]}=$_->[2]; + } + } while ($varbind->[31] and $varbind->[31]->[2] != 'NOSUCHINSTANCE' and ($current < 600)); + + return $retmap; + print "Count was $current\n"; + #print Dumper($varbind->[60]->[2]); + print "\n\n"; + return undef; + my $count=0; + while ($varbind->[0] =~ /^$oid\.?(.*)/) { + $count++; + if ($1) { + $retmap->{$1.".".$varbind->[1]}=$varbind->[2]; #If $1 is set, means key should + } else { + $retmap->{$varbind->[1]}=$varbind->[2]; #If $1 is set, means key should + } + $session->getnext($varbind); + } + return $retmap; +} + +sub eventlog { #Tried various optimizations, but MM seems not to do bulk-request + #TODO: retrieval of non blade events, what should be syntax? + #TODO: try retrieving 5 at a time, then 1 at a time when that stops working + my $cmd=shift; + my $data; + my @output; + my $oid = $eventlogoid; + if ($cmd eq 'all') { + $cmd=65535; #no MM has this many logs possible, should be a good number + } + if ($cmd =~ /^(\d+)$/) { + my $requestednumber=$1; + unless (@eventlog_array) { + #my $varbind=new SNMP::Varbind([$oid,0]); + #while ($data=$session->getnext($varbind)) { + # print Dumper($data); + # if ($session->{ErrorStr}) { printf $session->{ErrorStr}."\n"; } + # foreach (keys %$data) { + # $oid=$_; + # } + # unless (oid_base_match($eventlogoid,$oid)) { + # last; + # } + my $logents = walkelog($session,$oid); + foreach (sort {$a <=> $b} (keys %$logents)) { + push @eventlog_array,$logents->{$_}."\n"; + } + #push @eventlog_array,$data->{$oid}; #TODO: filter against slot number, check for $allchassis for non-blade + #} + } + my $numentries=0; + #my $allchassis = isallchassis; + foreach (@eventlog_array) { + m/Severity:(\S+)\s+Source:(\S+)\s+Name:\S*\s+Date:(\S+)\s+Time:(\S+)\s+Text:(.+)/; + my $sev=$1; + my $source=$2; + my $date=$3; + my $time=$4; + my $text=$5; + my $matchstring; + if ($slot > 0) { + $matchstring=sprintf("BLADE_%02d",$slot); + } else { + $matchstring="^(?!BLADE).*"; + } + if ($source =~ m/$matchstring$/i) { #MM guys changed their minds on capitalization + $numentries++; + unshift @output,"$sev:$date $time $text"; #unshift to get it in a sane order + } + if ($numentries >= $requestednumber) { + last; + } + } + return (0,@output); + } + my $data; + if ($cmd eq "clear") { + unless (isallchassis) { + return (1,"Cannot clear eventlogs except for entire chassis"); + } + if ($didchassis) { return 0, "eventlog cleared" } + my $varbind = new SNMP::Varbind([$clearlogoid,0,1,'INTEGER']); + $data = $session->set($varbind); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $didchassis=1; + if ($varbind->[2] eq 1) { + return 0, "eventlog cleared"; + } + } +} + + + +sub bootseq { + my @args=@_; + my $data; + my @order=(); + if ($args[0] eq "list" or $args[0] eq "stat") { + foreach my $oid (@bootseqoids) { + $data=$session->get([$oid,$slot]); + if ($session->{ErrorStr}) { return (1, $session->{ErrorStr}); } + push @order,$bootdevices{$data}; + } + return (0,join(',',@order)); + } else { + foreach (@args) { + my @neworder=(split /,/,$_); + push @order,@neworder; + } + my $number=@order; + if ($number > 4) { + return (1,"Only four boot sequence entries allowed"); + } + my $nonespecified=0; + foreach (@order) { + unless (defined($bootnumbers{$_})) { return (1,"Unsupported device $_"); } + unless ($bootnumbers{$_}) { $nonespecified = 1; } + if ($nonespecified and $bootnumbers{$_}) { return (1,"Error: cannot specify 'none' before a device"); } + } + unless ($bootnumbers{$order[0]}) { + return (1,"Error: cannot specify 'none' as first device"); + } + foreach (3,2,1,0) { + my $param = $bootnumbers{$order[$_]}; + unless ($param) { + $param = 0; + my $varbind = new SNMP::Varbind([$bootseqoids[$_],$slot,$param,'INTEGER']); + $data = $session->set($varbind); + #$session->set($bootseqoids[$_].".$slot",$param); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + } + } + foreach (0,1,2,3) { + my $param = $bootnumbers{$order[$_]}; + if ($param) { + my $varbind = new SNMP::Varbind([$bootseqoids[$_],$slot,$param,'INTEGER']); + $data = $session->set($varbind); + #$session->set($bootseqoids[$_].".$slot",$param); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + } + } + return bootseq('list'); + } +} + + +sub inv { + my @invitems; + my $data; + my @output; + foreach (@_) { + push @invitems,split( /,/,$_); + } + my $item; + while (my $item = shift @invitems) { + if ($item =~ /^all/) { + push @invitems,(qw(mtm serial mac firm)); + next; + } + if ($item =~ /^firm/) { + push @invitems,(qw(bios diag mprom mparom)); + next; + } + if ($item =~ /^bios/) { + my $biosver; + my $biosbuild; + my $biosdate; + $biosver=$session->get([$bladebiosveroid.".$slot"]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $biosbuild=$session->get([$bladebiosbuildidoid.".$slot"]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $biosdate=$session->get([$bladebiosdateoid.".$slot"]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + push @output,"BIOS: $biosver ($biosbuild $biosdate)"; + } + if ($item =~ /^diag/) { + my $diagver; + my $diagdate; + my $diagbuild; + $data=$session->get([$bladediagveroid,$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $diagver = $data; + $data=$session->get([$bladediagbuildidoid,$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $diagbuild = $data; + $data=$session->get([$bladediagdateoid,$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $diagdate = $data; + push @output,"Diagnostics: $diagver ($diagbuild $diagdate)"; + } + if ($item =~ /^[sm]prom/) { + my $spver; + my $spbuild; + $data=$session->get([$bladempveroid,$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $spver=$data; + $data=$session->get([$bladempbuildidoid,$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $spbuild=$data; + push @output,"BMC/Mgt processor: $spver ($spbuild)"; + } + if ($item =~ /^mparom/) { + my $mpabuild; + my $mpaver; + my $mpadate; + $data=$session->get([$bladempaveroid,$activemm]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $mpaver=$data; + $data=$session->get([$bladempabuildidoid,$activemm]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $mpabuild=$data; + $data=$session->get([$bladempadateoid,$activemm]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + $mpadate=$data; + push @output,"Management Module firmware: $mpaver ($mpabuild $mpadate)"; + } + if ($item =~ /^model/ or $item =~ /^mtm/) { + $data=$session->get([$blademtmoid,$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + push @output,"Machine Type/Model: ".$data; + } + if ($item =~ /^serial/) { + $data=$session->get([$bladeserialoid,$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + push @output,"Serial Number: ".$data; + } + + if ($item =~ /^mac/) { + foreach (0..3) { + $data=$session->get([$macoids[$_],$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + if ($data =~ /:/) { + push @output,"MAC Address ".($_+1).": ".$data; + } + } + foreach (0..3) { + my $oid=$hsdcmacoids[$_].".$slot"; + $data=$session->get([$hsdcmacoids[$_],$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + if ($data =~ /:/) { + push @output,"HS Daughter card MAC Address ".($_+1).": ".$data; + } + } + foreach (0..3) { + $data=$session->get([$dcmacoids[$_],$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + if ($data =~ /:/) { + push @output,"Daughter card 1 MAC Address ".($_+1).": ".$data; + } + } + } + } + return (0,@output); +} +sub power { + my $subcommand = shift; + my $data; + my $stat; + if ($subcommand eq "stat") { + $data = $session->get([$powerstatoid.".".$slot]); + if ($data == 1) { + $stat = "on"; + } elsif ( $data == 0) { + $stat = "off"; + } else { + $stat= "error"; + } + } elsif ($subcommand eq "off") { + $data = $session->set(new SNMP::Varbind([".".$powerchangeoid,$slot,0,'INTEGER'])); + unless ($data) { return (1,$session->{ErrorStr}); } + $stat = "off"; + } elsif ($subcommand eq "on") { + $data = $session->set(new SNMP::Varbind([".".$powerchangeoid,$slot,1,'INTEGER'])); + unless ($data) { return (1,$session->{ErrorStr}); } + $stat = ($data ? "on" : "off"); + } elsif ($subcommand eq "reset") { + $data = $session->set(new SNMP::Varbind([".".$powerresetoid,$slot ,1,'INTEGER'])); + unless ($data) { return (1,$session->{ErrorStr}); } + $stat = "reset"; + } + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + if ($stat) { return (0,$stat); } +} + + +sub beacon { + my $subcommand = shift; + my $data; + if ($subcommand eq "stat") { + } elsif ($subcommand eq "on") { + $data = $session->set(new SNMP::Varbind([$beaconoid,$slot , 1,'INTEGER'])); + } elsif ($subcommand eq "off") { + $data = $session->set(new SNMP::Varbind([$beaconoid,$slot , 0,'INTEGER'])); + } elsif ($subcommand eq "blink") { + $data = $session->set(new SNMP::Varbind([$beaconoid,$slot , 2,'INTEGER'])); + } else { + return (1,"$subcommand unsupported"); + } + my $stat = $session->get([$beaconoid.".".$slot]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + if ($stat==0) { + return (0,"off"); + } elsif ($stat==1) { + return (0,"on"); + } elsif ($stat==2) { + return (0,"blink"); + } elsif ($stat==3) { + return (0,"unsupported"); + } +} + +sub bladecmd { + $mpa = shift; + $slot = shift; + #my $user = shift; + #my $pass = shift; + my $command = shift; + my @args = @_; + my $error; + + + if ($slot > 0) { + my $tmp = $session->get([$bladexistsoid.".$slot"]); + if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } + unless ($tmp eq 1) { return (1,"Target bay empty"); } + } + if ($command eq "rbeacon") { + return beacon(@args); + } elsif ($command eq "rpower") { + return power(@args); + } elsif ($command =~ /r[ms]preset/) { + return resetmp(@args); + } elsif ($command eq "rbootseq") { + return bootseq(@args); + } elsif ($command eq "rinv") { + return inv(@args); + } elsif ($command eq "reventlog") { + return eventlog(@args); + } + + return (1,"$command not a supported command by blade method"); +} + + +sub process_request { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my $noderange = $request->{node}; + my $command = $request->{command}->[0]; + my @exargs; + unless ($noderange or $command eq "findme") { + if ($usage{$command}) { + $callback->({data=>$usage{$command}}); + $request = {}; + } + return; + } + if (ref($request->{arg})) { + @exargs = @{$request->{arg}}; + } else { + @exargs = ($request->{arg}); + } + my $bladeuser = 'USERID'; + my $bladepass = 'PASSW0RD'; + my $blademaxp = 64; + my $sitetab = xCAT::Table->new('site'); + my $mpatab = xCAT::Table->new('mpa'); + my $mptab = xCAT::Table->new('mp'); + my $tmp; + if ($sitetab) { + ($tmp)=$sitetab->getAttribs({'key'=>'blademaxp'},'value'); + if (defined($tmp)) { $blademaxp=$tmp->{value}; } + } + my $passtab = xCAT::Table->new('passwd'); + if ($passtab) { + ($tmp)=$passtab->getAttribs({'key'=>'blade'},'username','password'); + if (defined($tmp)) { + $bladeuser = $tmp->{username}; + $bladepass = $tmp->{password}; + } + } + if ($request->{command}->[0] eq "findme") { + my $mptab = xCAT::Table->new("mp"); + unless ($mptab) { return 2; } + my @bladents = $mptab->getAllNodeAttribs([qw(node)]); + my @blades; + foreach (@bladents) { + push @blades,$_->{node}; + } + my %invreq; + $invreq{node} = \@blades; + $invreq{arg} = ['mac']; + $invreq{command} = ['rinv']; + my $mac; + my $ip = $request->{'!xcat_clientip'}; + my $arptable = `/sbin/arp -n`; + my @arpents = split /\n/,$arptable; + foreach (@arpents) { + if (m/^($ip)\s+\S+\s+(\S+)\s/) { + $mac=$2; + last; + } + } + unless ($mac) { return }; + + unless ($macmap{$mac}) { + process_request(\%invreq,\&fillresps); + } + unless ($macmap{$mac}) { + return 1; #failure + } + my $mactab = xCAT::Table->new('mac',-create=>1); + $mactab->setNodeAttribs($macmap{$mac},{mac=>$mac}); + $mactab->close(); + my %request = ( + command => ['makedhcp'], + node => [$macmap{$mac}] + ); + $doreq->(\%request); + $request->{command}=['discovered']; + $request->{noderange} = [$macmap{$mac}]; + $doreq->($request); + %{$request}=(); #Clear request. it is done + undef $mactab; + return 0; + } + + + my $children = 0; + $SIG{CHLD} = sub { while (waitpid(-1, WNOHANG) > 0) { $children--; } }; + my $inputs = new IO::Select;; + foreach (@$noderange) { + my $node=$_; + my $user=$bladeuser; + my $pass=$bladepass; + my $nodeid; + my $mpa; + my $ent; + if (defined($mptab)) { + $ent=$mptab->getNodeAttribs($node,['mpa','id']); + if (defined($ent->{mpa})) { $mpa=$ent->{mpa}; } + if (defined($ent->{id})) { $nodeid = $ent->{id}; } + } + if (defined($mpatab)) { + ($ent)=$mpatab->getAttribs({'mpa'=>$mpa},'username','password'); + if (defined($ent->{password})) { $pass = $ent->{password}; } + if (defined($ent->{username})) { $user = $ent->{username}; } + } + $mpahash{$mpa}->{nodes}->{$node}=$nodeid; + $mpahash{$mpa}->{username} = $user; + $mpahash{$mpa}->{password} = $pass; + } + my $sub_fds = new IO::Select; + foreach $mpa (sort (keys %mpahash)) { + while ($children > $blademaxp) { sleep (0.1); } + $children++; + my $cfd; + my $pfd; + pipe $cfd, $pfd; + $cfd->autoflush(1); + $pfd->autoflush(1); + my $cpid = fork; + unless (defined($cpid)) { die "Fork error"; } + unless ($cpid) { + close($cfd); + dompa($pfd,$mpa,\%mpahash,$command,-args=>@exargs); + exit(0); + } + close ($pfd); + $sub_fds->add($cfd); + } + while ($children > 0) { + forward_data($callback,$sub_fds); + } + while (forward_data($callback,$sub_fds)) {} +} + +sub forward_data { + my $callback = shift; + my $fds = shift; + my @ready_fds = $fds->can_read(1); + my $rfh; + my $rc = @ready_fds; + foreach $rfh (@ready_fds) { + my $data; + if ($data = <$rfh>) { + while ($data !~ /ENDOFFREEZE6sK4ci/) { + $data .= <$rfh>; + } + my $responses=thaw($data); + foreach (@$responses) { + $callback->($_); + } + } else { + $fds->remove($rfh); + close($rfh); + } + } + return $rc; +} + + +sub dompa { + my $out = shift; + $mpa = shift; + my $mpahash = shift; + my $command=shift; + my %namedargs=@_; + my @exargs=$namedargs{-args}; + my $node; + $session = new SNMP::Session( + DestHost => $mpa, + Version => '3', + SecName => $mpahash->{$mpa}->{username}, + AuthProto => 'SHA', + AuthPass => $mpahash->{$mpa}->{password}, + PrivProto => 'DES', + SecLevel => 'authPriv', + UseNumeric => 1, + Retries => 2, # Give up sooner to make commands go smoother + Timeout=>1200000, #Beacon, for one, takes a bit over a second to return + PrivPass => $mpahash->{$mpa}->{password}); + if ($session->{ErrorStr}) { return 1,$session->{ErrorStr}; } + my $tmp = $session->get([$mmprimoid.".1"]); + if ($session->{ErrorStr}) { print $session->{ErrorStr}; } + $activemm = ($tmp ? 1 : 2); + my @outhashes; + if ($command eq "reventlog" and isallchassis) { +#Add a dummy node for eventlog to get non-blade events + $mpahash{$mpa}->{nodes}->{$mpa}=-1; + } + foreach $node (sort (keys %{$mpahash->{$mpa}->{nodes}})) { + my ($rc,@output) = bladecmd($mpa,$mpahash->{$mpa}->{nodes}->{$node},$command,@exargs); + my @output_hashes; + foreach(@output) { + my %output; + (my $desc,my $text) = split (/:/,$_,2); + unless ($text) { + $text=$desc; + } else { + $desc =~ s/^\s+//; + $desc =~ s/\s+$//; + if ($desc) { + $output{node}->[0]->{data}->[0]->{desc}->[0]=$desc; + } + } + $text =~ s/^\s+//; + $text =~ s/\s+$//; + $output{node}->[0]->{name}->[0]=$node; + $output{node}->[0]->{data}->[0]->{contents}->[0]=$text; + print $out freeze([\%output]); + print $out "\nENDOFFREEZE6sK4ci\n"; + } + } + #my $msgtoparent=freeze(\@outhashes); # = XMLout(\%output,RootName => 'xcatresponse'); + #print $out $msgtoparent; #$node.": $_\n"; +} + +1; + diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/bmcconfig.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/bmcconfig.pm new file mode 100644 index 000000000..43dc33155 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/bmcconfig.pm @@ -0,0 +1,102 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::bmcconfig; +use Data::Dumper; +use xCAT::Table; + +sub handled_commands { + return { + getbmcconfig => 'bmcconfig', + }; +} + +sub genpassword { + my $length = shift; + my $password=''; + my $characters= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890'; + srand; #have to reseed, rand is not rand otherwise + while (length($password) < $length) { + $password .= substr($characters,int(rand 63),1); + } + return $password; +} + +sub net_parms { + my $ip = shift; + if ($ip =~ /[A-Za-z]/) { + my $addr = (gethostbyname($ip))[4]; + my @bytes = unpack("C4",$addr); + $ip = join(".",@bytes); + } + my $nettab = xCAT::Table->new('networks'); + unless ($nettab) { return undef }; + my @nets = $nettab->getAllAttribs('net','mask','gateway'); + foreach (@nets) { + my $net = $_->{'net'}; + my $mask =$_->{'mask'}; + my $gw = $_->{'gateway'}; + $ip =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/; + my $ipnum = ($1<<24)+($2<<16)+($3<<8)+$4; + $mask =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/; + my $masknum = ($1<<24)+($2<<16)+($3<<8)+$4; + $net =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/; + my $netnum = ($1<<24)+($2<<16)+($3<<8)+$4; + if (($ipnum & $masknum)==$netnum) { + return ($ip,$mask,$gw); + } + } +} + + + +sub process_request { + my $request = shift; + my $callback = shift; + my $node = $request->{'!xcat_clienthost'}->[0]; + my $sitetable = xCAT::Table->new('site'); + my $ipmitable = xCAT::Table->new('ipmi'); + my $passtable = xCAT::Table->new('passwd'); + my $tmphash; + my $username = 'USERID'; + my $gennedpassword=0; + my $bmc; + my $password = 'PASSW0RD'; + if ($passtable) { $tmphash=$passtable->getAttribs({key=>'ipmi'},'username','password'); } + #Check for generics, can grab for both user and pass with a query + #since they cannot be in disparate records in passwd tab + if ($tmphash->{username}) { + $username=$tmphash->{username}; + } + if ($tmphash->{password}) { #It came for free with the last query + $password=$tmphash->{password}; + } + $tmphash=($sitetable->getAttribs({key=>'genpasswords'},'value'))[0]; + if ($tmphash->{value} eq "1" or $tmphash->{value} =~ /y(es)?/i) { + $password = genpassword(8); + $gennedpassword=1; + } else { + $tmphash=$ipmitable->getNodeAttribs($node,['password']); + if ($tmphash->{password}) { + $password = $tmphash->{password}; + } + } + $tmphash=$ipmitable->getNodeAttribs($node,['bmc','username']); + if ($tmphash->{bmc} ) { + $bmc=$tmphash->{bmc}; + } + if ($tmphash->{username}) { + $username = $tmphash->{username}; + } + (my $ip,my $mask,my $gw) = net_parms($bmc); + my $response={bmcip=>$ip,netmask=>$mask,gateway=>$gw,username=>$username,password=>$password}; + $callback->($response); + if ($gennedpassword) { # save generated password + $ipmitable->setNodeAttribs($node,{password=>$password}); + } + + return 1; +} + + + +1; + diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/centos.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/centos.pm new file mode 100644 index 000000000..eab1d3389 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/centos.pm @@ -0,0 +1,207 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::centos; +use Storable qw(dclone); +use Sys::Syslog; +use xCAT::Table; +use xCAT::Template; +use xCAT::Postage; +use Data::Dumper; +use Getopt::Long; +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); +use File::Path; +use File::Copy; + +my %distnames = ( + "1176234647.982657" => "centos5", + "1156364963.862322" => "centos4.4", + "1178480581.024704" => "centos4.5" + ); +my %numdiscs = ( + "1156364963.862322" => 4, + "1178480581.024704" => 3 + ); + +sub handled_commands { + return { + copycd => "centos", + mkinstall => "nodetype:os=centos.*" + } +} + +sub process_request { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my $distname = undef; + my $arch = undef; + my $path = undef; + if ($request->{command}->[0] eq 'copycd') { + return copycd($request,$callback,$doreq); + } elsif ($request->{command}->[0] eq 'mkinstall') { + return mkinstall($request,$callback,$doreq); + } +} + +sub mkinstall { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my @nodes = @{$request->{node}}; + my $node; + my $ostab = xCAT::Table->new('nodetype'); + my %doneimgs; + foreach $node (@nodes) { + my $osinst; + my $ent = $ostab->getNodeAttribs($node,['profile','os','arch']); + unless ($ent->{os} and $ent->{arch} and $ent->{profile}) { + $callback->({error=>["No profile defined in nodetype for $node"],errorcode=>[1]}); + next; #No profile + } + my $os = $ent->{os}; + my $arch = $ent->{arch}; + my $profile = $ent->{profile}; + unless (-r "/usr/share/xcat/install/centos/".$ent->{profile}.".tmpl") { + $callback->({error=>["No kickstart template exists for ".$ent->{profile}],errorcode=>[1]}); + next; + } + #Call the Template class to do substitution to produce a kickstart file in the autoinst dir + xCAT::Template->subvars("/usr/share/xcat/install/centos/".$ent->{profile}.".tmpl","/install/autoinst/".$node,$node); + mkpath "/install/postscripts/"; + xCAT::Postage->writescript($node,"/install/postscripts/".$node); + if (-r "/install/$os/$arch/images/pxeboot/vmlinuz" + and -r "/install/$os/$arch/images/pxeboot/initrd.img") { + #TODO: driver slipstream, targetted for network. + unless ($doneimgs{"$os|$arch"}) { + mkpath("/tftpboot/xcat/$os/$arch"); + copy("/install/$os/$arch/images/pxeboot/vmlinuz","/tftpboot/xcat/$os/$arch/"); + copy("/install/$os/$arch/images/pxeboot/initrd.img","/tftpboot/xcat/$os/$arch/"); + $doneimgs{"$os|$arch"}=1; + } + #We have a shot... + my $restab = xCAT::Table->new('noderes'); + my $hmtab = xCAT::Table->new('nodehm'); + my $ent = $restab->getNodeAttribs($node,['nfsserver','serialport','primarynic','installnic']); + my $sent = $hmtab->getNodeAttribs($node,['serialspeed','serialflow']); + unless ($ent and $ent->{nfsserver}) { + $callback->({error=>["No noderes.nfsserver defined for ".$ent->{profile}],errorcode=>[1]}); + next; + } + my $kcmdline="nofb utf8 ks=http://".$ent->{nfsserver}."/install/autoinst/".$node; + if ($ent->{installnic}) { + $kcmdline.=" ksdevice=".$ent->{installnic}; + } elsif ($ent->{primarynic}) { + $kcmdline.=" ksdevice=".$ent->{primarynic}; + } else { + $kcmdline .= " ksdevice=eth0"; + } + + #TODO: dd= for driver disks + if (defined($ent->{serialport})) { + unless ($sent->{serialspeed}) { + $callback->({error=>["serialport defined, but no serialspeed for $node in nodehm table"],errorcode=>[1]}); + next; + } + $kcmdline.=" console=ttyS".$ent->{serialport}.",".$sent->{serialspeed}; + if ($sent->{serialflow} =~ /(hard|cts|ctsrts)/) { + $kcmdline .= "n8r"; + } + } + $kcmdline .= " noipv6"; + + $restab->setNodeAttribs($node,{ + kernel=>"xcat/$os/$arch/vmlinuz", + initrd=>"xcat/$os/$arch/initrd.img", + kcmdline=>$kcmdline + }); + } else { + $callback->({error=>["Unable to find kernel and initrd for $os and $arch in install source /install/$os/$arch"],errorcode=>[1]}); + } + } +} + +sub copycd { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my $installroot; + my $sitetab = xCAT::Table->new('site'); + if ($sitetab) { + (my $ref) = $sitetab->getAttribs({key=>installdir},value); + print Dumper($ref); + if ($ref and $ref->{value}) { + $installroot = $ref->{value}; + } + } + + @ARGV= @{$request->{arg}}; + GetOptions( + 'n=s' => \$distname, + 'a=s' => \$arch, + 'p=s' => \$path + ); + unless ($path) { + #this plugin needs $path... + return; + } + if ($distname and $distname !~ /^centos/) { + #If they say to call it something other than CentOS, give up? + return; + } + unless (-r $path."/.discinfo") { + return; + } + my $dinfo; + open($dinfo,$path."/.discinfo"); + my $did = <$dinfo>; + chomp($did); + my $desc = <$dinfo>; + chomp($desc); + my $darch = <$dinfo>; + chomp($darch); + if ($darch and $darch =~ /i.86/) { + $darch = "x86"; + } + close($dinfo); + if ($distnames{$did}) { + unless ($distname) { + $distname = $distnames{$did}; + } + } elsif ($desc =~ /^Final$/) { + unless ($distname) { + $distname = "centos5"; + } + } elsif ($desc =~ /^CentOS-4 .*/) { + unless ($distname) { + $distname = "centos4"; + } + } + + unless ($distname) { + return; #Do nothing, not ours.. + } + if ($darch) { + unless ($arch) { + $arch = $darch; + } + if ($arch and $arch ne $darch) { + $callback->({error=>"Requested CentOS architecture $arch, but media is $darch"}); + return; + } + } + %{$request} = (); #clear request we've got it. + + $callback->({data=>"Copying media to $installroot/$distname/$arch/"}); + my $omask=umask 0022; + mkpath("$installroot/$distname/$arch"); + umask $omask; + my $rc = system("cd $path; find . | nice -n 20 cpio -dump $installroot/$distname/$arch"); + chmod 0755,"$installroot/$distname/$arch"; + if ($rc != 0) { + $callback->({error=>"Media copy operation failed, status $rc"}); + } else { + $callback->({data=>"Media copy operation successful"}); + } +} + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/conserver.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/conserver.pm new file mode 100644 index 000000000..82443b6f2 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/conserver.pm @@ -0,0 +1,206 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#TODO: delete entries not being refreshed if no noderange +package xCAT_plugin::conserver; +use xCAT::Table; +use strict; +use Data::Dumper; +my @cservers = qw(mrv cyclades); +my %termservers; #list of noted termservers + +sub handled_commands { + return { + makeconservercf => "conserver" + } +} + +sub process_request { + my $req = shift; + my $cb = shift; + if ($req->{command}->[0] eq "makeconservercf") { + makeconservercf($req,$cb); + } +} + +sub docfheaders { +# Put in standard headers common to all conserver.cf files + my $content = shift; + my $numlines = @$content; + my @meat = grep(!/^#/,@$content); + unless (grep(/^default full/,@meat)) { + push @$content,"default full { rw *; }\n"; + } + unless (grep(/^default cyclades/,@meat)) { + push @$content,"default cyclades { type host; portbase 7000; portinc 1; }\n" + } + unless (grep(/^default mrv/,@meat)) { + push @$content,"default mrv { type host; portbase 2000; portinc 100; }\n" + } + unless (grep(/^access \*/,@meat)) { +#TODO: something intelligent, allowing other hosts in + #push @$content,"#xCAT BEGIN ACCESS\n"; + push @$content,"access * {\n"; + push @$content," trusted 127.0.0.1;\n"; + push @$content,"}\n"; + #push @$content,"#xCAT END ACCESS\n"; + } + unless (grep(/^default \*/,@meat)) { + push @$content,"default * {\n"; + push @$content," logfile /var/log/consoles/&;\n"; + push @$content," timestamp 1hab;\n"; + push @$content," include full;\n"; + push @$content," master localhost;\n"; + push @$content,"}\n"; + } + + +} +sub makeconservercf { + my $req = shift; + %termservers = (); #clear hash of existing entries + my $cb = shift; + my $nodes = $req->{node}; + my $cfile; + my @filecontent; + open $cfile,'/etc/conserver.cf'; + while (<$cfile>) { + push @filecontent,$_; + } + close $cfile; + docfheaders(\@filecontent); + my $hmtab = xCAT::Table->new('nodehm'); + my @cfgents = $hmtab->getAllNodeAttribs(['mgt','cons']); +#cfgents should now have all the nodes, so we can fill in our hashes one at a time. + foreach (@cfgents) { + unless ($_->{cons}) {$_->{cons} = $_->{mgt};} #populate with fallback + my $cmeth=$_->{cons}; + if (grep(/^$cmeth$/,@cservers)) { #terminal server, more attribs needed + my $node = $_->{node}; + my $tent = $hmtab->getNodeAttribs($node,["termserver","termport"]); + $_->{termserver} = $tent->{termserver}; + $termservers{$tent->{termserver}} = 1; + $_->{termport}= $tent->{termport}; + } + } + if (($nodes and @$nodes > 0) or $req->{noderange}->[0]) { + foreach (@$nodes) { + my $node = $_; + foreach (@cfgents) { + if ($_->{node} eq $node) { + if ($_->{termserver} and $termservers{$_->{termserver}}) { + dotsent($_,\@filecontent); + delete $termservers{$_->{termserver}}; #prevent needless cycles being burned + } + donodeent($_,\@filecontent); + } + } + } + } else { #no nodes specified, do em all up + zapcfg(\@filecontent); # strip all xCAT configured stuff from config + foreach (@cfgents) { + if ($_->{termserver} and $termservers{$_->{termserver}}) { + dotsent($_,\@filecontent); + delete $termservers{$_->{termserver}}; #prevent needless cycles being burned + } + donodeent($_,\@filecontent); + } + } + open $cfile,'>','/etc/conserver.cf'; + foreach (@filecontent) { + print $cfile $_; + } + close $cfile; +} + +sub dotsent { + my $cfgent = shift; + my $tserv = $cfgent->{termserver}; + my $content = shift; + my $idx = 0; + my $toidx = -1; + my $skip = 0; + my $skipnext = 0; + while ($idx < $#$content) { # Go through and delete that which would match my entry + if ($content->[$idx] =~ /^#xCAT BEGIN $tserv TS/) { + $toidx=$idx; #TODO put it back right where I found it + $skip = 1; + $skipnext=1; + } elsif ($content->[$idx] =~ /^#xCAT END $tserv TS/) { + $skipnext = 0; + } + if ($skip) { + splice (@$content,$idx,1); + } else { + $idx++; + } + $skip = $skipnext; + } + push @$content,"#xCAT BEGIN $tserv TS\n"; + push @$content,"default $tserv {\n"; + push @$content," include ".$cfgent->{cons}.";\n"; + push @$content," host $tserv;\n"; + push @$content,"}\n"; + push @$content,"#xCAT END $tserv TS\n"; +} +sub donodeent { + my $cfgent = shift; + my $node = $cfgent->{node}; + my $content = shift; + my $idx=0; + my $toidx=-1; + my $skip = 0; + my $skipnext = 0; + while ($idx < $#$content) { # Go through and delete that which would match my entry + if ($content->[$idx] =~ /^#xCAT BEGIN $node CONS/) { + $toidx=$idx; #TODO put it back right where I found it + $skip = 1; + $skipnext=1; + } elsif ($content->[$idx] =~ /^#xCAT END $node CONS/) { + $skipnext = 0; + } + if ($skip) { + splice (@$content,$idx,1); + } else { + $idx++; + } + $skip = $skipnext; + } + push @$content,"#xCAT BEGIN $node CONS\n"; + push @$content,"console $node {\n"; + #if ($cfgent->{cons} + my $cmeth=$cfgent->{cons}; + print $cmeth."\n"; + if (grep(/^$cmeth$/,@cservers)) { + push @$content," include ".$cfgent->{termserver}.";\n"; + push @$content," port ".$cfgent->{termport}.";\n"; + } else { #a script method... + push @$content," type exec;\n"; + push @$content," exec /usr/share/xcat/cons/".$cmeth." ".$node.";\n" + } + push @$content,"}\n"; + push @$content,"#xCAT END $node CONS\n"; +} + +sub zapcfg { + my $content = shift; + my $idx=0; + my $toidx=-1; + my $skip = 0; + my $skipnext = 0; + while ($idx <= $#$content) { # Go through and delete that which would match my entry + if ($content->[$idx] =~ /^#xCAT BEGIN/) { + $toidx=$idx; #TODO put it back right where I found it + $skip = 1; + $skipnext=1; + } elsif ($content->[$idx] =~ /^#xCAT END/) { + $skipnext = 0; + } + if ($skip) { + splice (@$content,$idx,1); + } else { + $idx++; + } + $skip = $skipnext; + } +} + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/copycds.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/copycds.pm new file mode 100644 index 000000000..dd3c1acdb --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/copycds.pm @@ -0,0 +1,70 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::copycds; +use Storable qw(dclone); +use xCAT::Table; +use Data::Dumper; +use Getopt::Long; +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); + +my $processed = 0; +my $callback; + +sub handled_commands { + return { + copycds => "copycds", + } +} + +sub take_answer { +#TODO: Intelligently filter and decide things + my $resp = shift; + $callback->($resp); +} + +sub process_request { + my $request = shift; + $callback = shift; + my $doreq = shift; + my $distname = undef; + my $arch = undef; + + @ARGV = @{$request->{arg}}; + GetOptions( + 'n|name|osver=s' => \$distname, + 'a|arch=s' => \$arch + ); + if ($arch and $arch =~ /i.86/) { + $arch = x86; + } + my @args = @ARGV; #copy ARGV + unless ($#args >= 0) { + $callback->({error=>"copycds needs at least one full path to ISO currently."}); + return; + } + foreach (@args) { + unless (-r $_ and -f $_) { + $callback->({error=>"copycds currently only understands FULL path to iso files, $_ not processed"}); + return; + } + mkdir "/mnt/xcat"; + if (system("mount -o loop $_ /mnt/xcat")) { + $callback->({error=>"copycds was unable to examine $_ as an install image"}); + return; + } + my $newreq = dclone($request); + $newreq->{command}= [ 'copycd' ]; #Note the singular, it's different + $newreq->{arg} = ["-p","/mnt/xcat"]; + if ($distname) { + push @{$newreq->{arg}},("-n",$distname); + } + if ($arch) { + push @{$newreq->{arg}},("-a",$arch); + } + $doreq->($newreq,\&take_answer); + + system("umount /mnt/xcat"); + } +} + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/destiny.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/destiny.pm new file mode 100644 index 000000000..d16d54401 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/destiny.pm @@ -0,0 +1,239 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::destiny; +use xCAT::NodeRange; +use Data::Dumper; +use Sys::Syslog; +use strict; + +my $request; +my $callback; +my $subreq; +my $errored = 0; + +sub handled_commands { + return { + setdestiny => "destiny", + getdestiny => "destiny", + nextdestiny => "destiny" + } +} +sub process_request { + $request = shift; + $callback = shift; + $subreq = shift; + if ($request->{command}->[0] eq 'getdestiny') { + getdestiny(); + } + if ($request->{command}->[0] eq 'nextdestiny') { + nextdestiny($request); + } + if ($request->{command}->[0] eq 'setdestiny') { + setdestiny($request); + } +} + +sub relay_response { + my $resp = shift; + $callback->($resp); + if ($resp and $resp->{errorcode} and $resp->{errorcode}->[0]) { + $errored = 1; + } +} + +sub setdestiny { + my $req=shift; + my $chaintab = xCAT::Table->new('chain'); + my @nodes=@{$req->{node}}; + my $state = $req->{arg}->[0]; + if ($state eq "next") { + return nextdestiny(); + } elsif ($state =~ /^install$/ or $state eq "install") { + my $nodetype = xCAT::Table->new('nodetype'); + foreach (@{$req->{node}}) { + my $ntent = $nodetype->getNodeAttribs($_,[qw(os arch profile)]); + if ($ntent and $ntent->{os}) { + $state = "install ".$ntent->{os}; + } + if ($ntent and $ntent->{arch}) { + $state .= "-".$ntent->{arch}; + } + if ($ntent and $ntent->{profile}) { + $state .= "-".$ntent->{profile}; + } + $chaintab->setNodeAttribs($_,{currchain=>"boot"}); + } + $subreq->({command=>["mkinstall"], + node=>$req->{node}}, \&relay_response); + if ($errored) { return; } + } elsif ($state eq "shell" or $state eq "standby" or $state =~ /^runcmd/) { + my $noderes=xCAT::Table->new('noderes'); + my $nodetype = xCAT::Table->new('nodetype'); + my $sitetab = xCAT::Table->new('site'); + (my $portent) = $sitetab->getAttribs({key=>'xcatdport'},'value'); + (my $mastent) = $sitetab->getAttribs({key=>'master'},'value'); + foreach (@nodes) { + my $ent = $nodetype->getNodeAttribs($_,[qw(arch)]); + unless ($ent and $ent->{arch}) { + $callback->({error=>["No archictecture defined in nodetype table for $_"],errorcode=>[1]}); + return; + } + my $arch = $ent->{arch}; + my $ent = $noderes->getNodeAttribs($_,[qw(xcatmaster)]); + my $master; + if ($mastent and $mastent->{value}) { + $master = $mastent->{value}; + } + if ($ent and $ent->{xcatmaster}) { + $master = $ent->{xcatmaster}; + } + unless ($master) { + $callback->({error=>["No master in site table nor noderes table for $_"],errorcode=>[1]}); + return; + } + my $xcatdport="3001"; + if ($portent and $portent->{value}) { + $xcatdport = $portent->{value}; + } + $noderes->setNodeAttribs($_,{kernel => "xcat/nbk.$arch", + initrd => "xcat/nbfs.$arch.gz", + kcmdline => "xcatd=$master:$xcatdport"}); + } + $nodetype->close; + } elsif (!($state eq "boot")) { + $callback->({error=>["Unknown state $state requested"],errorcode=>[1]}); + return; + } + foreach (@nodes) { + $chaintab->setNodeAttribs($_,{currstate=>$state}); + } + return getdestiny(); +} + + +sub nextdestiny { + my @nodes; + if ($request and $request->{node}) { + if (ref($request->{node})) { + @nodes = @{$request->{node}}; + } else { + @nodes = ($request->{node}); + } + #TODO: service third party getdestiny.. + } else { #client asking to move along its own chain + #TODO: SECURITY with this, any one on a node could advance the chain, for node, need to think of some strategy to deal with... + unless ($request->{'!xcat_clienthost'}->[0]) { + #ERROR? malformed request + return; #nothing to do here... + } + my $node = $request->{'!xcat_clienthost'}->[0]; + ($node) = noderange($node); + unless ($node) { + #not a node, don't trust it + return; + } + @nodes=($node); + } + + my $node; + foreach $node (@nodes) { + my $chaintab = xCAT::Table->new('chain'); + unless($chaintab) { + syslog("local1|err","ERROR: $node requested destiny update, no chain table"); + return; #nothing to do... + } + my $ref = $chaintab->getNodeAttribs($node,[qw(currstate currchain chain)]); + unless ($ref->{chain}) { + syslog ("currchain local1|err","ERROR: node requested destiny update, no path in chain.currchain"); + $chaintab->close; + return; #Can't possibly do anything intelligent.. + } + unless ($ref->{currchain}) { #If no current chain, copy the default + $ref->{currchain} = $ref->{chain}; + } + my @chain = split /[,:;]/,$ref->{currchain}; + + $ref->{currstate} = shift @chain; + $ref->{currchain}=join(',',@chain); + unless ($ref->{currchain}) { #If we've gone off the end of the chain, have currchain stick + $ref->{currchain} = $ref->{currstate}; + } + $chaintab->setNodeAttribs($node,$ref); #$ref is in a state to commit back to db + $chaintab->close; + my %requ; + $requ{node}=[$node]; + $requ{arg}=[$ref->{currstate}]; + setdestiny(\%requ); + getdestiny(); + } +} + + +sub getdestiny { + my @args; + my @nodes; + if ($request->{node}) { + if (ref($request->{node})) { + @nodes = @{$request->{node}}; + } else { + @nodes = ($request->{node}); + } + } else { # a client asking for it's own destiny. + unless ($request->{'!xcat_clienthost'}->[0]) { + $callback->({destiny=>[ 'discover' ]}); + return; + } + my ($node) = noderange($request->{'!xcat_clienthost'}->[0]); + unless ($node) { # it had a valid hostname, but isn't a node + $callback->({destiny=>[ 'discover' ]}); + return; + } + @nodes=($node); + } + my $node; + foreach $node (@nodes) { + my $chaintab = xCAT::Table->new('chain'); + unless ($chaintab) { #Without destiny, have the node wait with ssh hopefully open at least + $callback->({node=>[{name=>[$node],data=>['standby'],destiny=>[ 'standby' ]}]}); + return; + } + my $ref = $chaintab->getNodeAttribs($node,[qw(currstate chain)]); + unless ($ref) { + $callback->({node=>[{name=>[$node],data=>['standby'],destiny=>[ 'standby' ]}]}); + return; + } + unless ($ref->{currstate}) { #Has a record, but not yet in a state... + my @chain = split /,/,$ref->{chain}; + $ref->{currstate} = shift @chain; + $chaintab->setNodeAttribs($node,{currstate=>$ref->{currstate}}); + } + my $noderestab = xCAT::Table->new('noderes'); #In case client decides to download images, get data out to it + my %response; + $response{name}=[$node]; + $response{data}=[$ref->{currstate}]; + $response{destiny}=[$ref->{currstate}]; + my $sitetab= xCAT::Table->new('site'); + my $nrent = $noderestab->getNodeAttribs($node,[qw(tftpserver kernel initrd kcmdline xcatmaster)]); + (my $sent) = $sitetab->getAttribs({key=>'master'},'value'); + if (defined $nrent->{kernel}) { + $response{kernel}=$nrent->{kernel}; + } + if (defined $nrent->{initrd}) { + $response{initrd}=$nrent->{initrd}; + } + if (defined $nrent->{kcmdline}) { + $response{kcmdline}=$nrent->{kcmdline}; + } + if (defined $nrent->{tftpserver}) { + $response{imgserver}=$nrent->{tftpserver}; + } elsif (defined $nrent->{xcatmaster}) { + $response{imgserver}=$nrent->{xcatmaster}; + } elsif (defined($sent->{value})) { + $response{imgserver}=$sent->{value}; + } + + $callback->({node=>[\%response]}); + } +} + + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/dhcp.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/dhcp.pm new file mode 100644 index 000000000..85f095df6 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/dhcp.pm @@ -0,0 +1,333 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::dhcp; +use xCAT::Table; +use Data::Dumper; +use MIME::Base64; +use Socket; +use Sys::Syslog; +use IPC::Open2; + +my @dhcpconf; #Hold DHCP config file contents to be written back. +my @nrn; # To hold output of netstat -rn to be consulted throughout process +my $domain; +my $omshell; + + +sub handled_commands { + return { + makedhcp => "dhcp", + } +} + +sub delnode { + my $node = shift; + my $inetn = inet_aton($node); + + print $omshell "new host\n"; + print $omshell "set name = \"$node\"\n"; #Find and destroy conflict name + print $omshell "open\n"; + print $omshell "remove\n"; + print $omshell "close\n"; + if ($inetn) { + my $ip = inet_ntoa(inet_aton($node));; + unless ($ip) { return; } + print $omshell "new host\n"; + print $omshell "set ip-address = $ip\n"; #find and destroy ip conflict + print $omshell "open\n"; + print $omshell "remove\n"; + print $omshell "close\n"; + } +} + + +sub addnode { +#Use omshell to add the node. +#the process used is blind typing commands that should work +#it tries to delet any conflicting entries matched by name and +#hardware address and ip address before creating a brand now one +#unfortunate side effect: dhcpd.leases can look ugly over time, when +#doing updates would keep it cleaner, good news, dhcpd restart cleans +#up the lease file the way we would want anyway. + my $node = shift; + my $ent; + my $mactab = xCAT::Table->new('mac'); + unless ($mactab) { return; } #TODO: report error sanely + $ent = $mactab->getNodeAttribs($node,[qw(mac interface)]); + unless ($ent and $ent->{mac}) { + return; #TODO: sane error + } + my $inetn = inet_aton($node); + unless ($inetn) { + syslog("local1|err","xCAT DHCP plugin unable to resolve IP for $node"); + return; + } + my $ip = inet_ntoa(inet_aton($node));; + print "Setting $node ($ip) to ".$ent->{mac}."\n"; + print $omshell "new host\n"; + print $omshell "set name = \"$node\"\n"; #Find and destroy conflict name + print $omshell "open\n"; + print $omshell "remove\n"; + print $omshell "close\n"; + print $omshell "new host\n"; + print $omshell "set ip-address = $ip\n"; #find and destroy ip conflict + print $omshell "open\n"; + print $omshell "remove\n"; + print $omshell "close\n"; + print $omshell "new host\n"; + print $omshell "set hardware-address = ".$ent->{mac}."\n"; #find and destroy mac conflict + print $omshell "open\n"; + print $omshell "remove\n"; + print $omshell "close\n"; + print $omshell "new host\n"; + print $omshell "set name = \"$node\"\n"; + print $omshell "set hardware-address = ".$ent->{mac}."\n"; + print $omshell "set hardware-type = 1\n"; + print $omshell "set ip-address = $ip\n"; + print $omshell "create\n"; + unless (grep /#definition for host $node/,@dhcpconf) { + push @dhcpconf,"#definition for host $node can be found in the dhcpd.leases file\n"; + } +} +sub process_request { + my $req = shift; + my $callback = shift; + my $sitetab = xCAT::Table->new('site'); + my %activenics; + my $querynics=1; + if ($sitetab) { + my $href; + ($href) = $sitetab->getAttribs({key=>'dhcpinterfaces'},'value'); + unless ($href and $href->{value}) { #LEGACY: singular keyname for old style site value + ($href) = $sitetab->getAttribs({key=>'dhcpinterface'},'value'); + } + if ($href and $href->{value}) { + foreach (split /[,\s]+/,$href->{value}) { + $activenics{$_} = 1; + $querynics=0; + } + } + ($href) = $sitetab->getAttribs({key=>'domain'},'value'); + unless ($href and $href->{value}) { + $callback->({error=>["No domain defined in site tabe"],errorcode=>[1]}); + return; + } + $domain = $href->{value}; + } + + @dhcpconf = (); + unless ($req->{arg} or $req->{node}) { + $callback->({data=>["Usage: makedhcp <-n> "]}); + return; + } + if (grep /-n/,@{$req->{arg}}) { + if (-e "/etc/dhcpd.conf") { + my $bakname = "/etc/dhcpd.conf.xcatbak"; + while (-e $bakname) { #look for unused backup name.. + $bakname .= "~"; + } + rename("/etc/dhcpd.conf",$bakname); + } + } else { + open($rconf,"/etc/dhcpd.conf"); # Read file into memory + if ($rconf) { + while (<$rconf>) { + push @dhcpconf,$_; + } + close($rconf); + } + unless ($dhcpconf[0] =~ /^#xCAT/) { #Discard file if not xCAT originated, like 1.x did + @dhcpconf = (); + } + } + @nrn = split /\n/,`/bin/netstat -rn`; + splice @nrn,0,2; #get rid of header + if ($querynics) { #Use netstat to determine activenics only when no site ent. + foreach (@nrn) { + my @ent = split /\s+/; + if ($ent[7] =~ m/(ipoib|ib|vlan|bond|eth|myri|man|wlan)/) { #Mask out many types of interfaces, like xCAT 1.x + $activenics{$ent[7]} = 1; + } + } + } + unless ($dhcpconf[0]) { #populate an empty config with some starter data... + newconfig(); + } + foreach (keys %activenics) { + addnic($_); + } + if ($req->{node}) { + my $passtab = xCAT::Table->new('passwd'); + my $ent; + ($ent) = $passtab->getAttribs({key=>"omapi"},qw(username password)); + unless ($ent->{username} and $ent->{password}) { return; } # TODO sane err + #Have nodes to update + #open2($omshellout,$omshell,"/usr/bin/omshell"); + open($omshell,"|/usr/bin/omshell > /dev/null"); + + print $omshell "key ".$ent->{username}." \"".$ent->{password}."\"\n"; + print $omshell "connect\n"; + foreach(@{$req->{node}}) { + if (grep /^-d$/,@{$req->{arg}}) { + delnode $_; + } else { + addnode $_; + } + } + close($omshell); + } + foreach (@nrn) { + my @line = split /\s+/; + if ($activenics{$line[7]}) { + addnet($line[0],$line[2]); + } + } + writeout(); +} + +sub addnet { + my $net = shift; + my $mask = shift; + my $nic; + unless (grep /\} # $net\/$mask subnet_end/,@dhcpconf) { + foreach (@nrn) { # search for relevant NIC + my @ent = split /\s+/; + if ($ent[0] eq $net and $ent[2] eq $mask) { + $nic=$ent[7]; + } + } + print "Need to add $net $mask under $nic\n"; + my $idx=0; + while ($idx <= $#dhcpconf) { + if ($dhcpconf[$idx] =~ /\} # $nic nic_end\n/) { + last; + } + $idx++; + } + unless ($dhcpconf[$idx] =~ /\} # $nic nic_end\n/) { + return 1; #TODO: this is an error condition + } + # if here, means we found the idx before which to insert + my $nettab = xCAT::Table->new("networks"); + my $nameservers; + my $gateway; + my $tftp; + my $range; + if ($nettab) { + my ($ent) = $nettab->getAttribs({net=>$net,mask=>$mask},qw(tftpserver nameservers gateway dynamicrange)); + if ($ent and $ent->{nameservers}) { + $nameservers = $ent->{nameservers}; + } + if ($ent and $ent->{tftpserver}) { + $tftp = $ent->{tftpserver}; + } + if ($ent and $ent->{gateway}) { + $gateway = $ent->{gateway}; + } + if ($ent and $ent->{dynamicrange}) { + $range = $ent->{dynamicrange}; + $range =~ s/[,-]/ /g; + } + } + my @netent; + @netent = ( + " subnet $net netmask $mask {\n", + " max-lease-time 43200;\n", + " min-lease-time 43200;\n", + " default-lease-time 43200;\n" + ); + if ($gateway) { + push @netent," option routers $gateway;\n"; + } + if ($tftp) { + push @netent," next-server $tftp;\n"; + } + push @netent," option domain-name \"$domain\";\n"; + if ($nameservers) { + push @netent," option domain-name-servers $nameservers;\n"; + } + push @netent," if option client-architecture = 00:00 { #x86\n"; + push @netent," filename \"pxelinux.0\";\n"; + push @netent," } else if option client-architecture = 00:02 { #ia64\n "; + push @netent," filename \"elilo.efi\";\n"; + push @netent," } else if substring(filename,0,1) = null { #otherwise, provide yaboot if the client isn't specific\n "; + push @netent," filename \"yaboot\";\n"; + push @netent," }\n"; + if ($range) { push @netent," range dynamic-bootp $range;\n" }; + push @netent," } # $net\/$mask subnet_end\n"; + splice(@dhcpconf,$idx,0,@netent); + } +} + + + + +sub addnic { + my $nic = shift; + my $firstindex=0; + my $lastindex=0; + unless (grep /} # $nic nic_end/,@dhcpconf) { #add a section if not there + print "Adding NIC $nic\n"; + push @dhcpconf,"shared-network $nic {\n"; + push @dhcpconf,"\} # $nic nic_end\n"; + } + #return; #Don't touch it, it should already be fine.. + #my $idx=0; + #while ($idx <= $#dhcpconf) { + # if ($dhcpconf[$idx] =~ /^shared-network $nic {/) { + # $firstindex = $idx; # found the first place to chop... + # } elsif ($dhcpconf[$idx] =~ /} # $nic network_end/) { + # $lastindex=$idx; + # } + # $idx++; + #} + #print Dumper(\@dhcpconf); + #if ($firstindex and $lastindex) { + # splice @dhcpconf,$firstindex,($lastindex-$firstindex+1); + #} + #print Dumper(\@dhcpconf); +} + + +sub writeout { + my $targ; + open($targ,'>',"/etc/dhcpd.conf"); + foreach (@dhcpconf) { + print $targ $_; + } + close($targ) +} + +sub newconfig { +# This function puts a standard header in and enough to make omapi work. + my $passtab = xCAT::Table->new('passwd',-create=>1); + push @dhcpconf,"#xCAT generated dhcp configuration\n"; + push @dhcpconf,"\n"; + push @dhcpconf,"authoritative;\n"; + push @dhcpconf,"ddns-update-style none;\n"; + push @dhcpconf,"option client-architecture code 93 = unsigned integer 16;\n"; + push @dhcpconf,"\n"; + push @dhcpconf,"omapi-port 7911;\n"; #Enable omapi... + push @dhcpconf,"key xcat_key {\n"; + push @dhcpconf," algorithm hmac-md5;\n"; + my $secret = encode_base64(genpassword(32)); #Random from set of 62^32 + chomp $secret; + $passtab->setAttribs({key=>omapi},{username=>'xcat_key',password=>$secret}); + push @dhcpconf," secret \"".$secret."\";\n"; + push @dhcpconf,"};\n"; + push @dhcpconf,"omapi-key xcat_key;\n"; +} + +sub genpassword { +#Generate a pseudo-random password of specified length + my $length = shift; + my $password=''; + my $characters= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890'; + srand; #have to reseed, rand is not rand otherwise + while (length($password) < $length) { + $password .= substr($characters,int(rand 63),1); + } + return $password; +} + + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/hosts.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/hosts.pm new file mode 100644 index 000000000..eaab7a6e5 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/hosts.pm @@ -0,0 +1,88 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::hosts; +use xCAT::Table; +use Data::Dumper; + +my @hosts; #Hold /etc/hosts data to be written back + + +my %usage=( + makehosts => "Usage: makehosts [-n] ", +); +sub handled_commands { + return { + makehosts => "hosts", + } +} + + +sub addnode { + my $node = shift; + my $ip = shift; + my $othernames = shift; + my $idx=0; + my $foundone=0; + + while ($idx <= $#hosts) { + if ($hosts[$idx] =~ /^${ip}\s/ or $hosts[$idx] =~ /^\d+\.\d+\.\d+\.\d+\s+${node}\s/) { + #TODO: if foundone, delete a dupe + $hosts[$idx] = "$ip $node $othernames\n"; + $foundone=1; + } + $idx++; + } + if ($foundone) { return;} + push @hosts,"$ip $node $othernames\n"; +} +sub process_request { + my $req = shift; + my $callback = shift; + my $hoststab = xCAT::Table->new('hosts'); + @hosts = (); + if (grep /-h/,@{$req->{arg}}) { + $callback->({data=>$usage{makehosts}}); + return; + } + if (grep /-n/,@{$req->{arg}}) { + if (-e "/etc/hosts") { + my $bakname = "/etc/hosts.xcatbak"; + while (-e $bakname) { #look for unused backup name.. + $bakname .= "~"; + } + rename("/etc/hosts",$bakname); + } + } else { + open($rconf,"/etc/hosts"); # Read file into memory + if ($rconf) { + while (<$rconf>) { + push @hosts,$_; + } + close($rconf); + } + } + + if ($req->{node}) { + foreach(@{$req->{node}}) { + my $ref = $hoststab->getNodeAttribs($_,[qw(ip node hostnames)]); + addnode $ref->{node},$ref->{ip},$ref->{hostnames}; + } + } else { + my @hostents = $hoststab->getAllNodeAttribs(['ip','node','hostnames']); + foreach (@hostents) { + addnode $_->{node},$_->{ip},$_->{hostnames}; + } + } + writeout(); +} + + +sub writeout { + my $targ; + open($targ,'>',"/etc/hosts"); + foreach (@hosts) { + print $targ $_; + } + close($targ) +} + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/ipmi.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/ipmi.pm new file mode 100644 index 000000000..ec872bcd8 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/ipmi.pm @@ -0,0 +1,4447 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#egan@us.ibm.com +#modified by jbjohnso@us.ibm.com +#(C)IBM Corp + +$xcat_plugins{ipmi}="this"; +package xCAT_plugin::ipmi; + +use Storable qw(store_fd retrieve_fd thaw freeze); + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw( + ipmiinit + ipmicmd +); + +sub handled_commands { + return { + rpower => 'nodehm:power,mgt', + rvitals => 'nodehm:vitals,mgt', + rinv => 'nodehm:inv,mgt', + rsetboot => 'nodehm:mgt', + rbeacon => 'nodehm:beacon,mgt', + reventlog => 'nodehm:eventlog,mgt', + } +} +my %usage = ( + "rpower" => "Usage: rpower [on|off|reset|stat|boot]", + "rbeacon" => "Usage: rbeacon [on|off|stat]", + "rvitals" => "Usage: rvitals [all|temp|voltage|fanspeed|power|leds]", + "reventlog" => "Usage: reventlog [all|clear|]", + "rinv" => "Usage: rinv [all|model|serial|vpd|mprom|deviceid|uuid]", + "rsetboot" => "Usage: rsetboot [net|hd|cd|def|stat]" +); + + + +use strict; +use Data::Dumper; +use POSIX "WNOHANG"; +use IO::Handle; +use IO::Socket; +use IO::Select; +use Class::Struct; +use Digest::MD5 qw(md5); +use POSIX qw(WNOHANG mkfifo strftime); +use Fcntl qw(:flock); + +#local to module +my @rmcp = (0x06,0x00,0xff,0x07); +my $auth; +my $rssa = 0x20; +my $rqsa = 0x81; +my $seqlun = 0x00; +my @session_id = (0,0,0,0); +my @challenge = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); +my @seqnum = (0,0,0,0); +my $userid; +my $passwd; +my $timeout; +my $port; +my $debug; +my $sock; +my @user; +my @pass; +my $channel_number; +my %sdr_hash; +my %fru_hash; +my $authoffset=0; +my $enable_cache="yes"; +my $cache_dir = "/var/cache/xcat"; +#my $ibmledtab = $ENV{XCATROOT}."/lib/GUMI/ibmleds.tab"; +use xCAT::data::ibmleds; +use xCAT::data::ipmigenericevents; +use xCAT::data::ipmisensorevents; +my $cache_version = 1; + +my %codes = ( + 0x00 => "Command Completed Normal", + 0xC0 => "Node busy, command could not be processed", + 0xC1 => "Invalid command", + 0xC2 => "Command invalid for given LUN", + 0xC3 => "Timeout while processing command, response unavailable", + 0xC4 => "Out of space, could not execute command", + 0xC5 => "Reservation canceled or invalid reservation ID", + 0xC6 => "Request data truncated", + 0xC7 => "Request data length invalid", + 0xC8 => "Request data field length limit exceeded", + 0xC9 => "Parameter out of range", + 0xCA => "Cannot return number of requested data bytes", + 0xCB => "Requested Sensor, data, or record not present", + 0xCB => "Not present", + 0xCC => "Invalid data field in Request", + 0xCD => "Command illegal for specified sensor or record type", + 0xCE => "Command response could not be provided", + 0xCF => "Cannot execute duplicated request", + 0xD0 => "Command reqponse could not be provided. SDR Repository in update mode", + 0xD1 => "Command response could not be provided. Device in firmware update mode", + 0xD2 => "Command response could not be provided. BMC initialization or initialization agent in progress", + 0xD3 => "Destination unavailable", + 0xD4 => "Insufficient privilege level", + 0xD5 => "Command or request parameter(s) not supported in present state", + 0xFF => "Unspecified error", +); + +my %units = ( + 0 => "unspecified", + 1 => "C", + 2 => "F", + 3 => "K", + 4 => "Volts", + 5 => "Amps", + 6 => "Watts", + 7 => "Joules", + 8 => "Coulombs", + 9 => "VA", + 10 => "Nits", + 11 => "lumen", + 12 => "lux", + 13 => "Candela", + 14 => "kPa", + 15 => "PSI", + 16 => "Newton", + 17 => "CFM", + 18 => "RPM", + 19 => "Hz", + 20 => "microsecond", + 21 => "millisecond", + 22 => "second", + 23 => "minute", + 24 => "hour", + 25 => "day", + 26 => "week", + 27 => "mil", + 28 => "inches", + 29 => "feet", + 30 => "cu in", + 31 => "cu feet", + 32 => "mm", + 33 => "cm", + 34 => "m", + 35 => "cu cm", + 36 => "cu m", + 37 => "liters", + 38 => "fluid ounce", + 39 => "radians", + 40 => "steradians", + 41 => "revolutions", + 42 => "cycles", + 43 => "gravities", + 44 => "ounce", + 45 => "pound", + 46 => "ft-lb", + 47 => "oz-in", + 48 => "gauss", + 49 => "gilberts", + 50 => "henry", + 51 => "millihenry", + 52 => "farad", + 53 => "microfarad", + 54 => "ohms", + 55 => "siemens", + 56 => "mole", + 57 => "becquerel", + 58 => "PPM", + 59 => "reserved", + 60 => "Decibels", + 61 => "DbA", + 62 => "DbC", + 63 => "gray", + 64 => "sievert", + 65 => "color temp deg K", + 66 => "bit", + 67 => "kilobit", + 68 => "megabit", + 69 => "gigabit", + 70 => "byte", + 71 => "kilobyte", + 72 => "megabyte", + 73 => "gigabyte", + 74 => "word", + 75 => "dword", + 76 => "qword", + 77 => "line", + 78 => "hit", + 79 => "miss", + 80 => "retry", + 81 => "reset", + 82 => "overflow", + 83 => "underrun", + 84 => "collision", + 85 => "packets", + 86 => "messages", + 87 => "characters", + 88 => "error", + 89 => "correctable error", + 90 => "uncorrectable error", +); + +my %chassis_types = ( + 0 => "Unspecified", + 1 => "Other", + 2 => "Unknown", + 3 => "Desktop", + 4 => "Low Profile Desktop", + 5 => "Pizza Box", + 6 => "Mini Tower", + 7 => "Tower", + 8 => "Portable", + 9 => "LapTop", + 10 => "Notebook", + 11 => "Hand Held", + 12 => "Docking Station", + 13 => "All in One", + 14 => "Sub Notebook", + 15 => "Space-saving", + 16 => "Lunch Box", + 17 => "Main Server Chassis", + 18 => "Expansion Chassis", + 19 => "SubChassis", + 20 => "Bus Expansion Chassis", + 21 => "Peripheral Chassis", + 22 => "RAID Chassis", + 23 => "Rack Mount Chassis", +); + +my %MFG_ID = ( + 2 => "IBM", + 343 => "Intel", +); + +my %PROD_ID = ( + "2:34869" => "e325", + "2:3" => "x346", + "2:4" => "x336", + "343:258" => "Tiger 2", + "343:256" => "Tiger 4", +); + +my $localtrys = 3; +my $localdebug = 0; + +struct SDR_rep_info => { + version => '$', + rec_count => '$', + resv_sdr => '$', +}; + +struct SDR => { + rec_type => '$', + sensor_owner_id => '$', + sensor_owner_lun => '$', + sensor_number => '$', + entity_id => '$', + entity_instance => '$', + sensor_init => '$', + sensor_cap => '$', + sensor_type => '$', + event_type_code => '$', + ass_event_mask => '@', + deass_event_mask => '@', + dis_read_mask => '@', + sensor_units_1 => '$', + sensor_units_2 => '$', + sensor_units_3 => '$', + linearization => '$', + M => '$', + tolerance => '$', + B => '$', + accuracy => '$', + accuracy_exp => '$', + R_exp => '$', + B_exp => '$', + analog_char_flag => '$', + nominal_reading => '$', + normal_max => '$', + normal_min => '$', + sensor_max_read => '$', + sensor_min_read => '$', + upper_nr_threshold => '$', + upper_crit_thres => '$', + upper_ncrit_thres => '$', + lower_nr_threshold => '$', + lower_crit_thres => '$', + lower_ncrit_thres => '$', + pos_threshold => '$', + neg_threshold => '$', + id_string_type => '$', + id_string => '$', + #LED id + led_id => '$', +}; + +struct FRU => { + rec_type => '$', + desc => '$', + value => '$', +}; + +sub ipmiinit { + my $ipmimaxp = 80; + my $ipmitimeout = 3; + my $ipmitrys = 3; + my $ipmiuser = 'USERID'; + my $ipmipass = 'PASSW0RD'; + my $tmp; + + + my $table = xCAT::Table->new('site'); + if ($table) { + $tmp=$table->getAttribs({'key'=>'ipmimaxp'},'value'); + if (defined($tmp)) { $ipmimaxp=$tmp->{value}; } + $tmp=$table->getAttribs({'key'=>'ipmitimeout'},'value'); + if (defined($tmp)) { $ipmitimeout=$tmp->{value}; } + $tmp=$table->getAttribs({'key'=>'ipmiretries'},'value'); + if (defined($tmp)) { $ipmitrys=$tmp->{value}; } + $tmp=$table->getAttribs({'key'=>'ipmisdrcache'},'value'); + } + $table = xCAT::Table->new('passwd'); + if ($table) { + $tmp=$table->getAttribs({'key'=>'ipmi'},['username','password']); + if (defined($tmp)) { + $ipmiuser = $tmp->{username}; + $ipmipass = $tmp->{password}; + } + } + return($ipmiuser,$ipmipass,$ipmimaxp,$ipmitimeout,$ipmitrys); +} + +sub ipmicmd { + my $node = shift; + $port = shift; + $userid = shift; + $passwd = shift; + $timeout = shift; + $localtrys = shift; + $debug = shift; + $localdebug = $debug; + + if($userid eq "(null)") { + $userid = ""; + } + if($passwd eq "(null)") { + $passwd = ""; + } + + @user = dopad16($userid); + @pass = dopad16($passwd); + + $seqlun = 0x00; + @session_id = (0,0,0,0); + @challenge = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); + @seqnum = (0,0,0,0); + $authoffset=0; + + my $command = shift; + my $subcommand = shift; + + my $rc=0; + my $text=""; + my $error=""; + my @output; + my $noclose=0; + + my $packed_ip = gethostbyname($node); + if(!defined($packed_ip)) { + $text = "failed to get IP for $node"; + return(2,$text); + } + my $nodeip = inet_ntoa($packed_ip); + + $sock = IO::Socket::INET->new( + Proto => 'udp', + PeerHost => $nodeip, + PeerPort => $port, + ); + if(!defined($sock)) { + $text = "failed to get socket: $@\n"; + return(2,$text); + } + + $error = getchanauthcap(); + if($error) { + return(1,$error); + } + if($debug) { + print "$node: gotchanauthcap\n"; + } + + if($command eq "ping") { + return(0,"ping"); + } + + $error = getsessionchallenge(); + if($error) { + return(1,$error); + } + if($debug) { + print "$node: gotsessionchallenge\n"; + } + + $error = activatesession(); + if($error) { + return(1,$error); + } + if($debug) { + print "$node: active session\n"; + } + + $error = setprivlevel(); + if($error) { + return(1,$error); + } + if($debug) { + print "$node: priv level set\n"; + } + + if($command eq "rpower") { + if($subcommand eq "stat" || $subcommand eq "state" || $subcommand eq "status") { + ($rc,$text) = power("stat"); + } + elsif($subcommand eq "on") { + ($rc,$text) = power("on"); + } + elsif($subcommand eq "nmi") { + ($rc,$text) = power("nmi"); + } + elsif($subcommand eq "off") { +# +# e325 hack +# +# my $mfg_id; +# my $prod_id; +# my $device_id; +# my $text0; +# +# ($rc,$text,$mfg_id,$prod_id,$device_id) = getdevid(); +# +# if(0 && $mfg_id == 2 && ($prod_id == 0x8835 || $prod_id == 8835) && $device_id == 0) { +# ($rc,$text0) = power("reset"); +# sleep(5); +# } +# +# e325 hack end +# + ($rc,$text) = power("off"); +# +# if($text0 ne "") { +# $text = $text0 . " " . $text; +# } + } + elsif($subcommand eq "reset") { + ($rc,$text) = power("reset"); + $noclose = 1; + } + elsif($subcommand eq "cycle") { + my $text2; + + ($rc,$text) = power("stat"); + + if($rc == 0 && $text eq "on") { + ($rc,$text) = power("off"); + if($rc == 0) { + sleep(5); + } + } + + if($rc == 0 && $text eq "off") { + ($rc,$text2) = power("on"); + } + + if($rc == 0) { + $text = $text . " " . $text2 + } + } + elsif($subcommand eq "boot") { + my $text2; + + ($rc,$text) = power("stat"); + + if($rc == 0) { + if($text eq "on") { + ($rc,$text2) = power("reset"); + $noclose = 1; + } + elsif($text eq "off") { + ($rc,$text2) = power("on"); + } + else { + $rc = 1; + } + + $text = $text . " " . $text2 + } + } + else { + $rc = 1; + $text = "unsupported command $command $subcommand"; + } + } + elsif($command eq "rbeacon") { + ($rc,$text) = beacon($subcommand); + } +# elsif($command eq "info") { +# if($subcommand eq "sensorname") { +# ($rc,$text) = initsdr(); +# if($rc == 0) { +# my $key; +# $text=""; +# foreach $key (keys %sdr_hash) { +# my $sdr = $sdr_hash{$key}; +# if($sdr->sensor_number == @_) { +# $text = $sdr_hash{$key}->id_string; +# last; +# } +# } +## if(defined $sdr_hash{@_}) { +## $text = $sdr_hash{@_}->id_string; +## } +# } +# } +# } + elsif($command eq "rvitals") { + ($rc,@output) = vitals($subcommand); + } + elsif($command eq "rspreset") { + ($rc,@output) = resetbmc(); + $noclose=1; + } + elsif($command eq "reventlog") { + if($subcommand eq "decodealert") { + ($rc,$text) = decodealert(@_); + } + else { + ($rc,@output) = eventlog($subcommand); + } + } + elsif($command eq "rinv") { + ($rc,@output) = inv($subcommand); + } + elsif($command eq "fru") { + ($rc,@output) = fru($subcommand); + } + elsif($command eq "sol.command") { + my $dc=0; + + $@ = ""; + eval { + my $cc=0; + my $kid; + my $pid=$$; + + $SIG{USR1} = sub {$cc=0;}; + $SIG{USR2} = sub {$dc++;}; + $SIG{CHLD} = sub {while(waitpid(-1,WNOHANG) > 0) { sleep(1); }}; + + mkfifo("/tmp/.sol.$pid",0666); + + my $child = fork(); + if(!defined $child) { + die; + } + + if($child > 0) { + $cc=1; + } + else { + system("$subcommand /tmp/.sol.$pid"); + + if($?/256 == 1) { + kill(12,$pid); + } + if($?/256 == 2) { + kill(12,$pid); + sleep(1); + kill(12,$pid); + } + + kill(10,$pid); + exit(0); + } + + open(FH,"< /tmp/.sol.$pid"); + my $kpid = ; + close(FH); + unlink("/tmp/.sol.$pid"); + + while($cc == 1) { + sleep(5); + ($rc,$text) = power("stat"); + $text=""; + if($rc != 0) { + kill(15,$kpid); + $cc=0; + } + } + + do { + $kid = waitpid(-1,WNOHANG); + sleep(1); + } until($kid == -1); + }; + if($@) { + @output = $@; + } + + $rc = $dc; + if($rc == 1) { + $noclose = 1; + } + } + elsif($command eq "rgetnetinfo") { + if($subcommand eq "all") { + my @subcommands = ( + "ip", + "netmask", + "gateway", + "backupgateway", + "snmpdest1", + "snmpdest2", + "snmpdest3", + "snmpdest4", + "community", + ); + my @coutput; + + foreach(@subcommands) { + $subcommand = $_; + ($rc,@output) = getnetinfo($subcommand); + push(@coutput,@output); + } + + @output = @coutput; + } + else { + ($rc,@output) = getnetinfo($subcommand); + } + } + elsif($command eq "rsetnetinfo") { + ($rc,@output) = setnetinfo($subcommand,@_); + if($rc == 0) { + ($rc,@output) = getnetinfo($subcommand); + } + } + elsif($command eq "sete325cli") { + ($rc,@output) = sete325cli($subcommand); + } + elsif($command eq "sete326cli") { + ($rc,@output) = sete325cli($subcommand); + } + elsif($command eq "generic") { + ($rc,@output) = generic($subcommand); + } + elsif($command eq "writefru") { + ($rc,@output) = writefru($subcommand,shift); + } + elsif($command eq "fru") { + ($rc,@output) = fru($subcommand); + } + elsif($command eq "rsetboot") { + ($rc,@output) = setboot($subcommand); + } + + else { + $rc = 1; + $text = "unsupported command $command $subcommand"; + } + if($debug) { + print "$node: command completed\n"; + } + + if($noclose == 0) { + $error = closesession(); + if($error) { + return(1,"$text, session close: $error"); + } + if($debug) { + print "$node: session closed.\n"; + } + } + + if($text) { + push(@output,$text); + } + + $sock->close(); + return($rc,@output); +} + +sub resetbmc { + my $netfun = 0x18; + my @cmd = (0x02); + my @returnd = (); + my $rc = 0; + my $text; + my $error; + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + if ($error) { + $rc = 1; + $text = $error; + } else { + if (0 == $returnd[36]) { + $text = "BMC reset"; + } else { + $text = sprintf("BMC Responded with code %d",$returnd[36]); + } + } + return($rc,$text); +} + +sub setnetinfo { + my $subcommand = shift; + my $input = shift; + + my $netfun = 0x30; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + my $match; + + if($subcommand eq "snmpdest") { + $subcommand = "snmpdest1"; + } + + if($subcommand eq "garp") { + my $halfsec = pop(@$input) * 2; + + if($halfsec > 255) { + $halfsec = 255; + } + if($halfsec < 4) { + $halfsec = 4; + } + + @cmd = (0x01,$channel_number,0x0b,$halfsec); + } + elsif($subcommand =~ m/snmpdest(\d+)/ ) { + my $dstip = pop(@$input); + my @dip = split /\./, $dstip; + @cmd = (0x01,$channel_number,0x13,$1,0x00,0x00,$dip[0],$dip[1],$dip[2],$dip[3],0,0,0,0,0,0); + } + else { + return(1,"unsupported command setnetinfo $subcommand"); + } + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + } + else { + if($subcommand eq "garp" or $subcommand =~ m/snmpdest\d+/) { + $code = $returnd[36]; + + if($code == 0x00) { + $text = "ok"; + } + } + + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + } + + return($rc,$text); +} + +sub getnetinfo { + my $subcommand = shift; + + my $netfun = 0x30; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + my $format = "%-25s"; + + if ($subcommand eq "snmpdest") { + $subcommand = "snmpdest1"; + } + + if($subcommand eq "garp") { + @cmd = (0x02,$channel_number,0x0b,0x00,0x00); + } + elsif ($subcommand =~ m/^snmpdest(\d+)/ ) { + @cmd = (0x02,$channel_number,0x13,$1,0x00); + } + elsif ($subcommand eq "ip") { + @cmd = (0x02,$channel_number,0x03,0x00,0x00); + } + elsif ($subcommand eq "netmask") { + @cmd = (0x02,$channel_number,0x06,0x00,0x00); + } + elsif ($subcommand eq "gateway") { + @cmd = (0x02,$channel_number,0x0C,0x00,0x00); + } + elsif ($subcommand eq "backupgateway") { + @cmd = (0x02,$channel_number,0x0E,0x00,0x00); + } + elsif ($subcommand eq "community") { + @cmd = (0x02,$channel_number,0x10,0x00,0x00); + } + else { + return(1,"unsupported command getnetinfo $subcommand"); + } + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + } + else { + if($subcommand eq "garp") { + $code = $returnd[36]; + + if($code == 0x00) { + $code = $returnd[38] / 2; + $text = sprintf("$format %d","Gratuitous ARP seconds:",$code); + } + else { + $rc = 1; + $text = $codes{$code}; + } + } + elsif($subcommand =~ m/^snmpdest(\d+)/ ) { + $text = sprintf("$format %d.%d.%d.%d", + "BMC SNMP Destination $1:", + $returnd[41], + $returnd[42], + $returnd[43], + $returnd[44]); + } + elsif($subcommand eq "ip") { + $text = sprintf("$format %d.%d.%d.%d", + "BMC IP:", + $returnd[38], + $returnd[39], + $returnd[40], + $returnd[41]); + } + elsif($subcommand eq "netmask") { + $text = sprintf("$format %d.%d.%d.%d", + "BMC Netmask:", + $returnd[38], + $returnd[39], + $returnd[40], + $returnd[41]); + } + elsif($subcommand eq "gateway") { + $text = sprintf("$format %d.%d.%d.%d", + "BMC Gateway:", + $returnd[38], + $returnd[39], + $returnd[40], + $returnd[41]); + } + elsif($subcommand eq "backupgateway") { + $text = sprintf("$format %d.%d.%d.%d", + "BMC Backup Gateway:", + $returnd[38], + $returnd[39], + $returnd[40], + $returnd[41]); + } + elsif ($subcommand eq "community") { + $text = sprintf("$format ","BMC SNMP Community:"); + my $l = 38; + while ($returnd[$l] ne 0) { + $l = $l + 1; + } + my $i=38; + while ($i<$l) { + $text = $text . sprintf("%c",$returnd[$i]); + $i = $i + 1; + } + } + + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + } + + return($rc,$text); +} + +sub sete325cli { + my $subcommand = shift; + + my $netfun = 0xc8; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + if($subcommand eq "disable") { + @cmd = (0x00); + } + elsif($subcommand eq "cli") { + @cmd = (0x02); + } + else { + return(1,"unsupported command sete325cli $subcommand"); + } + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + } + else { + if($code == 0x00) { + $rc = 0; + $text = "$subcommand"; + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + } + + return($rc,$text); +} + +sub setboot { + my $subcommand=shift; + my $netfun = 0x00; + my @cmd = (0x08,0x3,0x8); + my @returnd = (); + my $error; + my $rc = 0; + my $text = ""; + my $code; + my $skipset = 0; + my %bootchoices = ( + 0 => 'BIOS default', + 1 => 'Network', + 2 => 'Hard Drive', + 5 => 'CD/DVD', + 6 => 'BIOS Setup', + ); + + #This disables the 60 second timer + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + if ($subcommand eq "net") { + @cmd=(0x08,0x5,0x80,0x4,0x0,0x0,0x0); + } + elsif ($subcommand eq "hd" ) { + @cmd=(0x08,0x5,0x80,0x8,0x0,0x0,0x0); + } + elsif ($subcommand eq "cd" ) { + @cmd=(0x08,0x5,0x80,0x14,0x0,0x0,0x0); + } + elsif ($subcommand =~ m/^def/) { + @cmd=(0x08,0x5,0x0,0x0,0x0,0x0,0x0); + } + elsif ($subcommand eq "setup" ) { #Not supported by BMCs I've checked so far.. + @cmd=(0x08,0x5,0x18,0x0,0x0,0x0,0x0); + } + elsif ($subcommand =~ m/^stat/) { + $skipset=1; + } + else { + return(1,"unsupported command setboot $subcommand"); + } + + + unless ($skipset) { + $error = docmd( + $netfun, + \@cmd, + \@cmd, + \@returnd + ); + if($error) { + return(1,$error); + } + $code = $returnd[36-$authoffset]; + unless ($code == 0x00) { + return(1,$codes{$code}); + } + } + @cmd=(0x09,0x5,0x0,0x0); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + if($error) { + return(1,$error); + } + $code = $returnd[36-$authoffset]; + unless ($code == 0x00) { + return(1,$codes{$code}); + } + unless ($returnd[39-$authoffset] & 0x80) { + $text = "boot override inactive"; + return($rc,$text); + } + my $boot=($returnd[40-$authoffset] & 0x3C) >> 2; + $text = $bootchoices{$boot}; + return($rc,$text); +} + +sub power { + my $subcommand = shift; + + my $netfun = 0x00; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + if($subcommand eq "stat") { + @cmd = (0x01); + } + elsif($subcommand eq "on") { + @cmd = (0x02,0x01); + } + elsif($subcommand eq "off") { + @cmd = (0x02,0x00); + } + elsif($subcommand eq "reset") { + @cmd = (0x02,0x03); + } + elsif($subcommand eq "nmi") { + @cmd = (0x02,0x04); + } + else { + return(1,"unsupported command power $subcommand"); + } + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + } + else { + if($subcommand eq "stat") { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $code = $returnd[37-$authoffset]; + + if($code & 0b00000001) { + $text = "on"; + } + else { + $text = "off"; + } + } + else { + $rc = 1; + $text = $codes{$code}; + } + } + if($subcommand eq "nmi") { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $text="nmi"; + } + else { + $rc = 1; + $text = $codes{$code}; + } + } + if($subcommand eq "on") { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $text="on"; + } + else { + $rc = 1; + $text = $codes{$code}; + } + } + if($subcommand eq "off") { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $text="off"; + } + elsif($code == 0xd5) { + $text="off"; + } + else { + $rc = 1; + $text = $codes{$code}; + } + } + if($subcommand eq "reset") { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $text="reset"; + } + elsif($code == 0xd5) { + $text="off"; + } + else { + $rc = 1; + $text = $codes{$code}; + } + } + + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + } + + return($rc,$text); +} + +sub generic { + my $subcommand = shift; + my $netfun; + my @args; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + ($netfun,@args) = split(/-/,$subcommand); + + $netfun=oct($netfun); + printf("netfun: 0x%02x\n",$netfun); + + print "command: "; + foreach(@args) { + push(@cmd,oct($_)); + printf("0x%02x ",oct($_)); + } + print "\n\n"; + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + else { + $rc = 1; + $text = $codes{$code}; + } + + printf("return code: 0x%02x\n\n",$code); + + print "return data:\n"; + my @rdata = @returnd[37-$authoffset..@returnd-2]; + hexadump(\@rdata); + print "\n"; + + print "full output:\n"; + hexadump(\@returnd); + print "\n"; + +# if(!$text) { +# $rc = 1; +# $text = sprintf("unknown response %02x",$code); +# } + + return($rc,$text); +} + +sub beacon { + my $subcommand = shift; + + my $netfun = 0x00; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + if($subcommand eq "on") { + @cmd = (0x04,0xFF); + } + elsif($subcommand eq "off") { + @cmd = (0x04,0x00); + } + else { + return(1,"unsupported command beacon $subcommand"); + } + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + } + else { + if($subcommand eq "on") { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $text="on"; + } + else { + $rc = 1; + $text = $codes{$code}; + } + } + if($subcommand eq "off") { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $text="off"; + } + else { + $rc = 1; + $text = $codes{$code}; + } + } + + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + } + + return($rc,$text); +} + +sub inv { + my $subcommand = shift; + + my $rc = 0; + my $text; + my @output; + my @types; + my $format = "%-20s %s"; + + ($rc,$text) = initfru(); + if($rc != 0) { + return($rc,$text); + } + + if($subcommand eq "all") { + @types = qw(model serial deviceid mprom guid); + } + elsif($subcommand eq "model") { + @types = qw(model); + } + elsif($subcommand eq "serial") { + @types = qw(serial); + } + elsif($subcommand eq "vpd") { + @types = qw(model serial deviceid mprom); + } + elsif($subcommand eq "mprom") { + @types = qw(mprom); + } + elsif($subcommand eq "deviceid") { + @types = qw(deviceid); + } + elsif($subcommand eq "guid") { + @types = qw(guid); + } + elsif($subcommand eq "uuid") { + @types = qw(guid); + } + else { + return(1,"unsupported command inv $subcommand"); + } + + foreach(@types) { + my $type = $_; + my $otext; + my $key; + + foreach $key (keys %fru_hash) { + my $fru = $fru_hash{$key}; + #print($fru->rec_type."\n"); + if($fru->rec_type eq $type) { + $otext = sprintf($format,$fru_hash{$key}->desc . ":",$fru_hash{$key}->value); + #print $otext; + push(@output,$otext); + } + } + } + + return($rc,@output); +} + +sub initoemfru { + my $mfg_id = shift; + my $prod_id = shift; + my $device_id = shift; + + my $netfun; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my @output; + my $code; + + if($mfg_id == 2 && ($prod_id == 34869 or $prod_id == 31081 or $prod_id==34888)) { + $netfun = 0xc8; + + @cmd=(0x05); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + my @oem_fru_data = @returnd[37-$authoffset..@returnd-2]; + my $model_type = getascii(@oem_fru_data[0..3]); + my $model_number = getascii(@oem_fru_data[4..6]); + my $serial = getascii(@oem_fru_data[7..13]); + my $model = "$model_type-$model_number"; + + my $fru = FRU->new(); + $fru->rec_type("serial"); + $fru->desc("Serial Number"); + $fru->value($serial); + $fru_hash{1} = $fru; + + $fru = FRU->new(); + $fru->rec_type("model"); + $fru->desc("Model Number"); + $fru->value($model); + $fru_hash{2} = $fru; + + return(2,""); + } + if($mfg_id == 2 && $prod_id == 4 && 0) { + $netfun = 0x3a; + + @cmd=(0x0b,0x0,0x0,0x0,0x1,0x8); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + +hexadump(\@returnd); +return(2,""); + + my @oem_fru_data = @returnd[37-$authoffset..@returnd-2]; + my $model_type = getascii(@oem_fru_data[0..3]); + my $model_number = getascii(@oem_fru_data[4..6]); + my $serial = getascii(@oem_fru_data[7..13]); + my $model = "$model_type-$model_number"; + + my $fru = FRU->new(); + $fru->rec_type("serial"); + $fru->desc("Serial Number"); + $fru->value($serial); + $fru_hash{1} = $fru; + + $fru = FRU->new(); + $fru->rec_type("model"); + $fru->desc("Model Number"); + $fru->value($model); + $fru_hash{2} = $fru; + + return(2,""); + } + if($mfg_id == 2 && $prod_id == 20) { + my $serial = "unknown"; + my $model = "x3655"; + + my $fru = FRU->new(); + $fru->rec_type("serial"); + $fru->desc("Serial Number"); + $fru->value($serial); + $fru_hash{1} = $fru; + + $fru = FRU->new(); + $fru->rec_type("model"); + $fru->desc("Model Number"); + $fru->value($model); + $fru_hash{2} = $fru; + + return(2,""); + } + if($mfg_id == 2 && $prod_id == 3) { + my $serial = "unknown"; + my $model = "x346"; + + my $fru = FRU->new(); + $fru->rec_type("serial"); + $fru->desc("Serial Number"); + $fru->value($serial); + $fru_hash{1} = $fru; + + $fru = FRU->new(); + $fru->rec_type("model"); + $fru->desc("Model Number"); + $fru->value($model); + $fru_hash{2} = $fru; + + return(2,""); + } + if($mfg_id == 2 && $prod_id == 4) { + my $serial = "unknown"; + my $model = "x336"; + + my $fru = FRU->new(); + $fru->rec_type("serial"); + $fru->desc("Serial Number"); + $fru->value($serial); + $fru_hash{1} = $fru; + + $fru = FRU->new(); + $fru->rec_type("model"); + $fru->desc("Model Number"); + $fru->value($model); + $fru_hash{2} = $fru; + + return(2,""); + } + + return(1,"No OEM FRU Support"); +} + +sub initfru { + my $netfun = 0x28; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my @output; + my $code; + + my $mfg_id; + my $prod_id; + my $device_id; + my $dev_rev; + my $fw_rev1; + my $fw_rev2; + my $mprom; + my $fru; + my $guid; + my @guidcmd; + + ($rc,$text,$mfg_id,$prod_id,$device_id,$dev_rev,$fw_rev1,$fw_rev2) = getdevid(); + if($rc != 0) { + return($rc,$text); + } + + @guidcmd = (0x18,0x37); + if($mfg_id == 2 && $prod_id == 34869) { + @guidcmd = (0x18,0x08); + } + if($mfg_id == 2 && $prod_id == 4) { + @guidcmd = (0x18,0x08); + } + if($mfg_id == 2 && $prod_id == 3) { + @guidcmd = (0x18,0x08); + } + + ($rc,$text,$guid) = getguid(\@guidcmd); + if($rc != 0) { + return($rc,$text); + } + + if($mfg_id == 2 && $prod_id == 34869) { + $mprom = sprintf("%x.%x",$fw_rev1,$fw_rev2); + } + else { + my @lcmd = (0x50); + my @lreturnd = (); + my $lerror = docmd( + 0xe8, + \@lcmd, + \@lreturnd + ); + if ($lerror == 0 && $lreturnd[36-$authoffset] == 0) { + my @a = ($fw_rev2); + my @b= @lreturnd[37-$authoffset .. $#lreturnd-1]; + $mprom = sprintf("%d.%s (%s)",$fw_rev1,decodebcd(\@a),getascii(@b)); + } else { + my @a = ($fw_rev2); + $mprom = sprintf("%d.%s",$fw_rev1,decodebcd(\@a)); + } + } + + $fru = FRU->new(); + $fru->rec_type("mprom"); + $fru->desc("BMC Firmware"); + $fru->value($mprom); + $fru_hash{mprom} = $fru; + + $fru = FRU->new(); + $fru->rec_type("guid"); + $fru->desc("GUID"); + $fru->value($guid); + $fru_hash{guid} = $fru; + + $fru = FRU->new(); + $fru->rec_type("deviceid"); + $fru->desc("Manufacturer ID"); + my $value = $mfg_id; + if($MFG_ID{$mfg_id}) { + $value = "$MFG_ID{$mfg_id} ($mfg_id)"; + } + $fru->value($value); + $fru_hash{mfg_id} = $fru; + + $fru = FRU->new(); + $fru->rec_type("deviceid"); + $fru->desc("Product ID"); + $value = $prod_id; + my $tmp = "$mfg_id:$prod_id"; + if($PROD_ID{$tmp}) { + $value = "$PROD_ID{$tmp} ($prod_id)"; + } + $fru->value($value); + $fru_hash{prod_id} = $fru; + + $fru = FRU->new(); + $fru->rec_type("deviceid"); + $fru->desc("Device ID"); + $fru->value($device_id); + $fru_hash{device_id} = $fru; + +# ($rc,$text)=initoemfru($mfg_id,$prod_id,$device_id); +# if($rc == 1) { +# return($rc,$text); +# } +# if($rc == 2) { +# return(0,""); +# } + + @cmd=(0x10,0x00); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + my $fru_size_ls = $returnd[37-$authoffset]; + my $fru_size_ms = $returnd[38-$authoffset]; + my $fru_size = $fru_size_ms*256 + $fru_size_ls; + my $fru_bytes_words = $returnd[39-$authoffset] & 1; + + ($rc,@output) = frudump(0,8,8); + if($rc != 0) { + return($rc,@output); + } + + my $fru_header_ver = $output[0]; + my $fru_header_offset_internal = $output[1]; + my $fru_header_offset_chassis = $output[2] * 8; + my $fru_header_offset_board = $output[3]; + my $fru_header_offset_product = $output[4]; + my $fru_header_offset_mult = $output[5]; + + if($fru_header_ver != 1) { + ($rc,$text)=initoemfru($mfg_id,$prod_id,$device_id); + if($rc == 1) { + $text = "FRU format unknown"; + return($rc,$text); + } + if($rc == 2) { + return(0,""); + } + } + + ($rc,@output) = frudump($fru_header_offset_chassis,2,8); + if($rc != 0) { + return($rc,@output); + } + + my $chassis_info_area_format_version = $output[0]; + my $chassis_info_area_length = $output[1] * 8; + + if($chassis_info_area_format_version != 1) { + ($rc,$text)=initoemfru($mfg_id,$prod_id,$device_id); + if($rc == 1) { + $text = "FRU format unknown"; + return($rc,$text); + } + if($rc == 2) { + return(0,""); + } + } + + ($rc,@output) = frudump($fru_header_offset_chassis,$chassis_info_area_length,8); + if($rc != 0) { + return($rc,@output); + } + + my $c=2; + my $chassis_type = $output[$c++]; + my $chassis_part_num_type = ($output[$c] & 0b11000000) >> 6; + my $chassis_part_num_len = $output[$c] & 0b00111111; + my $chassis_part_num; + $c++; + if($chassis_part_num_type == 3) { + $chassis_part_num = getascii(@output[$c..$c+$chassis_part_num_len-1]); + } + else { + $chassis_part_num = "unsupported type $chassis_part_num_type"; + } + $c=$c+$chassis_part_num_len; + my $chassis_serial_num_type = ($output[$c] & 0b11000000) >> 6; + my $chassis_serial_num_len = $output[$c] & 0b00111111; + my $chassis_serial_num; + $c++; + if($chassis_serial_num_type == 3) { + $chassis_serial_num = getascii(@output[$c..$c+$chassis_serial_num_len-1]); + } + else { + $chassis_serial_num = "unsupported type $chassis_serial_num_type"; + } + if(!$chassis_part_num) { + $chassis_part_num = "undefined"; + } + if(!$chassis_serial_num) { + $chassis_serial_num = "undefined"; + } + + $fru = FRU->new(); + $fru->rec_type("serial"); + $fru->desc("Serial Number"); + $fru->value($chassis_serial_num); + $fru_hash{1} = $fru; + + if($chassis_types{$chassis_type}) { + $chassis_part_num .= " "; + $chassis_part_num .= $chassis_types{$chassis_type}; + } + + $fru = FRU->new(); + $fru->rec_type("model"); + $fru->desc("Model Number"); + $fru->value($chassis_part_num); + $fru_hash{2} = $fru; + + return($rc,$text); +} + +sub fru { + my $subcommand = shift; + my $netfun = 0x28; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my @output; + my $code; + + @cmd=(0x10,0x00); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + my $fru_size_ls = $returnd[37-$authoffset]; + my $fru_size_ms = $returnd[38-$authoffset]; + my $fru_size = $fru_size_ms*256 + $fru_size_ls; + my $fru_bytes_words = $returnd[39-$authoffset] & 1; + + if($subcommand eq "dump") { + print "FRU Size: $fru_size\n"; + my ($rc,@output) = frudump(0,$fru_size,8); + if($rc) { + return($rc,@output); + } + hexadump(\@output); + return(0,""); + } + if($subcommand eq "wipe") { + my @bytes = (); + + for(my $i = 0;$i < $fru_size;$i++) { + push(@bytes,0xff); + } + my ($rc,$text) = fruwrite(0,\@bytes,8); + if($rc) { + return($rc,$text); + } + return(0,"FRU $fru_size bytes wiped"); + } + + return(0,""); +} + +sub frudump { + my $offset = shift; + my $length = shift; + my $chunk = shift; + + my $netfun = 0x28; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my @output; + my $code; + my @fru_data=(); + + for(my $c=$offset;$c < $length+$offset;$c += $chunk) { + my $ms = int($c / 0x100); + my $ls = $c - $ms * 0x100; + + @cmd=(0x11,0x00,$ls,$ms,$chunk); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + my $count = $returnd[37-$authoffset]; + if($count != $chunk) { + $rc = 1; + $text = "FRU read error (bytes requested: $chunk, got: $count)"; + return($rc,$text); + } + + my @data = @returnd[38-$authoffset..@returnd-2]; + @fru_data = (@fru_data,@data); + } + + return(0,@fru_data); +} + +sub writefru { + my $serial = shift; + my $model = shift; + my @bytes; + my $rc; + my $text; + + #header + @bytes = (0x01,0x01,0x02,0x00,0x00,0x00,0x00); + @bytes = (@bytes,dochksum(\@bytes)); + + ($rc,$text) = fruwrite(0,\@bytes,8); + if($rc) { + return($rc,$text); + } + + #internal use area + @bytes = (0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00); + + ($rc,$text) = fruwrite(8,\@bytes,8); + if($rc) { + return($rc,$text); + } + + #chassis info area + @bytes = (0x01,0x04,0x17); + push(@bytes,0b11000000 + length($model)); + @bytes = (@bytes,unpack("C*",$model)); + push(@bytes,0b11000000 + length($serial)); + @bytes = (@bytes,unpack("C*",$serial)); + push(@bytes,0xc1); + + foreach(@bytes..30) { + push(@bytes,0x00); + } + @bytes = (@bytes,dochksum(\@bytes)); + + if(@bytes > 32) { + return(1,"Serial + Model too long"); + } + + ($rc,$text) = fruwrite(16,\@bytes,8); + if($rc) { + return($rc,$text); + } + + return(0,"FRU Updated"); +} + +sub fruwrite { + my $offset = shift; + my $bytes = shift; + my $chunk = shift; + my $length = @$bytes; + + my $netfun = 0x28; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my @output; + my $code; + my @fru_data=(); + + for(my $c=$offset;$c < $length+$offset;$c += $chunk) { + my $ms = int($c / 0x100); + my $ls = $c - $ms * 0x100; + + @cmd=(0x12,0x00,$ls,$ms,@$bytes[$c-$offset..$c-$offset+$chunk-1]); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + my $count = $returnd[37-$authoffset]; + if($count != $chunk) { + $rc = 1; + $text = "FRU write error (bytes requested: $chunk, wrote: $count)"; + return($rc,$text); + } + } + + return(0); +} + +sub decodealert { + my $trap = shift; + my $node = shift; + my $pet = shift; + my $rc; + my $text; + + ($rc,$text) = initsdr(); + if($rc != 0) { + return($rc,$text); + } + + my $type; + my $desc; + #my $ipmisensoreventtab = "$ENV{XCATROOT}/lib/GUMI/ipmisensorevent.tab"; + #my $ipmigenericeventtab = "$ENV{XCATROOT}/lib/GUMI/ipmigenericevent.tab"; + + my $offsetmask = 0b00000000000000000000000000001111; + my $offsetrmask = 0b00000000000000000000000001110000; + my $assertionmask = 0b00000000000000000000000010000000; + my $eventtypemask = 0b00000000000000001111111100000000; + my $sensortypemask = 0b00000000111111110000000000000000; + my $reservedmask = 0b11111111000000000000000000000000; + + my $offset = $trap & $offsetmask; + my $offsetr = $trap & $offsetrmask; + my $event_dir = $trap & $assertionmask; + my $event_type = ($trap & $eventtypemask) >> 8; + my $sensor_type = ($trap & $sensortypemask) >> 16; + my $reserved = ($trap & $reservedmask) >> 24; + + if($debug >= 2) { + printf("offset: %02xh\n",$offset); + printf("offsetr: %02xh\n",$offsetr); + printf("assertion: %02xh\n",$event_dir); + printf("eventtype: %02xh\n",$event_type); + printf("sensortype: %02xh\n",$sensor_type); + printf("reserved: %02xh\n",$reserved); + } + + my @hex = (0,@$pet); + my $pad = $hex[0]; + my @uuid = @hex[1..16]; + my @seqnum = @hex[17,18]; + my @timestamp = @hex[19,20,21,22]; + my @utcoffset = @hex[23,24]; + my $trap_source_type = $hex[25]; + my $event_source_type = $hex[26]; + my $sev = $hex[27]; + my $sensor_device = $hex[28]; + my $sensor_num = $hex[29]; + my $entity_id = $hex[30]; + my $entity_instance = $hex[31]; + my $event_data_1 = $hex[32]; + my $event_data_2 = $hex[33]; + my $event_data_3 = $hex[34]; + my @event_data = @hex[35..39]; + my $langcode = $hex[40]; + my $mfg_id = $hex[41] + $hex[42] * 0x100 + $hex[43] * 0x10000 + $hex[44] * 0x1000000; + my $prod_id = $hex[45] + $hex[46] * 0x100; + my @oem = $hex[47..@hex-1]; + + if($sev == 0x00) { + $sev = "LOG"; + } + elsif($sev == 0x01) { + $sev = "MONITOR"; + } + elsif($sev == 0x02) { + $sev = "INFORMATION"; + } + elsif($sev == 0x04) { + $sev = "OK"; + } + elsif($sev == 0x08) { + $sev = "WARNING"; + } + elsif($sev == 0x10) { + $sev = "CRITICAL"; + } + elsif($sev == 0x20) { + $sev = "NON-RECOVERABLE"; + } + else { + $sev = "UNKNOWN-SEVERITY:$sev"; + } + $text = "$sev:"; + + ($rc,$type,$desc) = getsensorevent($sensor_type,$offset,"ipmisensorevents"); + if($rc == 1) { + $type = "Unknown Type $sensor_type"; + $desc = "Unknown Event $offset"; + $rc = 0; + } + + if($event_type <= 0x0c) { + my $gtype; + my $gdesc; + ($rc,$gtype,$gdesc) = getsensorevent($event_type,$offset,"ipmigenericevents"); + if($rc == 1) { + $gtype = "Unknown Type $gtype"; + $gdesc = "Unknown Event $offset"; + $rc = 0; + } + + $desc = $gdesc; + } + + if($type eq "" || $type eq "-") { + $type = "OEM Sensor Type $sensor_type" + } + if($desc eq "" || $desc eq "-") { + $desc = "OEM Sensor Event $offset" + } + + if($type eq $desc) { + $desc = ""; + } + + my $extra_info = getaddsensorevent($sensor_type,$offset,$event_data_1,$event_data_2,$event_data_3); + if($extra_info) { + if($desc) { + $desc = "$desc $extra_info"; + } + else { + $desc = "$extra_info"; + } + } + + $text = "$text $type,"; + $text = "$text $desc"; + + my $key; + my $sensor_desc = sprintf("Sensor 0x%02x",$sensor_num); + foreach $key (keys %sdr_hash) { + my $sdr = $sdr_hash{$key}; + if($sdr->sensor_number == $sensor_num) { + $sensor_desc = $sdr_hash{$key}->id_string; + if($sdr->rec_type == 0x01) { + last; + } + } + } + + $text = "$text ($sensor_desc)"; + + if($event_dir) { + $text = "$text - Recovered"; + } + + return(0,$text); +} + +sub eventlog { + my $subcommand = shift; + + my $netfun = 0x28; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + my @output; + my $num; + my $entry; + my @sel; + #my $ipmisensoreventtab = "$ENV{XCATROOT}/lib/GUMI/ipmisensorevent.tab"; + #my $ipmigenericeventtab = "$ENV{XCATROOT}/lib/GUMI/ipmigenericevent.tab"; + my $mfg_id; + my $prod_id; + my $device_id; + + ($rc,$text,$mfg_id,$prod_id,$device_id) = getdevid(); + $rc=0; + if($subcommand eq "all") { + $num = 0x100 * 0x100; + } + elsif($subcommand eq "clear") { + } + elsif($subcommand =~ /^\d+$/) { + $num = $subcommand; + } + else { + return(1,"unsupported command eventlog $subcommand"); + } + + @cmd=(0x40); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + elsif($code == 0x81) { + $rc = 1; + $text = "cannot execute command, SEL erase in progress"; + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + my $sel_version = $returnd[37-$authoffset]; + if($sel_version != 0x51) { + $rc = 1; + $text = sprintf("SEL version 51h support only, version reported: %x",$sel_version); + return($rc,$text); + } + + my $num_entries = $returnd[39-$authoffset]*256 + $returnd[38-$authoffset]; + if($num_entries <= 0) { + $rc = 1; + $text = "no SEL entries"; + return($rc,$text); + } + + my $canres = $returnd[50-$authoffset] & 0b00000010; + if(!$canres) { + $rc = 1; + $text = "SEL reservation not supported"; + return($rc,$text); + } + + @cmd=(0x42); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + elsif($code == 0x81) { + $rc = 1; + $text = "cannot execute command, SEL erase in progress"; + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + my $res_id_ls = $returnd[37-$authoffset]; + my $res_id_ms = $returnd[38-$authoffset]; + + if($subcommand eq "clear") { + @cmd=(0x47,$res_id_ls,$res_id_ms,0x43,0x4c,0x52,0xaa); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + my $erase_status = $returnd[37-$authoffset] & 0b00000001; + +#skip test for now, need to get new res id for some machines + while($erase_status == 0 && 0) { + sleep(1); + @cmd=(0x47,$res_id_ls,$res_id_ms,0x43,0x4c,0x52,0x00); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + $erase_status = $returnd[37-$authoffset] & 0b00000001; + } + + $text = "SEL cleared"; + return($rc,$text); + } + + ($rc,$text) = initsdr(); + if($rc != 0) { + return($rc,$text); + } + + @cmd=(0x43,$res_id_ls,$res_id_ms,0x00,0x00,0x00,0xFF); + while(1) { + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + } + elsif($code == 0x81) { + $rc = 1; + $text = "cannot execute command, SEL erase in progress"; + } + else { + $rc = 1; + $text = $codes{$code}; + } + + if($rc != 0) { + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + my $next_rec_ls = $returnd[37-$authoffset]; + my $next_rec_ms = $returnd[38-$authoffset]; + my @sel_data = @returnd[39-$authoffset..39-$authoffset+16]; + @cmd=(0x43,$res_id_ls,$res_id_ms,$next_rec_ls,$next_rec_ms,0x00,0xFF); + + $entry++; + if($debug) { + print "$entry: "; + hexdump(\@sel_data); + } + + my $record_id = $sel_data[0] + $sel_data[1]*256; + my $record_type = $sel_data[2]; + + if($record_type == 0x02) { + } + else { + $text=getoemevent($record_type,$mfg_id,\@sel_data); + push(@output,$text); + if($next_rec_ms == 0xFF && $next_rec_ls == 0xFF) { + last; + } + next; + } + + my $timestamp = $sel_data[3] + $sel_data[4]*0x100 + $sel_data[5]*0x10000 + $sel_data[6]*0x1000000; + my ($seldate,$seltime) = timestamp2datetime($timestamp); +# $text = "$entry: $seldate $seltime"; + $text = ":$seldate $seltime"; + +# my $gen_id_slave_addr = ($sel_data[7] & 0b11111110) >> 1; +# my $gen_id_slave_addr_hs = ($sel_data[7] & 0b00000001); +# my $gen_id_ch_num = ($sel_data[8] & 0b11110000) >> 4; +# my $gen_id_ipmb = ($sel_data[8] & 0b00000011); + + my $sensor_owner_id = $sel_data[7]; + my $sensor_owner_lun = $sel_data[8]; + + my $sensor_type = $sel_data[10]; + my $sensor_num = $sel_data[11]; + my $event_dir = $sel_data[12] & 0b10000000; + my $event_type = $sel_data[12] & 0b01111111; + my $offset = $sel_data[13] & 0b00001111; + my $event_data_1 = $sel_data[13]; + my $event_data_2 = $sel_data[14]; + my $event_data_3 = $sel_data[15]; + my $sev = 0; + $sev = ($sel_data[14] & 0b11110000) >> 4; +# if($event_type != 1) { +# $sev = ($sel_data[14] & 0b11110000) >> 4; +# } +# $text = "$text $sev:"; + + my $type; + my $desc; + ($rc,$type,$desc) = getsensorevent($sensor_type,$offset,"ipmisensorevents"); + if($rc == 1) { + $type = "Unknown Type $sensor_type"; + $desc = "Unknown Event $offset"; + $rc = 0; + } + + if($event_type <= 0x0c) { + my $gtype; + my $gdesc; + ($rc,$gtype,$gdesc) = getsensorevent($event_type,$offset,"ipmigenericevents"); + if($rc == 1) { + $gtype = "Unknown Type $gtype"; + $gdesc = "Unknown Event $offset"; + $rc = 0; + } + + $desc = $gdesc; + } + + if($type eq "" || $type eq "-") { + $type = "OEM Sensor Type $sensor_type" + } + if($desc eq "" || $desc eq "-") { + $desc = "OEM Sensor Event $offset" + } + + if($type eq $desc) { + $desc = ""; + } + + my $extra_info = getaddsensorevent($sensor_type,$offset,$event_data_1,$event_data_2,$event_data_3); + if($extra_info) { + if($desc) { + $desc = "$desc $extra_info"; + } + else { + $desc = "$extra_info"; + } + } + + $text = "$text $type,"; + $text = "$text $desc"; + +# my $key; + my $key = $sensor_owner_id . "." . $sensor_owner_lun . "." . $sensor_num; + my $sensor_desc = sprintf("Sensor 0x%02x",$sensor_num); +# foreach $key (keys %sdr_hash) { +# my $sdr = $sdr_hash{$key}; +# if($sdr->sensor_number == $sensor_num) { +# $sensor_desc = $sdr_hash{$key}->id_string; +# last; +# } +# } + if(defined $sdr_hash{$key}) { + $sensor_desc = $sdr_hash{$key}->id_string; + } + + $text = "$text ($sensor_desc)"; + + if($event_dir) { + $text = "$text - Recovered"; + } + + push(@output,$text); + + if($next_rec_ms == 0xFF && $next_rec_ls == 0xFF) { + last; + } + } + + my @routput = reverse(@output); + my @noutput; + my $c; + foreach(@routput) { + $c++; + if($c > $num) { + last; + } + push(@noutput,$_); + } + @output = reverse(@noutput); + + return($rc,@output); +} +sub getoemevent { + my $record_type = shift; + my $mfg_id = shift; + my $sel_data = shift; + my $text=""; + if ($record_type < 0xE0 && $record_type > 0x2F) { #Should be timestampped, whatever it is + my $timestamp = @$sel_data[3] + @$sel_data[4]*0x100 + @$sel_data[5]*0x10000 + @$sel_data[6]*0x1000000; + my ($seldate,$seltime) = timestamp2datetime($timestamp); + my @rest = @$sel_data[7..15]; + if ($mfg_id==2) { + $text="$seldate $seltime IBM OEM Event-"; + if ($rest[3]==0 && $rest[4]==0 && $rest[7]==0) { + $text=$text."PCI Event/Error, details in next event" + } elsif ($rest[3]==1 && $rest[4]==0 && $rest[7]==0) { + $text=$text."Processor Event/Error occurred, details in next event" + } elsif ($rest[3]==2 && $rest[4]==0 && $rest[7]==0) { + $text=$text."Memory Event/Error occurred, details in next event" + } elsif ($rest[3]==3 && $rest[4]==0 && $rest[7]==0) { + $text=$text."Scalability Event/Error occurred, details in next event" + } elsif ($rest[3]==4 && $rest[4]==0 && $rest[7]==0) { + $text=$text."PCI bus Event/Error occurred, details in next event" + } elsif ($rest[3]==5 && $rest[4]==0 && $rest[7]==0) { + $text=$text."Chipset Event/Error occurred, details in next event" + } elsif ($rest[3]==6 && $rest[4]==1 && $rest[7]==0) { + $text=$text."BIOS/BMC Power Executive mismatch (BIOS $rest[5], BMC $rest[6])" + } elsif ($rest[3]==6 && $rest[4]==2 && $rest[7]==0) { + $text=$text."Boot denied due to power limitations" + } else { + $text=$text."Unknown event ". phex(\@rest); + } + } else { + $text = "$seldate $seltime " . sprintf("Unknown OEM SEL Type %02x:",$record_type) . phex(\@rest); + } + } else { #Non-timestamped + my %memerrors = ( + 0x00 => "DIMM enabled", + 0x01 => "DIMM disabled, failed ECC test", + 0x02 => "POST/BIOS memory test failed, DIMM disabled", + 0x03 => "DIMM disabled, non-supported memory device", + 0x04 => "DIMM disabled, non-matching or missing DIMM(s)", + ); + my %pcierrors = ( + 0x00 => "Device OK", + 0x01 => "Required ROM space not available", + 0x02 => "Required I/O Space not available", + 0x03 => "Required memory not available", + 0x04 => "Required memory below 1MB not available", + 0x05 => "ROM checksum failed", + 0x06 => "BIST failed", + 0x07 => "Planar device missing or disabled by user", + 0x08 => "PCI device has an invalid PCI configuration space header", + 0x09 => "FRU information for added PCI device", + 0x0a => "FRU information for removed PCI device", + 0x0b => "A PCI device was added, PCI FRU information is stored in next log entry", + 0x0c => "A PCI device was removed, PCI FRU information is stored in next log entry", + 0x0d => "Requested resources not available", + 0x0e => "Required I/O Space Not Available", + 0x0f => "Required I/O Space Not Available", + 0x10 => "Required I/O Space Not Available", + 0x11 => "Required I/O Space Not Available", + 0x12 => "Required I/O Space Not Available", + 0x13 => "Planar video disabled due to add in video card", + 0x14 => "FRU information for PCI device partially disabled ", + 0x15 => "A PCI device was partially disabled, PCI FRU information is stored in next log entry", + 0x16 => "A 33Mhz device is installed on a 66Mhz bus, PCI device information is stored in next log entry", + 0x17 => "FRU information, 33Mhz device installed on 66Mhz bus", + 0x18 => "Merge cable missing", + 0x19 => "Node 1 to Node 2 cable missing", + 0x1a => "Node 1 to Node 3 cable missing", + 0x1b => "Node 2 to Node 3 cable missing", + 0x1c => "Nodes could not merge", + 0x1d => "No 8 way SMP cable", + 0x1e => "Primary North Bridge to PCI Host Bridge IB Link has failed", + 0x1f => "Redundant PCI Host Bridge IB Link has failed", + ); + my %procerrors = ( + 0x00 => "Processor has failed BIST", + 0x01 => "Unable to apply processor microcode update", + 0x02 => "POST does not support current stepping level of processor", + 0x03 => "CPU mismatch detected", + ); + my @rest = @$sel_data[3..15]; + if ($record_type == 0xE0 && $rest[0]==2 && $mfg_id==2 && $rest[1]==0 && $rest[12]==1) { #Rev 1 POST memory event + $text="IBM Memory POST Event-"; + my $msuffix=sprintf(", chassis %d, card %d, dimm %d",$rest[3],$rest[4],$rest[5]); + #the next bit is a basic lookup table, should implement as a table ala ibmleds.tab, or a hash... yeah, a hash... + $text=$text.$memerrors{$rest[2]}.$msuffix; + } elsif ($record_type == 0xE0 && $rest[0]==1 && $mfg_id==2 && $rest[12]==0) { #A processor error or event, rev 0 only known in the spec I looked at + $text=$text.$procerrors{$rest[1]}; + } elsif ($record_type == 0xE0 && $rest[0]==0 && $mfg_id==2) { #A PCI error or event, rev 1 or 2, the revs differe in endianness + my $msuffix; + if ($rest[12]==0) { + $msuffix=sprintf("chassis %d, slot %d, bus %s, device %02x%02x:%02x%02x",$rest[2],$rest[3],$rest[4],$rest[5],$rest[6],$rest[7],$rest[8]); + } elsif ($rest[12]==1) { + $msuffix=sprintf("chassis %d, slot %d, bus %s, device %02x%02x:%02x%02x",$rest[2],$rest[3],$rest[4],$rest[5],$rest[6],$rest[7],$rest[8]); + } else { + return ("Unknown IBM PCI event/error format"); + } + $text=$text.$pcierrors{$rest[1]}.$msuffix; + } else { + #Some event we can't define that is OEM or some otherwise unknown event + $text = sprintf("SEL Type %02x:",$record_type) . phex(\@rest); + } + } #End timestampped intepretation + return ($text); +} + +sub getsensorevent +{ + my $sensortype = sprintf("%02Xh",shift); + my $sensoroffset = sprintf("%02Xh",shift); + my $file = shift; + + my @line; + my $type; + my $code; + my $desc; + my $offset; + my $rc = 1; + + if ($file eq "ipmigenericevents") { + if ($xCAT::data::ipmigenericevents::ipmigenericevents{"$sensortype,$sensoroffset"}) { + ($type,$desc) = split (/,/,$xCAT::data::ipmigenericevents::ipmigenericevents{"$sensortype,$sensoroffset"},2); + return(0,$type,$desc); + } + if ($xCAT::data::ipmigenericevents::ipmigenericevents{"$sensortype,-"}) { + ($type,$desc) = split (/,/,$xCAT::data::ipmigenericevents::ipmigenericevents{"$sensortype,-"},2); + return(0,$type,$desc); + } + } + if ($file eq "ipmisensorevents") { + if ($xCAT::data::ipmisensorevents::ipmisensorevents{"$sensortype,$sensoroffset"}) { + ($type,$desc) = split (/,/,$xCAT::data::ipmisensorevents::ipmisensorevents{"$sensortype,$sensoroffset"},2); + return(0,$type,$desc); + } + if ($xCAT::data::ipmisensorevents::ipmisensorevents{"$sensortype,-"}) { + ($type,$desc) = split (/,/,$xCAT::data::ipmisensorevents::ipmisensorevents{"$sensortype,-"},2); + return(0,$type,$desc); + } + } + return (0,"No Mappings found","No Mappings found"); +} + +sub getaddsensorevent { + my $sensor_type = shift; + my $offset = shift; + my $event_data_1 = shift; + my $event_data_2 = shift; + my $event_data_3 = shift; + my $text = ""; + + if($sensor_type == 0x0f) { + if($offset == 0x00) { + my %extra = ( + 0x00 => "Unspecified", + 0x01 => "No system memory installed", + 0x02 => "No usable system memory", + 0x03 => "Unrecoverable hard disk failure", + 0x04 => "Unrecoverable system board failure", + 0x05 => "Unrecoverable diskette failure", + 0x06 => "Unrecoverable hard disk controller failure", + 0x07 => "Unrecoverable keyboard failure", + 0x08 => "Removable boot media not found", + 0x09 => "Unrecoverable video controller failure", + 0x0a => "No video device detected", + 0x0b => "Firmware (BIOS) ROM corruption detected", + 0x0c => "CPU voltage mismatch", + 0x0d => "CPU speed mitching failure", + ); + $text = $extra{$event_data_2}; + } + if($offset == 0x02) { + my %extra = ( + 0x00 => "Unspecified", + 0x01 => "Memory initialization", + 0x02 => "Hard-disk initialization", + 0x03 => "Secondary processor(s) initialization", + 0x04 => "User authentication", + 0x05 => "User-initiated system setup", + 0x06 => "USB resource configuration", + 0x07 => "PCI resource configuration", + 0x08 => "Option ROM initialization", + 0x09 => "Video initialization", + 0x0a => "Cache initialization", + 0x0b => "SM Bus initialization", + 0x0c => "Keyboard controller initialization", + 0x0d => "Embedded controller/management controller initialization", + 0x0e => "Docking station attachement", + 0x0f => "Enabling docking station", + 0x10 => "Docking staion ejection", + 0x11 => "Disable docking station", + 0x12 => "Calling operation system wake-up vector", + 0x13 => "Starting operation system boot process, call init 19h", + 0x14 => "Baseboard or motherboard initialization", + 0x16 => "Floppy initialization", + 0x17 => "Keyboard test", + 0x18 => "Pointing device test", + 0x19 => "Primary processor initialization", + ); + $text = $extra{$event_data_2}; + } + } + if($sensor_type == 0x12) { + if($offset == 0x03) { + } + if($offset == 0x04) { + my %extra = ( + 0x00 => "Alert", + 0x01 => "power off", + 0x02 => "reset", + 0x04 => "power cycle", + 0x08 => "OEM action", + 0x10 => "NMI", + ); + if($event_data_2 & 0b00100000) { + $text = "$text, NMI"; + } + if($event_data_2 & 0b00010000) { + $text = "$text, OEM action"; + } + if($event_data_2 & 0b00001000) { + $text = "$text, power cycle"; + } + if($event_data_2 & 0b00000100) { + $text = "$text, reset"; + } + if($event_data_2 & 0b00000010) { + $text = "$text, power off"; + } + if($event_data_2 & 0b00000001) { + $text = "$text, Alert"; + } + $text =~ s/^, //; + } + } + + return($text); +} + +sub checkleds { + my $netfun = 0xe8; #really 0x3a + my @cmd; + my @returnd = (); + my $error; + my $led_id_ms; + my $led_id_ls; + my $rc = 0; + my @output =(); + my $text=""; + my $key; + my $mfg_id; + my $prod_id; + ($rc,$text,$mfg_id,$prod_id) = getdevid(); + if ($mfg_id != 2) { + return (0,"LED status only supported on IBM hardware"); + } + + ($rc,$text) = initsdr(); + if($rc != 0) { + return($rc,$text); + } + foreach $key (sort {$sdr_hash{$a}->id_string cmp $sdr_hash{$b}->id_string} keys %sdr_hash) { + my $sdr = $sdr_hash{$key}; + if($sdr->sensor_type == 0xED && $sdr->rec_type == 0xC0) { + #this stuff is to help me build the file from spec paste + #my $tehstr=sprintf("grep 0x%04X /opt/xcat/lib/x3755led.tab",$sdr->led_id); + #my $tehstr=`$tehstr`; + #$tehstr =~ s/^0x....//; + + #printf("%X.%X.0x%04x",$mfg_id,$prod_id,$sdr->led_id); + #print $tehstr; + + #We are inconsistant in our spec, first try a best guess + #at endianness, assume the smaller value is MSB + if (($sdr->led_id&0xff) > ($sdr->led_id>>8)) { + $led_id_ls=$sdr->led_id&0xff; + $led_id_ms=$sdr->led_id>>8; + } else { + $led_id_ls=$sdr->led_id>>8; + $led_id_ms=$sdr->led_id&0xff; + } + + @cmd=(0xc0,$led_id_ms,$led_id_ls); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + if ($returnd[36-$authoffset] == 0xc9) { + my $tmp; + #we probably guessed endianness wrong. + $tmp=$led_id_ls; + $led_id_ls=$led_id_ms; + $led_id_ms=$tmp; + @cmd=(0xc0,$led_id_ms,$led_id_ls); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + } + + if ($returnd[38-$authoffset] != 0) { + #It's on... + if ($returnd[42-$authoffset] == 4) { + push(@output,sprintf("LED 0x%02x%02x (%s) active to indicate BIOS detected error (or user requested LED activity)",$led_id_ms,$led_id_ls,getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds"))); + } + elsif ($returnd[42-$authoffset] == 3) { + push(@output,sprintf("A user has manually requested LED 0x%04x (%s) be active",$sdr->led_id,getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds"))); + } + elsif ($returnd[42-$authoffset] == 1 && $sdr->led_id !=0) { + push(@output,sprintf("LED 0x%02x%02x (%s) active to indicate LED 0x%02x%02x (%s) is active",$led_id_ms,$led_id_ls,getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds"),$returnd[40-$authoffset],$returnd[41-$authoffset],getsensorname($mfg_id,$prod_id,($returnd[40-$authoffset]<<8)+$returnd[41-$authoffset],"ibmleds"))); + } + elsif ($sdr->led_id ==0) { + push(@output,sprintf("LED 0x0000 (%s) active to indicate system error condition.",getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds"))); + } + elsif ($returnd[42-$authoffset] == 2) { + my $sensor_desc; + #Ok, LED is tied to a sensor.. + my $sensor_num=$returnd[41-$authoffset]; + foreach $key (keys %sdr_hash) { + my $osdr = $sdr_hash{$key}; + if($osdr->sensor_number == $sensor_num) { + $sensor_desc = $sdr_hash{$key}->id_string; + if($osdr->rec_type == 0x01) { + last; + } + } + } + $rc=0; + #push(@output,sprintf("Sensor 0x%02x (%s) has activated LED 0x%04x",$sensor_num,$sensor_desc,$sdr->led_id)); + push(@output,sprintf("LED 0x%02x%02x active to indicate Sensor 0x%02x (%s) error.",$led_id_ms,$led_id_ls,$sensor_num,$sensor_desc)); + } + } + + } + } + if ($#output==-1) { + push(@output,"No active error LEDs detected"); + } + return($rc,@output); +} + +sub vitals { + my $subcommand = shift; + + my $rc = 0; + my $text; + my $key; + my @sensor_filters=(0x00); + my @output; + my $reading; + my $unitdesc; + my $value; + my $format = "%-30s%8s %-20s"; + my $per = " "; + $rc=0; + + if($subcommand eq "all") { + @sensor_filters=(0x01,0x02,0x03,0x04); + } + elsif($subcommand =~ /temp/) { + @sensor_filters=(0x01); + } + elsif($subcommand eq "voltage") { + @sensor_filters=(0x02,0x03); + } + elsif($subcommand eq "fanspeed") { + @sensor_filters=(0x04); + } + elsif($subcommand eq "power") { + ($rc,$text) = power("stat"); + $text = sprintf($format,"Power Status:",$text); + return($rc,$text); + } + elsif($subcommand eq "leds") { + my @cleds; + ($rc,@cleds) = checkleds(); + foreach $text (@cleds) { + push(@output,$text); + } + } + else { + return(1,"unsupported command vitals $subcommand"); + } + + ($rc,$text) = initsdr(); + if($rc != 0) { + return($rc,$text); + } + + foreach(@sensor_filters) { + my $filter = $_; + + foreach $key (sort {$sdr_hash{$a}->id_string cmp $sdr_hash{$b}->id_string} keys %sdr_hash) { + my $sdr = $sdr_hash{$key}; + if($sdr->sensor_type == $filter && $sdr->rec_type == 0x01) { + my $lformat = $format; + + ($rc,$reading) = readsensor($sdr->sensor_number); + $unitdesc = ""; + if($rc == 0) { + $unitdesc = $units{$sdr->sensor_units_2}; + + $value = (($sdr->M * $reading) + ($sdr->B * (10**$sdr->B_exp))) * (10**$sdr->R_exp); + if($sdr->linearization == 0) { + $reading = $value; + if($value == int($value)) { + $lformat = "%-30s%8d%-20s"; + } + else { + $lformat = "%-30s%8.3f%-20s"; + } + } + elsif($sdr->linearization == 7) { + if($value > 0) { + $reading = 1/$value; + } + else { + $reading = 0; + } + $lformat = "%-30s%8d %-20s"; + } + else { + $reading = "RAW($sdr->linearization) $reading"; + } + + if($sdr->sensor_units_1 & 1) { + $per = "% "; + } + + if($unitdesc eq "C") { + my $f = ($reading * 9/5) + 32; + $unitdesc = "C (" . int($f + .5) . " F)"; + } + if($unitdesc eq "F") { + my $c = ($reading - 32) * 5/9; + $unitdesc = "F (" . int($c + .5) . " C)"; + } + } + $text = sprintf($lformat,$sdr->id_string . ":",$reading,$per.$unitdesc); + push(@output,$text); + } +# else { +# printf("%x %s %d\n",$sdr->sensor_number,$sdr->id_string,$sdr->sensor_type); +# } + } + } + + if($subcommand eq "all") { + my @cleds; + ($rc,$text) = power("stat"); + $text = sprintf($format,"Power Status:",$text); + push(@output,$text); + ($rc,@cleds) = checkleds(); + foreach $text (@cleds) { + push(@output,$text); + } + } + + return($rc,@output); +} + +sub readsensor { + my $sensor = shift; + my $netfun = 0x10; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + @cmd = (0x2d,$sensor); + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + if($code != 0x00) { + $rc = 1; + $text = $codes{$code}; + + if(!$text) { + $text = sprintf("unknown response %02x",$code); + } + chomp $text; + + return($rc,$text); + } + + if ($returnd[38-$authoffset] & 0x20) { + $rc = 1; + $text = "N/A"; + return($rc,$text); + } + $text = $returnd[37-$authoffset]; + + return($rc,$text); +} + +sub initsdr { + my $netfun; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + my $sdr_rep_info = SDR_rep_info->new(); + + my $resv_id_ls; + my $resv_id_ms; + my $nrid_ls = 0; + my $nrid_ms = 0; + my $rid_ls = 0; + my $rid_ms = 0; + my $sdr_ver; + my $sdr_type; + my $sdr_offset; + my $sdr_len; + my @sdr_data = (); + my $offset; + my $len; + my $i; +# my $numbytes = 27; + my $numbytes = 22; + my $override_string; + my $ipmisensortab = "$ENV{XCATROOT}/lib/GUMI/ipmisensor.tab"; + my $byte_format; + my $cache_file; + + my $mfg_id; + my $prod_id; + my $device_id; + my $dev_rev; + my $fw_rev1; + my $fw_rev2; + + ($rc,$text,$mfg_id,$prod_id,$device_id,$dev_rev,$fw_rev1,$fw_rev2) = getdevid(); + if($rc != 0) { + return($rc,$text); + } + + $cache_file = "$cache_dir/sdr_$mfg_id.$prod_id.$device_id.$dev_rev.$fw_rev1.$fw_rev2.$cache_version"; + if($enable_cache eq "yes") { + $rc = loadsdrcache($cache_file); + if($rc == 0) { + return($rc); + } + $rc = 0; + } + + ($rc,$text) = get_sdr_rep_info($sdr_rep_info); + if($rc != 0) { + return($rc,$text); + } + + if($sdr_rep_info->version != 0x51) { + $rc = 1; + $text = "SDR version 51h support only."; + return($rc,$text); + } + + if($sdr_rep_info->resv_sdr != 1) { + $rc = 1; + $text = "SDR reservation unsupported."; + return($rc,$text); + } + + ($rc,$text,$resv_id_ls,$resv_id_ms) = resv_sdr_repo(); + if($rc != 0) { + return($rc,$text); + } + + if($debug) { + print "mfg,prod,dev: $mfg_id, $prod_id, $device_id\n"; + printf("SDR info: %02x %d %d\n",$sdr_rep_info->version,$sdr_rep_info->rec_count,$sdr_rep_info->resv_sdr); + print "resv_id: $resv_id_ls $resv_id_ms\n"; + } + + foreach(1..$sdr_rep_info->rec_count) { + $netfun = 0x28; + @cmd = (0x23,$resv_id_ls,$resv_id_ms,$nrid_ls,$nrid_ms,0,5); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + if($code != 0x00) { + $rc = 1; + $text = $codes{$code}; + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } +###x336 hack + $rid_ls = $nrid_ls; + $rid_ms = $nrid_ms; +### + $nrid_ls = $returnd[37-$authoffset]; + $nrid_ms = $returnd[38-$authoffset]; +### correct IPMI code +# $rid_ls = $returnd[39-$authoffset]; +# $rid_ms = $returnd[40-$authoffset]; +### + $sdr_ver = $returnd[41-$authoffset]; + $sdr_type = $returnd[42-$authoffset]; + $sdr_len = $returnd[43-$authoffset] + 5; + + if($sdr_type == 0x01) { + $sdr_offset = 0; + } + elsif($sdr_type == 0x02) { + $sdr_offset = 16; + } + elsif($sdr_type == 0xC0) { + #LED descriptor, maybe + } + elsif($sdr_type == 0x12) { + next; + } + else { + next; + } + + @sdr_data = (0,0,0,$sdr_ver,$sdr_type,$sdr_len); + $offset = 5; + for($i=5;$i<$sdr_len;$i+=$numbytes) { + $len = $numbytes; + if($offset+$len > $sdr_len) { + $len = $sdr_len - $offset; + } + + @cmd = (0x23,$resv_id_ls,$resv_id_ms,$rid_ls,$rid_ms,$offset,$len); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + if($code != 0x00) { + $rc = 1; + $text = $codes{$code}; + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + @sdr_data = (@sdr_data,@returnd[39-$authoffset..@returnd-2]); + + $offset += $len; + } + + if($debug) { + hexadump(\@sdr_data); + } + if($sdr_type == 0x12) { + hexadump(\@sdr_data); + next; + } + + my $sdr = SDR->new(); + + if ($mfg_id == 2 && $sdr_type==0xC0 && $sdr_data[9] == 0xED) { + #printf("%02x%02x\n",$sdr_data[13],$sdr_data[12]); + $sdr->rec_type($sdr_type); + $sdr->sensor_type($sdr_data[9]); + #Using an impossible sensor number to not conflict with decodealert + $sdr->sensor_owner_id(260); + $sdr->sensor_owner_lun(260); + if ($sdr_data[12] > $sdr_data[13]) { + $sdr->led_id(($sdr_data[13]<<8)+$sdr_data[12]); + } else { + $sdr->led_id(($sdr_data[12]<<8)+$sdr_data[13]); + } + #$sdr->led_id_ms($sdr_data[13]); + #$sdr->led_id_ls($sdr_data[12]); + $sdr->sensor_number(sprintf("%04x",$sdr->led_id)); + #printf("%02x,%02x,%04x\n",$mfg_id,$prod_id,$sdr->led_id); + #Was going to have a human readable name, but specs + #seem to not to match reality... + #$override_string = getsensorname($mfg_id,$prod_id,$sdr->sensor_number,$ipmiledtab); + #I'm hacking in owner and lun of 260 for LEDs.... + $sdr_hash{"260.260.".$sdr->led_id} = $sdr; + next; + } + + + $sdr->rec_type($sdr_type); + $sdr->sensor_owner_id($sdr_data[6]); + $sdr->sensor_owner_lun($sdr_data[7]); + $sdr->sensor_number($sdr_data[8]); + $sdr->entity_id($sdr_data[9]); + $sdr->entity_instance($sdr_data[10]); + $sdr->sensor_type($sdr_data[13]); + $sdr->sensor_units_1($sdr_data[21]); + $sdr->sensor_units_2($sdr_data[22]); + $sdr->sensor_units_3($sdr_data[23]); + + if($sdr_type == 0x01) { + $sdr->linearization($sdr_data[24] & 0b01111111); + $sdr->M(comp2int(10,(($sdr_data[26] & 0b11000000) << 2) + $sdr_data[25])); + $sdr->B(comp2int(10,(($sdr_data[28] & 0b11000000) << 2) + $sdr_data[27])); + $sdr->R_exp(comp2int(4,($sdr_data[30] & 0b11110000) >> 4)); + $sdr->B_exp(comp2int(4,$sdr_data[30] & 0b00001111)); + } + + $sdr->id_string_type($sdr_data[48-$sdr_offset]); + + $override_string = getsensorname($mfg_id,$prod_id,$sdr->sensor_number,$ipmisensortab); + + if($override_string ne "") { + $sdr->id_string($override_string); + } + else { + $byte_format = ($sdr->id_string_type & 0b11000000) >> 6; + if($byte_format == 0b11) { + my $len = ($sdr->id_string_type & 0b00011111) - 1; + if($len > 1) { + $sdr->id_string(pack("C*",@sdr_data[49-$sdr_offset..49-$sdr_offset+$len])); + } + else { + $sdr->id_string("no description"); + } + } + elsif($byte_format == 0b10) { + $sdr->id_string("ASCII packed unsupported"); + } + elsif($byte_format == 0b01) { + $sdr->id_string("BCD unsupported"); + } + elsif($byte_format == 0b00) { + $sdr->id_string("unicode unsupported"); + } + } + + $sdr_hash{$sdr->sensor_owner_id . "." . $sdr->sensor_owner_lun . "." . $sdr->sensor_number} = $sdr; + } + + if($debug) { + my $key; +# foreach $key (sort {$sdr_hash{$a}->sensor_number <=> $sdr_hash{$b}->sensor_number} keys %sdr_hash) { + foreach $key (sort {$sdr_hash{$a}->id_string cmp $sdr_hash{$b}->id_string} keys %sdr_hash) { + my $sdr = $sdr_hash{$key}; +# printf("%d %x %s\n",$sdr->rec_type,$sdr->sensor_number,$sdr->id_string); +# printf("%x %x %x %s\n",$sdr->sensor_owner_id,$sdr->sensor_owner_lun,$sdr->sensor_number,$sdr->id_string); + printf("%x %x %x %s %d\n",$sdr->sensor_owner_id,$sdr->sensor_owner_lun,$sdr->sensor_number,$sdr->id_string,$sdr->linearization); + } +# printf("\n%x %s\n",$sdr_hash{0x70}->sensor_number,$sdr_hash{0x70}->id_string); + } + + if($enable_cache eq "yes") { + storsdrcache($cache_file); + } + + return($rc,$text); +} + +sub getsensorname +{ + my $mfgid = shift; + my $prodid = shift; + my $sensor = shift; + my $file = shift; + + my $mfg; + my $prod; + my $type; + my $num; + my $desc; + my $name=""; + + if ($file eq "ibmleds") { + if ($xCAT::data::ibmleds::leds{"$mfgid,$prodid"}->{$sensor}) { + return $xCAT::data::ibmleds::leds{"$mfgid,$prodid"}->{$sensor}; + } else { + return ""; + } + } else { + return ""; + } +} + +sub getchassiscap { + my $netfun = 0x00; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + @cmd = (0x00); + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + + $code = $returnd[36-$authoffset]; + if($code == 0x00) { + $text = ""; + } + else { + $rc = 1; + $text = $codes{$code}; + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + + return($rc,@returnd[37-$authoffset..@returnd-2]); +} + +sub getdevid { + my $netfun = 0x18; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + @cmd = (0x01); + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + else { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $text = ""; + } + else { + $rc = 1; + $text = $codes{$code}; + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + } + + my $device_id = $returnd[37-$authoffset]; + my $device_rev = $returnd[38-$authoffset] & 0b00001111; + my $firmware_rev1 = $returnd[39-$authoffset] & 0b01111111; + my $firmware_rev2 = $returnd[40-$authoffset]; + my $ipmi_ver = $returnd[41-$authoffset]; + my $dev_support = $returnd[42-$authoffset]; + my $sensor_device = 0; + my $SDR = 0; + my $SEL = 0; + my $FRU = 0; + my $IPMB_ER = 0; + my $IPMB_EG = 0; + my $BD = 0; + my $CD = 0; + if($dev_support & 0b00000001) { + $sensor_device = 1; + } + if($dev_support & 0b00000010) { + $SDR = 1; + } + if($dev_support & 0b00000100) { + $SEL = 1; + } + if($dev_support & 0b00001000) { + $FRU = 1; + } + if($dev_support & 0b00010000) { + $IPMB_ER = 1; + } + if($dev_support & 0b00100000) { + $IPMB_EG = 1; + } + if($dev_support & 0b01000000) { + $BD = 1; + } + if($dev_support & 0b10000000) { + $CD = 1; + } + my $mfg_id = $returnd[43-$authoffset] + $returnd[44-$authoffset]*0x100 + $returnd[45-$authoffset]*0x10000; + my $prod_id = $returnd[46-$authoffset] + $returnd[47-$authoffset]*0x100; + my @data = @returnd[48-$authoffset..@returnd-2]; + + return($rc,$text,$mfg_id,$prod_id,$device_id,$device_rev,$firmware_rev1,$firmware_rev2); +} + +sub getguid { + my $guidcmd = shift; + my $netfun = @$guidcmd[0] || 0x18; + my @cmd = @$guidcmd[1] || 0x37; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + else { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $text = ""; + } + else { + $rc = 1; + $text = $codes{$code}; + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + } + + my @guid = @returnd[37-$authoffset..52-$authoffset]; + my $guidtext = sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",@guid); + $guidtext =~ tr/[a-z]/[A-Z]/; + + return($rc,$text,$guidtext); +} + +sub get_sdr_rep_info { + my $sdr_rep_info = shift; + + my $netfun = 0x28; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + @cmd = (0x20); + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + else { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $text = ""; + } + else { + $rc = 1; + $text = $codes{$code}; + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + } + + $sdr_rep_info->version($returnd[37-$authoffset]); + $sdr_rep_info->rec_count($returnd[38-$authoffset] + $returnd[39-$authoffset]*0x100); + $sdr_rep_info->resv_sdr(($returnd[50-$authoffset] & 0b00000010) ? 1 : 0); + + return($rc,$text); +} + +sub resv_sdr_repo { + my $netfun = 0x28; + my @cmd; + my @returnd = (); + my $error; + my $rc = 0; + my $text; + my $code; + + @cmd = (0x22); + + $error = docmd( + $netfun, + \@cmd, + \@returnd + ); + + if($error) { + $rc = 1; + $text = $error; + return($rc,$text); + } + else { + $code = $returnd[36-$authoffset]; + + if($code == 0x00) { + $text = ""; + } + else { + $rc = 1; + $text = $codes{$code}; + if(!$text) { + $rc = 1; + $text = sprintf("unknown response %02x",$code); + } + return($rc,$text); + } + } + + my $resv_id_ls = $returnd[37-$authoffset]; + my $resv_id_ms = $returnd[38-$authoffset]; + + return($rc,$text,$resv_id_ls,$resv_id_ms); +} + +sub docmd { + my $netfun = shift; + my $cmd = shift; + my $response = shift; + + my @rn; + my $length; + + my @msg; + my @message; + my $error = ""; + my @response; + my @data; + + incseqlun(); + + @data = ($rqsa,$seqlun,@$cmd); + @rn = ($rssa,$netfun); + $length = (scalar @data)+4; + @message = ($rssa,$netfun,dochksum(\@rn),@data,dochksum(\@data)); + + incseqnum(); + + @msg = ( + @rmcp, + $auth, + @seqnum, + @session_id, + authcode(2,\@message), + $length, + @message + ); + + ($error,@$response) = domsg($sock,\@msg,$timeout,1); + + return($error); +} + +sub getchanauthcap { + $auth = 0x00; + my $netfun = 0x18; + my @data; + my @rn; + my $length; + + my @msg; + my $error = ""; + my @response; + my $code; + + @data = ($rqsa,$seqlun,0x38,0x0e,0x04); + @rn = ($rssa,$netfun); + $length = (scalar @data)+4; + + @msg = ( + @rmcp, + $auth, + @seqnum, + @session_id, + $length, + $rssa, + $netfun, + dochksum(\@rn), + @data, + dochksum(\@data) + ); + + ($error,@response) = domsg($sock,\@msg,$timeout,0); + + if($error) { + return($error); + } + + $code = $response[20]; + if($code != 0x00) { + $error = $codes{$code}; + if(!$error) { + $error = "Unknown get channel authentication capabilities error $code" + } + return($error); + } + + $channel_number=$response[21]; + + if($response[22] & 0b00000100) { + $auth=0x02; + } + elsif($response[22] & 0b00010000) { + $auth=0x04; + } + else { + $error = "unsupported Authentication Type Support"; + } + + return($error); +} + +sub getsessionchallenge { + my $tauth = 0x00; + my $netfun = 0x18; + my @data; + my @rn; + my $length; + + my @msg; + my $error = ""; + my @response; + my $code; + + incseqlun(); + + @data = ($rqsa,$seqlun,0x39,$auth,@user); + @rn = ($rssa,$netfun); + $length = (scalar @data)+4; + + @msg = ( + @rmcp, + $tauth, + @seqnum, + @session_id, + $length, + $rssa, + $netfun, + dochksum(\@rn), + @data, + dochksum(\@data) + ); + + ($error,@response) = domsg($sock,\@msg,$timeout,0); + + if(!$error) { + $code = $response[20]; + if($code != 0x00) { + $error = $codes{$code}; + if(!$error) { + $error = "Unknown get session challenge error $code" + } + } + + if($code == 0x81) { + $error = "Invalid user name"; + } + elsif($code == 0x82) { + $error = "null user name not enabled"; + } + + @session_id = @response[21,22,23,24]; + + for (my $i=0;$i<16;$i++){ + $challenge[$i] = $response[25+$i]; + } + } + + return($error); +} + +sub activatesession { + my $netfun = 0x18; + my @data; + my @rn; + my $length; + + my @msg; + my @message; + my $error = ""; + my @response; + my $code; + + incseqlun(); + + @data = ($rqsa,$seqlun,0x3A,$auth,0x04,@challenge,0x01,0x00,0x00,0x00); + @rn = ($rssa,$netfun); + $length = (scalar @data)+4; + @message = ($rssa,$netfun,dochksum(\@rn),@data,dochksum(\@data)); + + @msg = ( + @rmcp, + $auth, + @seqnum, + @session_id, + authcode(2,\@message), + $length, + @message + ); + + ($error,@response) = domsg($sock,\@msg,$timeout,0); + + if(!$error) { + $code = $response[36]; + if($code != 0x00) { + $error = $codes{$code}; + if(!$error) { + $error = "Unknown activate session error $code" + } + } + + if($code == 0x81) { + $error = "No session slot available"; + } + elsif($code == 0x82) { + $error = "No slot available for given user"; + } + elsif($code == 0x83) { + $error = "No slot available to support user due to maximum privilege capability"; + } + elsif($code == 0x84) { + $error = "Session sequence number out-of-range"; + } + elsif($code == 0x85) { + $error = "Invalid session ID in request"; + } + elsif($code == 0x86) { + $error = "Requested maximum privilege level exceeds user and/of channel privilege limit"; + } + + $auth = $response[37]; + if($auth == 0x00) { + $authoffset=16; + } + elsif($auth == 0x02) { + } + elsif($auth == 0x04) { + } + else { + $error = "activate session requested unsupported Authentication Type Support"; + } + +###check + @session_id = @response[38,39,40,41]; + @seqnum = @response[42,43,44,45]; + } + + return($error); +} + +sub setprivlevel() +{ + my $netfun = 0x18; + my @data; + my @rn; + my $length; + + my @msg; + my @message; + my $error = ""; + my @response; + my $code; + + incseqlun(); + + @data = ($rqsa,$seqlun,0x3B,0x04); + @rn = ($rssa,$netfun); + $length = (scalar @data)+4; + @message = ($rssa,$netfun,dochksum(\@rn),@data,dochksum(\@data)); + + @msg = ( + @rmcp, + $auth, + @seqnum, + @session_id, + authcode(2,\@message), + $length, + @message + ); + + ($error,@response) = domsg($sock,\@msg,$timeout,1); + + if(!$error) { + $code = $response[36-$authoffset]; + if($code != 0x00) { + $error = $codes{$code}; + if(!$error) { + $error = "Unknown set session privilege level error $code" + } + } + + if($code == 0x80) { + $error = "Requested level not available for this user"; + } + elsif($code == 0x81) { + $error = "Requested level exceeds channel and/or user privilege limit"; + } + elsif($code == 0x82) { + $error = "Cannot disable user level authentication"; + } + } + + return($error); +} + +sub closesession() +{ + incseqnum(); + + my $netfun = 0x18; + my @data; + my @rn; + my $length; + + my @msg; + my @message; + my $error = ""; + my @response; + my $code; + + incseqlun(); + + @data = ($rqsa,$seqlun,0x3C,@session_id); + @rn = ($rssa,$netfun); + $length = (scalar @data)+4; + @message = ($rssa,$netfun,dochksum(\@rn),@data,dochksum(\@data)); + + @msg = ( + @rmcp, + $auth, + @seqnum, + @session_id, + authcode(2,\@message), + $length, + @message + ); + + ($error,@response) = domsg($sock,\@msg,$timeout,1); + + if(!$error) { + $code = $response[36-$authoffset]; + if($code != 0x00) { + $error = $codes{$code}; + if(!$error) { + $error = "Unknown close session error $code" + } + } + + if($code == 0x87) { + $error = "Invalid session ID in request"; + } + } + + return($error); +} + +sub domsg { + my $sock = shift; + my $msg = shift; + my $timeout = shift; + my $seq = shift || 0; + my $debug = $localdebug; + my $trys = $localtrys; + my $send; + my $quit = 0; + my $error=""; + my $recv; + my @response; + my $timedout; + my @foo; + my @message; + + $send = pack('C*',@$msg); + + while($trys > 0) { + $trys--; + $error = ""; + $timedout = 0; + + if($debug) { + print "try: $trys, timeout: $timeout\n"; + } + + if(!$sock->send($send)) { + $error = $!; + sleep(1); + next; + } + my $s = IO::Select->new($sock); + #local $SIG{ALRM} = sub { $timedout = 1 and die }; + #alarm($timeout); + my $received = $s->can_read($timeout); + if($received > 0) { + if ($sock->recv($recv,128)) { + if($recv) { + @response = unpack("C*",$recv); + last; + } + } else { + $error = $!; + } + } + else { + $error = "timeout"; + } + +###ugly updated hack to support md5. + if($seq) { + incseqnum(); + + @$msg[5..8] = @seqnum[0..3]; + @message = @$msg[30..@$msg-1]; + if($auth != 0x00) { + @$msg[13..28] = authcode(2,\@message); + } + + $send = pack('C*',@$msg); + } + } + + if($timedout == 1) { + if($error) { + $error = "timeout $error" + } + else { + $error = "timeout" + } + } + + return($error,@response); +} + +sub dochksum() +{ + my $data = shift; + my $sum = 0; + + foreach(@$data) { + $sum += $_; + } + + $sum = ~$sum + 1; + return($sum & 0xFF); +} + +sub dopad16 { + my @pad16 = unpack("C*",shift); + + for(my $i=@pad16;$i<16;$i++) { + $pad16[$i] = 0; + } + + return(@pad16); +} + +sub hexdump { + my $data = shift; + + foreach(@$data) { + printf("%02x ",$_); + } + print "\n"; +} + +sub getascii { + my @alpha; + my $text =""; + my $c = 0; + + foreach(@_) { + $alpha[$c] = sprintf("%c",$_); + if($alpha[$c] !~ /[\/\w\-:]/) { + if ($alpha[($c-1)] !~ /\s/) { + $alpha[$c] = " "; + } else { + $c--; + } + } + $c++; + } + foreach(@alpha) { + $text=$text.$_; + } + $text =~ s/^\s+|\s+$//; + return $text; +} +sub phex { + my $data = shift; + my @alpha; + my $text =""; + my $c = 0; + + foreach(@$data) { + $text = $text . sprintf("%02x ",$_); + $alpha[$c] = sprintf("%c",$_); + if($alpha[$c] !~ /\w/) { + $alpha[$c] = " "; + } + $c++; + } + $text = $text . "("; + foreach(@alpha) { + $text=$text.$_; + } + $text = $text . ")"; + return $text; +} + +sub hexadump { + my $data = shift; + my @alpha; + my $c = 0; + + foreach(@$data) { + printf("%02x ",$_); + $alpha[$c] = sprintf("%c",$_); + if($alpha[$c] !~ /\w/) { + $alpha[$c] = "."; + } + $c++; + if($c == 16) { + print " "; + foreach(@alpha) { + print $_; + } + print "\n"; + @alpha=(); + $c=0; + } + } + foreach($c..16) { + print " "; + } + foreach(@alpha) { + print $_; + } + print "\n"; +} + +sub incseqnum { + my $i; + + for($i = 0;$i < 4;$i++) { + if($seqnum[$i] < 0xFF) { + $seqnum[$i]++; + last; + } + $seqnum[$i] = 0; + } + + if($seqnum[3] > 0xFF) { + @seqnum = (0,0,0,0); + } +} + +sub incseqlun { + $seqlun += 4; + + if($seqlun > 0xFF) { + $seqlun = 0; + } +} + +sub authcode { + my $type = shift; + my $message = shift; + my @authcode; + + if($auth == 0x02) { + if($type == 1) { + @authcode = unpack("C*",md5(pack("C*",@pass,@session_id,@challenge,@pass))); + } + elsif($type == 2) { + @authcode = unpack("C*",md5(pack("C*",@pass,@session_id,@$message,@seqnum,@pass))); + } + } + elsif($auth == 0x04) { + @authcode = @pass; + } + elsif($auth == 0x00) { + @authcode = (); + } + + return(@authcode); +} + +sub comp2int { + my $length = shift; + my $bits = shift; + my $neg = 0; + + if($bits & 2**($length - 1)) { + $neg = 1; + } + + $bits &= (2**($length - 1) - 1); + + if($neg) { + $bits -= 2**($length - 1); + } + + return($bits); +} + +sub timestamp2datetime { + my $ts = shift; + my @t = localtime($ts); + my $time = strftime("%H:%M:%S",@t); + my $date = strftime("%m/%d/%Y",@t); + + return($date,$time); +} + +sub decodebcd { + my $numbers = shift; + my @bcd; + my $text; + my $ms; + my $ls; + + foreach(@$numbers) { + $ms = ($_ & 0b11110000) >> 4; + $ls = ($_ & 0b00001111); + push(@bcd,$ms); + push(@bcd,$ls); + } + + foreach(@bcd) { + if($_ < 0x0a) { + $text .= $_; + } + elsif($_ == 0x0a) { + $text .= " "; + } + elsif($_ == 0x0b) { + $text .= "-"; + } + elsif($_ == 0x0c) { + $text .= "."; + } + } + + return($text); +} + +sub storsdrcache { + my $file = shift; + my $key; + my $fh; + + system("mkdir -p $cache_dir"); + if(!open($fh,">$file")) { + return(1); + } + + flock($fh,LOCK_EX) || return(1); + + foreach $key (keys %sdr_hash) { + my $r = $sdr_hash{$key}; + store_fd($r,$fh); + } + + close($fh); + + return(0); +} + +sub loadsdrcache { + my $file = shift; + my $r; + my $c=0; + my $fh; + + if(!open($fh,"<$file")) { + return(1); + } + + flock($fh,LOCK_SH) || return(1); + + while() { + eval { + $r = retrieve_fd($fh); + } || last; + + $sdr_hash{$r->sensor_owner_id . "." . $r->sensor_owner_lun . "." . $r->sensor_number} = $r; + } + + close($fh); + + return(0); +} + + +sub process_request { + my $request = shift; + my $callback = shift; + my $noderange = $request->{node}; #Should be arrayref + my $command = $request->{command}->[0]; + my $extrargs = $request->{arg}; + my @exargs=($request->{arg}); + unless ($noderange) { + if ($usage{$command}) { + $callback->({data=>$usage{$command}}); + $request = {}; + } + return; + } + if (ref($extrargs)) { + @exargs=@$extrargs; + } + my $ipmiuser = 'USERID'; + my $ipmipass = 'PASSW0RD'; + my $ipmitrys = 3; + my $ipmitimeout = 2; + my $ipmimaxp = 64; + my $sitetab = xCAT::Table->new('site'); + my $ipmitab = xCAT::Table->new('ipmi'); + my $tmp; + if ($sitetab) { + $tmp=$sitetab->getAttribs({'key'=>'ipmimaxp'},'value'); + if (defined($tmp)) { $ipmimaxp=$tmp->{value}; } + $tmp=$sitetab->getAttribs({'key'=>'ipmitimeout'},'value'); + if (defined($tmp)) { $ipmitimeout=$tmp->{value}; } + $tmp=$sitetab->getAttribs({'key'=>'ipmiretries'},'value'); + if (defined($tmp)) { $ipmitrys=$tmp->{value}; } + $tmp=$sitetab->getAttribs({'key'=>'ipmisdrcache'},'value'); + if (defined($tmp)) { $enable_cache=$tmp->{value}; } + } + my $passtab = xCAT::Table->new('passwd'); + if ($passtab) { + $tmp=$passtab->getAttribs({'key'=>'ipmi'},['username','password']); + if (defined($tmp)) { + $ipmiuser = $tmp->{username}; + $ipmipass = $tmp->{password}; + } + } + + #my @threads; + my @donargs=(); + foreach(@$noderange) { + my $node=$_; + my $nodeuser=$ipmiuser; + my $nodepass=$ipmipass; + my $nodeip = $node; + my $ent; + if (defined($ipmitab)) { + $ent=$ipmitab->getNodeAttribs($node,['bmc','username','password']) ; + if (ref($ent) and defined $ent->{bmc}) { $nodeip = $ent->{bmc}; } + if (ref($ent) and defined $ent->{username}) { $nodeuser = $ent->{username}; } + if (ref($ent) and defined $ent->{password}) { $nodepass = $ent->{password}; } + } + push @donargs,[$node,$nodeip,$nodeuser,$nodepass]; + } + my $children = 0; + $SIG{CHLD} = sub {my $kpid; do { $kpid = waitpid(-1, WNOHANG); if ($kpid > 0) { $children--; } } while $kpid > 0; }; + my $sub_fds = new IO::Select; + foreach (@donargs) { + while ($children > $ipmimaxp) { sleep (0.1); } + $children++; + my $cfd; + my $pfd; + pipe $cfd, $pfd; + my $child = fork(); + unless (defined $child) { die "Fork failed" }; + if ($child == 0) { + close($cfd); + donode($pfd,$_->[0],$_->[1],$_->[2],$_->[3],$ipmitimeout,$ipmitrys,$command,-args=>@exargs); + close($pfd); + exit(0); + } + close ($pfd); + $sub_fds->add($cfd) + } + while ($children > 0) { + forward_data($callback,$sub_fds); + } + while (forward_data($callback,$sub_fds)) {} #Make sure they get drained, this probably is overkill but shouldn't hurt +} +sub forward_data { #unserialize data from pipe, chunk at a time, use magic to determine end of data structure + my $callback = shift; + my $fds = shift; + my @ready_fds = $fds->can_read(1); + my $rfh; + my $rc = @ready_fds; + foreach $rfh (@ready_fds) { + my $data; + if ($data = <$rfh>) { + while ($data !~ /ENDOFFREEZE6sK4ci/) { + $data .= <$rfh>; + } + my $responses=thaw($data); + foreach (@$responses) { + $callback->($_); + } + } else { + $fds->remove($rfh); + close($rfh); + } + } + return $rc; +} + +sub donode { + my $outfd = shift; + my $node = shift; + my $bmcip = shift; + my $user = shift; + my $pass = shift; + my $timeout = shift; + my $retries = shift; + my $command = shift; + my %namedargs=@_; + my $transid = $namedargs{-transid}; + my @exargs=$namedargs{-args}; + my ($rc,@output) = ipmicmd($bmcip,623,$user,$pass,$timeout,$retries,0,$command,@exargs); + my @outhashes; + foreach(@output) { + my %output; + (my $desc,my $text) = split(/:/,$_,2); + unless ($text) { + $text=$desc; + } else { + $desc =~ s/^\s+//; + $desc =~ s/\s+$//; + $output{node}->[0]->{data}->[0]->{desc}->[0]=$desc; + } + $text =~ s/^\s+//; + $text =~ s/\s+$//; + $output{node}->[0]->{name}->[0]=$node; + $output{node}->[0]->{data}->[0]->{contents}->[0]=$text; + if ($rc) { + $output{node}->[0]->{errorcode}=$rc; + } + #push @outhashes,\%output; #Save everything for the end, don't know how to be slicker with Storable and a pipe + print $outfd freeze([\%output]); + print $outfd "\nENDOFFREEZE6sK4ci\n"; + } + #my $msgtoparent=freeze(\@outhashes); + # print $outfd $msgtoparent; +} + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/networks.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/networks.pm new file mode 100644 index 000000000..d5d36064d --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/networks.pm @@ -0,0 +1,82 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::networks; +use xCAT::Table; +use Data::Dumper; +use Sys::Syslog; + + +sub handled_commands { + return { + makenetworks => "networks", + } +} + + +sub process_request { + my $nettab = xCAT::Table->new('networks',-create=>1,-autocommit=>0); + my @rtable = split /\n/,`/bin/netstat -rn`; + open($rconf,"/etc/resolv.conf"); + my @nameservers; + if ($rconf) { + my @rcont; + while (<$rconf>) { + push @rcont,$_; + } + close($rconf); + foreach (grep /nameserver/,@rcont) { + my $line = $_; + my @pair; + $line =~ s/#.*//; + @pair = split(/\s+/,$line); + push @nameservers,$pair[1]; + } + } + splice @rtable,0,2; + foreach (@rtable) { #should be the lines to think about, do something with U, and something else with UG + my $net; + my $mask; + my $gw; + my @ent = split /\s+/,$_; + if ($ent[3] eq 'U') { + $net = $ent[0]; + $mask = $ent[2]; + $nettab->setAttribs({'net'=>$net},{'mask'=>$mask}); + my $tent = $nettab->getAttribs({'net'=>$net},nameservers); + unless ($tent and $tent->{nameservers}) { + my $text = join ',',@nameservers; + $nettab->setAttribs({'net'=>$net},{nameservers=>$text}); + } + unless ($tent and $tent->{tftpserver}) { + my $netdev=$ent[7]; + my @netlines = split/\n/,`/sbin/ip addr show dev $netdev`; + foreach (grep /\s*inet\b/,@netlines) { + my @row = split(/\s+/,$_); + my $ipaddr = $row[2]; + $ipaddr =~ s/\/.*//; + my @maska=split(/\./,$mask); + my @ipa=split(/\./,$ipaddr); + my @neta=split(/\./,$net); + my $isme=1; + foreach (0..3) { + my $oct = (0+$maska[$_]) & ($ipa[$_]+0); + unless ($oct == $neta[$_]) { + $isme=0; + last; + } + } + if ($isme) { + $nettab->setAttribs({'net'=>$net},{tftpserver=>$ipaddr}); + last; + } + } + } + $nettab->commit; + #Nothing much sane to do for the other fields at the moment? + } elsif ($ent[3] eq 'UG') { + #TODO: networks through gateway. and how we might care.. + } else { + #TODO: anything to do with such entries? + } + } +} +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/nodediscover.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/nodediscover.pm new file mode 100644 index 000000000..7885ca65b --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/nodediscover.pm @@ -0,0 +1,65 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::nodediscover; +#use Net::SNMP qw(:snmp INTEGER); +use xCAT::Table; +use IO::Socket; +use SNMP; +use strict; + +use XML::Simple; +use Data::Dumper; +use POSIX "WNOHANG"; +use Storable qw(freeze thaw); +use IO::Select; +use IO::Handle; +use Sys::Syslog; + + +sub handled_commands { + return { + discovered => 'chain:ondiscover', + }; +} +sub process_request { + my $request = shift; + my $callback = shift; + my $node = $request->{node}->[0]; + my $ip = $request->{'!xcat_clientip'}; + openlog("xCAT node discovery",'','local0'); + #First, fill in tables with data fields.. + if (defined($request->{mtm}) or defined($request->{serial})) { + my $vpdtab = xCAT::Table->new("vpd",-create=>1); + if ($request->{mtm}->[0]) { + $vpdtab->setNodeAttribs($node,{mtm=>$request->{mtm}->[0]}); + } + if ($request->{serial}) { + $vpdtab->setNodeAttribs($node,{serial=>$request->{serial}->[0]}); + } + } + if (defined($request->{arch})) { + my $typetab=xCAT::Table->new("nodetype",-create=>1); + $typetab->setNodeAttribs($node,{arch=>$request->{arch}->[0]}); + } + #TODO: mac table? on the one hand, 'the' definitive interface was determined earlier... + #Delete the state it was in to make it traverse destiny once agoin + my $chaintab = xCAT::Table->new('chain'); + if ($chaintab) { + $chaintab->setNodeAttribs($node,{currstate=>'',currchain=>''}); + $chaintab->close(); + } + + + #now, notify the node to continue life + my $sock = new IO::Socket::INET ( + PeerAddr => $ip, + PeerPort => '3001', + Proto => 'tcp' + ); + unless ($sock) { syslog("err","Failed to notify $ip that it's actually $node."); return; } #Give up if the node won't hear of it. + print $sock "restart"; + close($sock); + syslog("info","$node has been discovered"); +} + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/notification.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/notification.pm new file mode 100644 index 000000000..88c835621 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/notification.pm @@ -0,0 +1,285 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::notification; +use xCAT::Table; +use xCAT::NotifHandler; +1; + +#------------------------------------------------------------------------------- +=head1 xCAT_plugin:notification +=head2 Package Description + xCAT notification plugini module. This mondule allows users to register and + unregister for the xCAT database table changes. +=cut +#------------------------------------------------------------------------------- + + +#-------------------------------------------------------------------------------- +=head3 handled_commands + It returns a list of commands handled by this plugin. + Arguments: + none + Returns: + a list of commands. +=cut +#------------------------------------------------------------------------------- +sub handled_commands { + return { + regnotif => "notification", + unregnotif => "notification", + refnotif => "notification", + lsnotif => "notification", + enablenotif => "notification", + disablenotif => "notification", + } +} + + +#-------------------------------------------------------------------------------- +=head3 process_request + It processes the monitoring control commands. + Arguments: + request -- a hash table which contains the command name. + callback -- a callback pointer to return the response to. + args -- a list of arguments that come with the command. + Returns: + 0 for success. The output is returned through the callback pointer. + 1. for unsuccess. The error messages are returns through the callback pointer. +=cut +#------------------------------------------------------------------------------- +sub process_request { + use Getopt::Long; + # options can be bundled up like -vV + Getopt::Long::Configure("bundling") ; + $Getopt::Long::ignorecase=0; + + my $request = shift; + my $callback = shift; + my $command = $request->{command}->[0]; + my $args=$request->{arg}; + + if ($command eq "regnotif") { + my ($ret, $msg) = regNotification($args, $callback); + if ($msg) { + my %rsp=(); + $rsp->{dara}->[0]= $msg; + $callback->($rsp); + } + return $ret; + } + elsif ($command eq "unregnotif") { + my ($ret, $msg) = unregNotification($args, $callback); + if ($msg) { + my %rsp=(); + $rsp->{data}->[0]= $msg; + $callback->($rsp); + } + return $ret; + } + else { + my %rsp=(); + $rsp->{data}->[0]= "unsupported command: $command."; + $callback->($rsp); + return 1; + } +} + + +#-------------------------------------------------------------------------------- +=head3 regNotification + It registers a notification routine or a command which + will be called when there are changes in the data base tables + that the routine/command is interested in. + Arguments: + args - The format of the args is: + [-h|--help|-v|--version] or + filename tablename[,tablename]... [-o|--operation tableop[,tableop][,tableop]]] + where + tablename - string. a list of comma seperated names of the tables whose changes + will be watched. + filename - string. The name of the module (e.g. /usr/lib/xxx.pm) or + command (e.g. /usr/bin/xxx) that handles the notification. + If it is a perl module, the module must implement the following routine: + processTableChanges(action, table_name, old_data, new_data) + Refer to notify() subroutine for the meaning of the parameters. + If it is a command, the command format should be: + command [-d|-a|-u] tablename {[colnames][rowvalues][rowvalues]...} {[colnames][rowvalues]} + tableop - it can be 'a' for add, 'd' for delete and 'u' for update. + callback - the pointer to the callback function. + Returns: + none + Comments: + If the module or the command already exists in the notification table, this subroutine + will replace it. +=cut +#------------------------------------------------------------------------------- +sub regNotification { + my $args=shift; + my $callback=shift; + my $VERSION; + my $HELP; + my $tableops; + + # subroutine to display the usage + sub regnotif_usage + { + my $callbk=shift; + if (! $callbk) { return;} + + my %rsp; + $rsp->{data}->[0]= "Usage:"; + $rsp->{data}->[1]= " regnotif filename tablename[,tablename]... [-o|--operation tableop[,tableop][,tableop]]]"; + $rsp->{data}->[2]= " where tableop can be 'a' for add, 'd' for delete and 'u' for update"; + $rsp->{data}->[3]= " regnotif [-h|--help|-v|--version]"; + $callbk->($rsp); + } + + @ARGV=@{$args}; + # parse the options + if(!GetOptions( + 'o|operation=s' => \$tableops, + 'h|help' => \$::HELP, + 'v|version' => \$::VERSION,)) + { + ®notif_usage($callback); + return; + } + + # display the usage if -h or --help is specified + if ($::HELP) { + ®notif_usage($callback); + return; + } + + # display the version statement if -v or --verison is specified + if ($::VERSION) + { + my %rsp; + $rsp->{data}->[0]= "regnotif version 1.0"; + if ($callback) { + $callback->($rsp); + } + return; + } + + # must specify the file name and table names + if (@ARGV < 2) + { + ®notif_usage($callback); + return; + } + + #table operations must be a,d or u seperated by ',' + if ($tableops) + { + if ($tableops !~ m/^(a|d|u)(,(a|d|u)){0,2}$/) { + my %rsp; + $rsp->{data}->[0]= "Invalid table operations: $tableops"; + if ($callback) { + $callback->($rsp); + } + return; + } + } + else + { + $tableops="a,d,u"; + } + + my $fname=shift(@ARGV); + my $table_names=shift(@ARGV); + + my $table=xCAT::Table->new("notification", -create => 1,-autocommit => 0); + if ($table) { + my %key_col = (filename=>$fname); + my %tb_cols=(tables=>$table_names, tableops=>$tableops); + $table->setAttribs(\%key_col, \%tb_cols); + $table->commit; + } + + #update notification cache + xCAT::NotifHandler::sendNotifSignal(); + return; +} + +#-------------------------------------------------------------------------------- +=head3 unregNotification + It unregisters a notification routine or a command. + Arguments: + args - the format of the ares is: + [-h|--help|-v|--version] or + filename + where + filename - string. The name of the module or command that handles the notification. + callback - the pointer to the callback funtion. + Returns: + none +=cut +#------------------------------------------------------------------------------- +sub unregNotification { + my $args=shift; + my $callback=shift; + my $VERSION; + my $HELP; + + # subroutine to display the usage + sub unregnotif_usage + { + my $callbk=shift; + if (! $callbk) { return;} + + my %rsp; + $rsp->{data}->[0]= "Usage:"; + $rsp->{data}->[1]= " unregnotif filename"; + $rsp->{data}->[2]= " unregnotif [-h|--help|-v|--version]"; + $callbk->($rsp); + } + + @ARGV=@{$args}; + # parse the options + if(!GetOptions( + 'h|help' => \$::HELP, + 'v|version' => \$::VERSION,)) + { + &unregnotif_usage($callback); + return; + } + + # display the usage if -h or --help is specified + if ($::HELP) { + &unregnotif_usage($callback); + return; + } + + # display the version statement if -v or --verison is specified + if ($::VERSION) + { + my %rsp; + $rsp->{data}->[0]= "unregnotif version 1.0"; + if ($callback) { + $callback->($rsp); + } + return; + } + + # must specify the node range + if (@ARGV < 1) { + &unregnotif_usage($callback); + return; + } + + + my $fname=shift(@ARGV); + + my $table=xCAT::Table->new("notification", -create => 1,-autocommit => 0); + if ($table) { + my %key_col = (filename=>$fname); + $table->delEntries(\%key_col); + $table->commit; + } + + #update notification cache + xCAT::NotifHandler::sendNotifSignal(); + return; +} + diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/pxe.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/pxe.pm new file mode 100644 index 000000000..3d59a7a8c --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/pxe.pm @@ -0,0 +1,172 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::pxe; +use Data::Dumper; +use Sys::Syslog; +use Socket; + +my $request; +my $callback; +my $dhcpconf = "/etc/dhcpd.conf"; +my $tftpdir = "/tftpboot"; +#my $dhcpver = 3; + +my %usage = ( + "nodeset" => "Usage: nodeset [install|shell|boot|runcmd=bmcsetup]", +); +sub handled_commands { + return { + nodeset => "noderes:netboot" + } +} + +sub check_dhcp { + #TODO: omapi magic to do things right + my $node = shift; + my $dhcpfile; + open ($dhcpfile,$dhcpconf); + while (<$dhcpfile>) { + if (/host $node\b/) { + close $dhcpfile; + return 1; + } + } + close $dhcpfile; + return 0; +} + +sub getstate { + my $node = shift; + if (check_dhcp($node)) { + if (-r $tftpdir . "/pxelinux.cfg/".$node) { + my $fhand; + open ($fhand,$tftpdir . "/pxelinux.cfg/".$node); + my $headline = <$fhand>; + close $fhand; + $headline =~ s/^#//; + chomp($headline); + return $headline; + } else { + return "boot"; + } + } else { + return "discover"; + } +} + +sub setstate { +=pod + + This function will manipulate the pxelinux.cfg structure to match what the noderes/chain tables indicate the node should be booting. + +=cut + my $node = shift; + my $restab = xCAT::Table->new('noderes'); + my $kern = $restab->getNodeAttribs($node,['kernel','initrd','kcmdline']); + my $pcfg; + open($pcfg,'>',$tftpdir."/pxelinux.cfg/".$node); + my $chaintab = xCAT::Table->new('chain'); + my $cref=$chaintab->getNodeAttribs($node,['currstate']); + if ($cref->{currstate}) { + print $pcfg "#".$cref->{currstate}."\n"; + } + print $pcfg "DEFAULT xCAT\n"; + print $pcfg "LABEL xCAT\n"; + my $chaintab = xCAT::Table->new('chain'); + my $stref = $chaintab->getNodeAttribs($node,['currstate']); + if ($stref and $stref->{currstate} eq "boot") { + print $pcfg "LOCALBOOT 0\n"; + close($pcfg); + } elsif ($kern and $kern->{kernel}) { + #It's time to set pxelinux for this node to boot the kernel.. + print $pcfg " KERNEL ".$kern->{kernel}."\n"; + if ($kern->{initrd} or $kern->{kcmdline}) { + print $pcfg " APPEND "; + } + if ($kern and $kern->{initrd}) { + print $pcfg "initrd=".$kern->{initrd}." "; + } + if ($kern and $kern->{kcmdline}) { + print $pcfg $kern->{kcmdline}."\n"; + } else { + print $pcfg "\n"; + } + close($pcfg); + my $inetn = inet_aton($node); + unless ($inetn) { + syslog("local1|err","xCAT unable to resolve IP for $node in pxe plugin"); + return; + } + } else { #TODO: actually, should possibly default to xCAT image? + print $pcfg "LOCALBOOT 0\n"; + close($pcfg); + } + my $ip = inet_ntoa(inet_aton($node));; + unless ($ip) { + syslog("local1|err","xCAT unable to resolve IP in pxe plugin"); + return; + } + my @ipa=split(/\./,$ip); + my $pname = sprintf("%02X%02X%02X%02X",@ipa); + link($tftpdir."/pxelinux.cfg/".$node,$tftpdir."/pxelinux.cfg/".$pname); +} + + + +my $errored = 0; +sub pass_along { + my $resp = shift; + $callback->($resp); + if ($resp and $resp->{errorcode} and $resp->{errorcode}->[0]) { + $errored=1; + } +} + + + + + +sub process_request { + $request = shift; + $callback = shift; + my $sub_req = shift; + my @args; + my @nodes; + if (ref($request->{node})) { + @nodes = @{$request->{node}}; + } else { + if ($request->{node}) { @nodes = ($request->{node}); } + } + unless (@nodes) { + if ($usage{$request->{command}->[0]}) { + $callback->({data=>$usage{$request->{command}->[0]}}); + } + return; + } + + if (ref($request->{arg})) { + @args=@{$request->{arg}}; + } else { + @args=($request->{arg}); + } + unless ($args[0] eq 'stat' or $args[0] eq 'enact') { + $sub_req->({command=>['setdestiny'], + node=>\@nodes, + arg=>[$args[0]]},\&pass_along); + } + if ($errored) { return; } + foreach (@nodes) { + my %response; + $response{node}->[0]->{name}->[0]=$_; + if ($args[0] eq 'stat') { + $response{node}->[0]->{data}->[0]= getstate($_); + $callback->(\%response); + } elsif ($args[0] eq 'enact') { + setstate($_); + } elsif ($args[0]) { #If anything else, send it on to the destiny plugin, then setstate + setstate($_); + } + } +} + + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/rhel.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/rhel.pm new file mode 100644 index 000000000..50cdb8633 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/rhel.pm @@ -0,0 +1,199 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::rhel; +use Storable qw(dclone); +use Sys::Syslog; +use xCAT::Table; +use xCAT::Template; +use xCAT::Postage; +use Data::Dumper; +use Getopt::Long; +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); +use File::Path; +use File::Copy; + +my %discids = ( + "1170973598.629055" => "rhelc5", + "1170978545.752040" => "rhels5" + ); + +sub handled_commands { + return { + copycd => "rhel", + mkinstall => "nodetype:os=rh.*" + } +} + +sub process_request { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my $distname = undef; + my $arch = undef; + my $path = undef; + if ($request->{command}->[0] eq 'copycd') { + return copycd($request,$callback,$doreq); + } elsif ($request->{command}->[0] eq 'mkinstall') { + return mkinstall($request,$callback,$doreq); + } +} + +sub mkinstall { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my @nodes = @{$request->{node}}; + my $node; + my $ostab = xCAT::Table->new('nodetype'); + my %doneimgs; + foreach $node (@nodes) { + my $osinst; + my $ent = $ostab->getNodeAttribs($node,['profile','os','arch']); + unless ($ent->{os} and $ent->{arch} and $ent->{profile}) { + $callback->({error=>["No profile defined in nodetype for $node"],errorcode=>[1]}); + next; #No profile + } + my $os = $ent->{os}; + my $arch = $ent->{arch}; + my $profile = $ent->{profile}; + unless (-r "/usr/share/xcat/install/rh/".$ent->{profile}.".tmpl") { + $callback->({error=>["No kickstart template exists for ".$ent->{profile}],errorcode=>[1]}); + next; + } + #Call the Template class to do substitution to produce a kickstart file in the autoinst dir + xCAT::Template->subvars("/usr/share/xcat/install/rh/".$ent->{profile}.".tmpl","/install/autoinst/".$node,$node); + mkpath "/install/postscripts/"; + xCAT::Postage->writescript($node,"/install/postscripts/".$node); + if (-r "/install/$os/$arch/images/pxeboot/vmlinuz" + and -r "/install/$os/$arch/images/pxeboot/initrd.img") { + unless ($doneimgs{"$os|$arch"}) { + #TODO: driver slipstream, targetted for network. + mkpath("/tftpboot/xcat/$os/$arch"); + copy("/install/$os/$arch/images/pxeboot/vmlinuz","/tftpboot/xcat/$os/$arch/"); + copy("/install/$os/$arch/images/pxeboot/initrd.img","/tftpboot/xcat/$os/$arch/"); + $doneimgs{"$os|$arch"}=1; + } + #We have a shot... + my $restab = xCAT::Table->new('noderes'); + my $ent = $restab->getNodeAttribs($node,['nfsserver','serialport','primarynic','installnic']); + my $hmtab = xCAT::Table->new('nodehm'); + my $sent = $hmtab->getNodeAttribs($node,['serialspeed','serialflow']); + unless ($ent and $ent->{nfsserver}) { + $callback->({error=>["No noderes.nfsserver defined for ".$ent->{profile}],errorcode=>[1]}); + next; + } + my $kcmdline="nofb utf8 ks=http://".$ent->{nfsserver}."/install/autoinst/".$node; + if ($ent->{installnic}) { + $kcmdline.=" ksdevice=".$ent->{installnic}; + } elsif ($ent->{primarynic}) { + $kcmdline.=" ksdevice=".$ent->{primarynic}; + } else { + $kcmdline .= " ksdevice=eth0"; + } + + #TODO: dd= for driver disks + if (defined $ent->{serialport}) { + unless ($sent->{serialspeed}) { + $callback->({error=>["serialport defined, but no serialspeed for $node in nodehm table"],errorcode=>[1]}); + next; + } + $kcmdline.=" console=ttyS".$ent->{serialport}.",".$sent->{serialspeed}; + if ($sent->{serialflow} =~ /(ctsrts|cts|hard)/) { + $kcmdline .= "n8r"; + } + } + $kcmdline .= " noipv6"; + + $restab->setNodeAttribs($node,{ + kernel=>"xcat/$os/$arch/vmlinuz", + initrd=>"xcat/$os/$arch/initrd.img", + kcmdline=>$kcmdline + }); + } else { + $callback->({error=>["Install image not found in /install/$os/$arch"],errorcode=>[1]}); + } + } +} + +sub copycd { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my $installroot; + my $sitetab = xCAT::Table->new('site'); + if ($sitetab) { + (my $ref) = $sitetab->getAttribs({key=>installdir},value); + print Dumper($ref); + if ($ref and $ref->{value}) { + $installroot = $ref->{value}; + } + } + + @ARGV= @{$request->{arg}}; + GetOptions( + 'n=s' => \$distname, + 'a=s' => \$arch, + 'p=s' => \$path + ); + unless ($path) { + #this plugin needs $path... + return; + } + if ($distname and $distname !~ /^rh/) { + #If they say to call it something other than RH, give up? + return; + } + unless (-r $path."/.discinfo") { + return; + } + my $dinfo; + open($dinfo,$path."/.discinfo"); + my $did = <$dinfo>; + chomp($did); + my $desc = <$dinfo>; + chomp($desc); + my $darch = <$dinfo>; + chomp($darch); + if ($darch and $darch =~ /i.86/) { + $darch = "x86"; + } + close($dinfo); + if ($desc =~ /^Red Hat Enterprise Linux Client 5$/) { + unless ($distname) { + $distname = "rhelc5"; + } + } elsif ($desc =~ /^Red Hat Enterprise Linux Server 5$/) { + unless ($distname) { + $distname = "rhels5"; + } + } + + print $desc; + unless ($distname) { + return; #Do nothing, not ours.. + } + if ($darch) { + unless ($arch) { + $arch = $darch; + } + if ($arch and $arch ne $darch) { + $callback->({error=>"Requested RedHat architecture $arch, but media is $darch"}); + return; + } + } + %{$request} = (); #clear request we've got it. + + $callback->({data=>"Copying media to $installroot/$distname/$arch/"}); + my $omask=umask 0022; + mkpath("$installroot/$distname/$arch"); + umask $omask; + my $rc = system("cd $path; find . | cpio -dump $installroot/$distname/$arch"); + chmod 0755,"$installroot/$distname/$arch"; + if ($rc != 0) { + $callback->({error=>"Media copy operation failed, status $rc"}); + } else { + $callback->({data=>"Media copy operation successful"}); + } +} + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/sles.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/sles.pm new file mode 100644 index 000000000..f69656c0f --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/sles.pm @@ -0,0 +1,213 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::sles; +use Storable qw(dclone); +use Sys::Syslog; +use xCAT::Table; +use xCAT::Template; +use xCAT::Postage; +use Data::Dumper; +use Getopt::Long; +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); +use File::Path; +use File::Copy; + + +sub handled_commands { + return { + copycd => "sles", + mkinstall => "nodetype:os=sles.*" + } +} + +sub process_request { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my $distname = undef; + my $arch = undef; + my $path = undef; + if ($request->{command}->[0] eq 'copycd') { + return copycd($request,$callback,$doreq); + } elsif ($request->{command}->[0] eq 'mkinstall') { + return mkinstall($request,$callback,$doreq); + } +} + +sub mkinstall { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my @nodes = @{$request->{node}}; + my $node; + my $ostab = xCAT::Table->new('nodetype'); + my %doneimgs; + foreach $node (@nodes) { + my $osinst; + my $ent = $ostab->getNodeAttribs($node,['profile','os','arch']); + unless ($ent->{os} and $ent->{arch} and $ent->{profile}) { + $callback->({error=>["No profile defined in nodetype for $node"],errorcode=>[1]}); + next; #No profile + } + my $os = $ent->{os}; + my $arch = $ent->{arch}; + my $profile = $ent->{profile}; + unless (-r "/usr/share/xcat/install/sles/".$ent->{profile}.".tmpl") { + $callback->({error=>["No AutoYaST template exists for ".$ent->{profile}],errorcode=>[1]}); + next; + } + #Call the Template class to do substitution to produce a kickstart file in the autoinst dir + xCAT::Template->subvars("/usr/share/xcat/install/sles/".$ent->{profile}.".tmpl","/install/autoinst/".$node,$node); + mkpath "/install/postscripts/"; + xCAT::Postage->writescript($node,"/install/postscripts/".$node); + if (-r "/install/$os/$arch/1/boot/$arch/loader/linux" + and -r "/install/$os/$arch/1/boot/$arch/loader/initrd") { + #TODO: driver slipstream, targetted for network. + unless ($doneimgs{"$os|$arch"}) { + mkpath("/tftpboot/xcat/$os/$arch"); + copy("/install/$os/$arch/1/boot/$arch/loader/linux","/tftpboot/xcat/$os/$arch/"); + copy("/install/$os/$arch/1/boot/$arch/loader/initrd","/tftpboot/xcat/$os/$arch/"); + $doneimgs{"$os|$arch"}=1; + } + #We have a shot... + my $restab = xCAT::Table->new('noderes'); + my $hmtab = xCAT::Table->new('nodehm'); + my $ent = $restab->getNodeAttribs($node,['nfsserver','serialport','primarynic','installnic']); + my $sent = $hmtab->getNodeAttribs($node,['serialspeed','serialflow']); + unless ($ent and $ent->{nfsserver}) { + $callback->({error=>["No server for $node defined"],errorcode=>[1]}); + next; + } + my $kcmdline="autoyast=http://".$ent->{nfsserver}."/install/autoinst/".$node." install=http://".$ent->{nfsserver}."/install/$os/$arch/1"; + if ($ent->{installnic}) { + $kcmdline.=" netdevice=".$ent->{installnic}; + } elsif ($ent->{primarynic}) { + $kcmdline.=" netdevice=".$ent->{primarynic}; + } else { + $kcmdline .= " netdevice=eth0"; + } + + #TODO: driver disk handling should in SLES case be a mod of the install source, nothing to see here + if (defined $ent->{serialport}) { + unless ($sent->{serialspeed}) { + $callback->({error=>["serialport defined, but no serialspeed for $node in nodehm table"],errorcode=>[1]}); + next; + } + $kcmdline.=" console=ttyS".$ent->{serialport}.",".$sent->{serialspeed}; + if ($sent and ($sent->{serialflow} =~ /(ctsrts|cts|hard)/)) { + $kcmdline .= "n8r"; + } + } + + $restab->setNodeAttribs($node,{ + kernel=>"xcat/$os/$arch/linux", + initrd=>"xcat/$os/$arch/initrd", + kcmdline=>$kcmdline + }); + } + } +} + +sub copycd { + my $request = shift; + my $callback = shift; + my $doreq = shift; + my $installroot; + my $sitetab = xCAT::Table->new('site'); + if ($sitetab) { + (my $ref) = $sitetab->getAttribs({key=>installdir},value); + print Dumper($ref); + if ($ref and $ref->{value}) { + $installroot = $ref->{value}; + } + } + + @ARGV= @{$request->{arg}}; + GetOptions( + 'n=s' => \$distname, + 'a=s' => \$arch, + 'p=s' => \$path + ); + unless ($path) { + #this plugin needs $path... + return; + } + if ($distname and $distname !~ /^sles/) { + #If they say to call it something other than SLES, give up? + return; + } + unless (-r $path."/content") { + return; + } + my $dinfo; + open($dinfo,$path."/content"); + while (<$dinfo>) { + if (m/^DEFAULTBASE\s+(\S+)/) { + $darch = $1; + chomp($darch); + last; + } + } + close($dinfo); + unless ($darch) { + return; + } + my $dirh; + opendir($dirh,$path); + my $discnumber; + my $totaldiscnumber; + while (my $pname = readdir($dirh)) { + if ($pname =~ /media.(\d+)/) { + $discnumber=$1; + chomp($discnumber); + my $mfile; + open($mfile,$path."/".$pname."/media"); + <$mfile>; + <$mfile>; + $totaldiscnumber=<$mfile>; + chomp($totaldiscnumber); + close($mfile); + open($mfile,$path."/".$pname."/products"); + my $prod = <$mfile>; + close($mfile); + if ($prod =~ m/SUSE-Linux-Enterprise-Server/) { + my @parts = split /\s+/,$prod; + my @subparts = split /-/,$parts[2]; + $distname="sles".$subparts[0]; + } + } + } + unless ($distname and $discnumber) { + return; + } + if ($darch and $darch =~ /i.86/) { + $darch = "x86"; + } elsif ($darch and $darch =~ /ppc/) { + $darch = "ppc64"; + } + if ($darch) { + unless ($arch) { + $arch = $darch; + } + if ($arch and $arch ne $darch) { + $callback->({error=>"Requested SLES architecture $arch, but media is $darch"}); + return; + } + } + %{$request} = (); #clear request we've got it. + + $callback->({data=>"Copying media to $installroot/$distname/$arch/$discnumber"}); + my $omask=umask 0022; + mkpath("$installroot/$distname/$arch/$discnumber"); + umask $omask; + my $rc = system("cd $path; find . | nice -n 20 cpio -dump $installroot/$distname/$arch/$discnumber/"); + chmod 0755,"$installroot/$distname/$arch"; + chmod 0755,"$installroot/$distname/$arch/$discnumber"; + if ($rc != 0) { + $callback->({error=>"Media copy operation failed, status $rc"}); + } else { + $callback->({data=>"Media copy operation successful"}); + } +} + +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/switch.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/switch.pm new file mode 100644 index 000000000..5b8fc2ead --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/switch.pm @@ -0,0 +1,53 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT_plugin::switch; +use IO::Socket; +use Data::Dumper; +use xCAT::MacMap; +use Sys::Syslog; +use Storable; + +my $macmap; +sub handled_commands { + $macmap = xCAT::MacMap->new(); + return { + findme => 'switch', + }; +} + +sub process_request { + my $req = shift; + my $cb = shift; + my $doreq = shift; + my $ip = $req->{'!xcat_clientip'}; + my $mac = ''; + my $arptable = `/sbin/arp -n`; + my @arpents = split /\n/,$arptable; + foreach (@arpents) { + if (m/^($ip)\s+\S+\s+(\S+)\s/) { + $mac=$2; + last; + } + } + unless ($mac) { + return; + } + my $node = $macmap->find_mac($mac); + if ($node) { + my $mactab = xCAT::Table->new('mac',-create=>1); + $mactab->setNodeAttribs($node,{mac=>$mac}); + $mactab->close(); + my %request = ( + command => ['makedhcp'], + node => [$node] + ); + $doreq->(\%request); + $req->{command}=['discovered']; + $req->{noderange} = [$node]; + $doreq->($req); + %{$req}=();#Clear req structure, it's done.. + undef $mactab; + } else { + #Shouldn't complain, might be blade, but how to log total failures? + } +} +1; diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/tabutils.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/tabutils.pm new file mode 100644 index 000000000..4a39e59dd --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/tabutils.pm @@ -0,0 +1,593 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +##################################################### +# +# xCAT plugin package to handle various commands that work with the +# xCAT tables +# +# Supported commands: +# nodeadd +# nodels +# nodech +# tabdump +# tabrestore +# noderm +# To be implemented: +# gettab +# chtab +# tabls +# getnodecfg (?? this doesn't seem much different from gettab) +# nr +# These were xCAT 1.2 commands. Are they still useful in xCAT 1.3? +# addattr +# delattr +# chtype +# +##################################################### +package xCAT_plugin::tabutils; +use xCAT::Table; +use xCAT::Schema; +use Data::Dumper; +use xCAT::NodeRange; +use xCAT::Schema; #noderm will need to build list of tables.. +#use Getopt::Long qw(GetOptionsFromArray); + +1; + +#some quick aliases to table/value +my %shortnames = ( + groups => [qw(nodelist groups)], + tags => [qw(nodelist groups)], + mgt => [qw(nodehm mgt)], + switch => [qw(switch switch)], +); + +##################################################### +# Return list of commands handled by this plugin +##################################################### +sub handled_commands { + return { + gettab => "tabutils", + tabdump => "tabutils", + tabrestore => "tabutils", + tabch => "tabutils", + nodech => "tabutils", + nodeadd => "tabutils", + noderm => "tabutils", + tabls => "tabutils", + nodels => "tabutils", + getnodecfg => "tabutils", + addattr => "tabutils", + delattr => "tabutils", + chtype => "tabutils", + nr => "tabutils" + } +} + +my %usage = ( + nodech => "Usage: nodech [table.column=value] [table.column=value] ...", + nodeadd => "Usage: nodeadd [table.column=value] [table.column=value] ...", + noderm => "Usage: noderm ", + tabdump => "Usage: tabdump \n where is one of the following:\n ".join("\n ",keys %xCAT::Schema::tabspec), + tabrestore => "Usage: tabrestore .csv", +); + +##################################################### +# Process the command +##################################################### +sub gettab { + my $req = shift; + my $callback = shift; + my $keyspec = shift @{$req->{arg}}; + my @keypairs = split /,/,$keyspec; + my %keyhash; + foreach (@keypairs) { + (my $key, my $value) = split /=/,$_; + $keyhash{$key}=$value; + } + my %tabhash; + foreach my $tabvalue (@{$req->{arg}}) { + (my $table,my $column) = split /\./,$tabvalue; + $tabhash{$table}->{$column} = 1; + } + foreach my $tabn (keys %tabhash) { + my $tab = xCAT::Table->new($tabn); + (my $ent) = $tab->getAttribs(\%keyhash,keys %{$tabhash{$tabn}}); + foreach my $coln (keys %{$tabhash{$tabn}}) { + $callback->({data=>["$tabn.$coln:".$ent->{$coln}]}); + } + $tab->close; + } +} + +sub process_request { + use Getopt::Long; + Getopt::Long::Configure("bundling"); + Getopt::Long::Configure("pass_through"); + + my $request = shift; + my $callback = shift; + my $nodes = $request->{node}; + my $command = $request->{command}->[0]; + my $args = $request->{arg}; + unless ($args or $nodes or $request->{data}) { + if ($usage{$command}) { + $callback->({data=>[$usage{$command}]}); + return; + } + } + + if ($command eq "nodels") { + return nodels($nodes,$args,$callback,$request->{noderange}->[0]); + } elsif ($command eq "noderm" or $command eq "rmnode") { + return noderm($nodes,$args,$callback); + } elsif ($command eq "nodeadd" or $command eq "addnode") { + return chnode($nodes,$args,$callback,1); + } elsif ($command eq "nodech" or $command eq "chnode") { + return chnode($nodes,$args,$callback,0); + } elsif ($command eq "tabrestore") { + return tabrestore($request,$callback); + } elsif ($command eq "tabdump") { + return tabdump($args,$callback); + } elsif ($command eq "gettab") { + return gettab($request,$callback); + } else { + print "$command not implemented yet\n"; + return (1,"$command not written yet"); + } + +} + + +sub noderm { + my $nodes = shift; + my $args = shift; + my $cb = shift; + my @tablist = ("-d"); + foreach (keys %{xCAT::Schema::tabspec}) { + if (grep /^node$/,@{$xCAT::Schema::tabspec{$_}->{cols}}) { + push @tablist,$_; + } + } + chnode($nodes,\@tablist,$cb,0); +} +sub tabrestore { +#request->{data} is an array of CSV formatted lines + my $request = shift; + my $cb = shift; + my $table = $request->{table}->[0]; + my $linenumber = 1; + my $tab = xCAT::Table->new($table,-create =>1,-autocommit=>0); + unless ($tab) { + $cb->({error=>"Unable to open $table"}); + return; + } + $tab->delEntries(); #Yes, delete *all* entries + my $header = shift @{$request->{data}}; + $header =~ s/"//g; #Strip " from overzealous CSV apps + $header =~ s/^#//; + $header =~ s/\s+$//; + my @colns = split(/,/,$header); + my $line; + my $rollback=0; + LINE: foreach $line (@{$request->{data}}) { + $linenumber++; + $line =~ s/\s+$//; + my $origline = $line; #save for error reporting + my %record; + my $col; + foreach $col (@colns) { + if ($line =~ /^,/ or $line eq "") { #Match empty, or end of line that is empty + #TODO: should we detect when there weren't enough CSV fields on a line to match colums? + $record{$col}=undef; + $line =~ s/^,//; + } elsif ($line =~ /^[^,]*"/) { # We have stuff in quotes... pain... + #I don't know what I'm doing, so I'll do it a hard way.... + if ($line !~ /^"/) { + $rollback = 1; + $cb->({error=>"CSV missing opening \" for record with \" characters on line $linenumber, character ".index($origline,$line).": $origline"}); + next LINE; + } + my $offset=1; + my $nextchar; + my $ent = ""; + while (not $ent) { + $offset = index($line,'"',$offset); + $offset++; + if ($offset <=0) { + #MALFORMED CSV, request rollback, report an error + $rollback=1; + $cb->({error=>"CSV unmatched \" in record on line $linenumber, character ".index($origline,$line).": $origline"}); + next LINE; + } + $nextchar = substr($line,$offset,1); + if ($nextchar eq '"') { + $offset++; + } elsif ($offset eq length($line) or $nextchar eq ',') { + $ent = substr($line,0,$offset,''); + $line =~ s/^,//; + chop $ent; + $ent = substr($ent,1); + $ent =~ s/""/"/g; + $record{$col}=$ent; + } else { + $cb->({error=>"CSV unescaped \" in record on line $linenumber, character ".index($origline,$line).": $origline"}); + $rollback=1; + next LINE; + } + } + } elsif ($line =~ /^([^,]+)/) { #easiest case, no Text::Balanced needed.. + $record{$col} = $1; + $line =~ s/^([^,]+)(,|$)//; + } + } + if ($line) { + $rollback = 1; + $cb->({error=>"Too many fields on line $linenumber: $origline | $line"}); + next LINE; + } + #TODO: check for error from DB and rollback + my @rc =$tab->setAttribs(\%record,\%record); + if (not defined($rc[0])) { + $rollback = 1; + $cb->({error=>"DB error ".$rc[1]. " with line $linenumber: ".$origline}); + } + } + if ($rollback) { + $tab->rollback(); + $tab->close; + undef $tab; + return; + } else { + $tab->commit; #Made it all the way here, commit + } +} +sub tabdump { +#TODO: need to return header for not-yet existing, but schemad tabs +#TODO: schema defined column order. + my $args = shift; + my $cb = shift; + my $table=""; + foreach (@$args) { + unless (/^-/) { + if ($table) { + return 1; #TODO: Error, usage + } + $table=$_; + } + } + my $tabh = xCAT::Table->new($table); + my %rsp; + unless ($tabh) { + if (defined($xCAT::Schema::tabspec{$table})) { + my $header = join ",",@{$xCAT::Schema::tabspec{$table}->{cols}}; + $header="#".$header; + push @{$rsp{data}},$header; + $cb->(\%rsp); + return; + } + $cb->({error=>"No such table: $table"}); + return 1; + } + my $recs=$tabh->getAllEntries(); + my $rec; + my $firstline = 1; + unless (@$recs) { + if (defined($xCAT::Schema::tabspec{$table})) { + my $header = join ",",@{$xCAT::Schema::tabspec{$table}->{cols}}; + $header="#".$header; + push @{$rsp{data}},$header; + $cb->(\%rsp); + return; + } + } + + foreach $rec (@$recs) { + my $line =''; + if ($firstline) { + $firstline = 0; + $line = join ",",@{$tabh->{colnames}}; + $line = "#".$line; + push @{$rsp{data}},$line; + $line = ''; + } + foreach (@{$tabh->{colnames}}) { + $rec->{$_} =~ s/"/""/g; + if (defined $rec->{$_}) { + $line = $line . '"' . $rec->{$_} . '",'; + } else { + $line .= ','; + } + } + $line =~ s/,$//; + $line = $line . $lineappend; + push @{$rsp{data}},$line; + } + $cb->(\%rsp); +} + + + +sub chnode { + my $nodes=shift; + my $args=shift; + my $callback=shift; + my $addmode=shift; + print $addmode; + if ($addmode) { + my $idx=0; + foreach my $arg (@$args) { + unless ($arg =~ /^-/) { + $nodes = [noderange($arg,0)]; + splice(@$args,$idx,1); + last; + } + $idx++; + } + unless ($nodes) { + $callback->({error=>"No range to add detected\n"}); + return; + } + } + my $column; + my $value; + my $temp; + my %tables; + my $tab; + my $deletemode; + + #No GetOptionsFromArray... + #GetOptionsFromArray($args,"d|delete" => \$deletemode); + #print Dumper($deletemode); + foreach (@$args) { + if (m/^-/) { #A quick and dirty option parser in lieu of lacking Getoptinos + if (m/^--/) { + if (m/--delete/) { + $deletemode=1; + next; + } else { + $callback->({data=>["ERROR: Malformed argument $_ ignored"]}); + next; + } + } else { + if (m/^-d$/) { + $deletemode=1; + next; + } else { + $callback->({data=>["ERROR: Malformed argument $_ ignored"]}); + next; + } + } + } + + if ($deletemode) { + if (m/[=\.]/) { + $callback->({data=>["ERROR: . and = not valid in delete mode"]}); + next; + } + $tables{$_} = 1; + next; + } + unless (m/=/) { + $callback->({data=>["ERROR: Malformed argument $_ ignored"]}); + next; + } + ($temp,$value)=split('=',$_,2); + my $op='='; + if ($temp =~ /,$/) { + $op=',='; + chop($temp); + } elsif ($temp =~ /\^$/) { + $op='^='; + chop($temp); + } + + if ($shortnames{$temp}) { + ($table,$column) = @{$shortnames{$temp}}; + } else { + ($table,$column) = split('\.',$temp,2); + } + $tables{$table}->{$column}=[$value,$op]; + } + foreach $tab (keys %tables) { + my $tabhdl = xCAT::Table->new($tab,-create => 1,-autocommit => 0); + if ($tabhdl) { + foreach (@$nodes) { + if ($deletemode) { + $tabhdl->delEntries({'node'=>$_}); + } else { + #$tabhdl->setNodeAttribs($_,$tables{$tab}); + my %uhsh; + my $node = $_; + foreach (keys %{$tables{$tab}}) { + my $op = $tables{$tab}->{$_}->[1]; + my $val = $tables{$tab}->{$_}->[0]; + my $key = $_; + if ($op eq '=') { + $uhsh{$key}=$val; + } elsif ($op eq ',=') { #splice assignment + my $cent = $tabhdl->getNodeAttribs($node,[$key]); + my $curval; + if ($cent) { $curval = $cent->{$key}; } + if ($curval) { + my @vals = split(/,/,$curval); + unless (grep /^$val$/,@vals) { + @vals=(@vals,$val); + my $newval = join(',',@vals); + $uhsh{$key}=$newval; + } + } else { + $uhsh{$key}=$val; + } + } elsif ($op eq '^=') { + my $cent = $tabhdl->getNodeAttribs($node,[$key]); + my $curval; + if ($cent) { $curval = $cent->{$key}; } + if ($curval) { + my @vals = split(/,/,$curval); + if (grep /^$val$/,@vals) { #only bother if there + @vals = grep(!/^$val$/,@vals); + my $newval = join(',',@vals); + $uhsh{$key}=$newval; + } + } #else, what they asked for is the case alredy + + + } + } + if (keys %uhsh) { + $tabhdl->setNodeAttribs($node,\%uhsh); + } + } + } + $tabhdl->commit; + } else { + $callback->({data=>["ERROR: Unable to open table $tab in configuration"]}); + } + } +} + + + + +##################################################### +# nodels command +##################################################### +sub nodels { + my $nodes=shift; + my $args=shift; + my $callback=shift; + my $noderange=shift; + + my $VERSION; + my $HELP; + + sub nodels_usage { + my %rsp; + $rsp->{data}->[0]= "Usage:"; + $rsp->{data}->[1]= " nodels [-?|-h|--help] "; + $rsp->{data}->[2]= " nodels [-v|--version] "; + $rsp->{data}->[3]= " nodels [noderange] "; +##### xcat 1.2 nodels usage: +# $rsp->{data}->[1]= " nodels [noderange] [group|pos|type|rg|install|hm|all]"; +# $rsp->{data}->[2]= " "; +# $rsp->{data}->[3]= " nodels [noderange] hm.{power|reset|cad|vitals|inv|cons}"; +# $rsp->{data}->[4]= " hm.{bioscons|eventlogs|getmacs|netboot}"; +# $rsp->{data}->[5]= " hm.{eth0|gcons|serialbios|beacon}"; +# $rsp->{data}->[6]= " hm.{bootseq|serialbps|all}"; +# $rsp->{data}->[7]= " "; +# $rsp->{data}->[8]= " nodels [noderange] rg.{tftp|nfs_install|install_dir|serial}"; +# $rsp->{data}->[9]= " rg.{usenis|install_roll|acct|gm|pbs}"; +# $rsp->{data}->[10]=" rg.{access|gpfs|netdevice|prinic|all}"; + $callback->($rsp); + } + + @ARGV=@{$args}; + if ( !GetOptions( + 'h|?|help' => \$HELP, + 'v|version' => \$VERSION, + ) ) { + &nodels_usage; + } + + # Help + if ($HELP) { + &nodels_usage; + return; + } + + # Version + if ($VERSION) { + my %rsp; + $rsp->{data}->[0]= "1.3"; + $callback->($rsp); + return; + } + + # TODO -- Parse command arguments +# my $opt; +# my %attrs; +# foreach $opt (@ARGV) { +# if ($opt =~ /^group/) { +# } +# } + my $argc = @ARGV; + + + if (@$nodes > 0 or $noderange) { #Make sure that there are zero nodes *and* that a noderange wasn't requested + # TODO - gather data for each node + # for now just return the flattened list of nodes) + my %rsp; #build up fewer requests, be less chatty + if ($argc) { + my %tables; + foreach (@ARGV) { + my $table; + my $column; + my $temp=$_; + if ($shortnames{$temp}) { + ($table,$column) = @{$shortnames{$temp}}; + } else { + ($table,$column) = split('\.',$temp,2); + } + unless (grep /^$column$/,@{$tables{$table}}) { + push @{$tables{$table}},[$column,$temp]; #Mark this as something to get + } + } + my $tab; + my %noderecs; + foreach $tab (keys %tables) { + my $tabh = xCAT::Table->new($tab); + unless ($tabh) { next; } + #print Dumper($tables{$tab}); + my $node; + foreach $node (@$nodes) { + my @cols; + my %labels; + foreach (@{$tables{$tab}}) { + push @cols,$_->[0]; + $labels{$_->[0]}=$_->[1]; + } + my $rec=$tabh->getNodeAttribs($node,\@cols); + foreach (keys %$rec) { + my %datseg; + $datseg{data}->[0]->{desc} = [$labels{$_}]; + $datseg{data}->[0]->{contents} = [$rec->{$_}]; + $datseg{name} = [$node]; #{}->{contents} = [$rec->{$_}]; + push @{$noderecs{$node}},\%datseg; + } + } + #$rsp->{node}->[0]->{data}->[0]->{desc}->[0] = $_; + #$rsp->{node}->[0]->{data}->[0]->{contents}->[0] = $_; + $tabh->close(); + undef $tabh; + } + foreach (sort (keys %noderecs)) { + push @{$rsp->{"node"}},@{$noderecs{$_}}; + } + } else { + foreach (@$nodes) { + my $noderec; + $noderec->{name}->[0]=($_); + push @{$rsp->{node}},$noderec; + } + } + $callback->($rsp); + } else { + # no noderange specified on command line, return list of all nodes + my $nodelisttab; + if ($nodelisttab=xCAT::Table->new("nodelist")) { + my @attribs=("node"); + my @ents=$nodelisttab->getAllAttribs(@attribs); + foreach (@ents) { + my %rsp; + if ($_->{node}) { + $rsp->{node}->[0]->{name}->[0]=($_->{node}); +# $rsp->{node}->[0]->{data}->[0]->{contents}->[0]="$_->{node} node contents"; +# $rsp->{node}->[0]->{data}->[0]->{desc}->[0]="$_->{node} node desc"; + $callback->($rsp); + } + } + } + } + + return 0; +} diff --git a/xCAT-server-2.0/usr/lib/xcat/plugins/xdsh.pm b/xCAT-server-2.0/usr/lib/xcat/plugins/xdsh.pm new file mode 100644 index 000000000..2d7491958 --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/plugins/xdsh.pm @@ -0,0 +1,162 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#------------------------------------------------------- + +=head1 + xCAT plugin package to handle xdsh + + Supported command: + xdsh-> dsh + xdcp-> dcp + +=cut + +#------------------------------------------------------- +package xCAT_plugin::xdsh; +use xCAT::Table; + +use xCAT::Utils; + +use xCAT::MsgUtils; + +1; + +#------------------------------------------------------- + +=head3 handled_commands + +Return list of commands handled by this plugin + +=cut + +#------------------------------------------------------- + +sub handled_commands +{ + return { + xdsh => "xdsh", + xdcp => "xdsh" + }; +} + +#------------------------------------------------------- + +=head3 process_request + + Process the command + +=cut + +#------------------------------------------------------- +sub process_request +{ + + my $request = shift; + my $callback = shift; + my $nodes = $request->{node}; + my $command = $request->{command}->[0]; + my $args = $request->{arg}; + my %rsp; + $::DSH = "/opt/csm/bin/dsh"; + $::DCP = "/opt/csm/bin/dcp"; + + # check that dsh is installed + if (!-e $::DSH) + { + $rsp->{data}->[0] = + "dsh is not installed. Cannot process the command\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + + } + else + { + + if (($command eq "xdsh") || ($command eq "xdcp")) + { + return + xdsh($nodes, $args, $callback, $command, + $request->{noderange}->[0]); + } + else + { # error + $rsp->{data}->[0] = + "Unknown command $command. Cannot process the command\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + } + } +} + +#------------------------------------------------------- + +=head3 xdsh + + Builds and runs the dsh or dcp command + + +=cut + +#------------------------------------------------------- +sub xdsh +{ + my $nodes = shift; + my $args = shift; + my $callback = shift; + my $command = shift; + my $noderange = shift; + + # + # set XCAT Context + # + $ENV{DSH_CONTEXT} = "XCAT"; + + # + # if nodes, Put nodes in a file so we do + # not risk hitting a command line + # limit + my $node_file; + if ($nodes) + { + $node_file = xCAT::Utils->make_node_list_file($nodes); + $ENV{'DSH_LIST'} = $node_file; # export the file for dsh + } + + # + # call dsh or dcp + # + + my $dsh_dcp_command = ""; + my %rsp; + if ($command eq "xdsh") + { + $dsh_dcp_command = $::DSH; + } + else + { + $dsh_dcp_command = $::DCP; + } + $dsh_dcp_command .= " "; + + foreach my $arg (@$args) + { # add arguments + $dsh_dcp_command .= $arg; # last argument must be command to run + $dsh_dcp_command .= " "; + } + $dsh_dcp_command .= "2>&1"; + my @local_results = `$dsh_dcp_command`; # run the dsh command + my $rc = $? >> 8; + my $i = 0; + chop @local_results; + foreach my $line (@local_results) + { + $rsp->{data}->[$i] = $line; + $i++; + } + + #$rsp->{data}->[$i] = "Return Code = $rc\n"; + xCAT::Utils->close_delete_file($::NODE_LIST_FILE, $node_file); + xCAT::MsgUtils->message("I", $rsp, $callback); + + #xCAT::MsgUtils->message("I", $rsp); + #$callback->($rsp); + return 0; +} + diff --git a/xCAT-server-2.0/usr/lib/xcat/shfunctions b/xCAT-server-2.0/usr/lib/xcat/shfunctions new file mode 100644 index 000000000..1b0e86cdc --- /dev/null +++ b/xCAT-server-2.0/usr/lib/xcat/shfunctions @@ -0,0 +1,97 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +export LOCKDIR=/var/lock/xcat + + +function needhelp +{ + if [ "$#" = "1" ] + then + for i in $* + do + test "$i" = "--help" && echo "\nhttp://xcat.org for support." && return 0 + test "$i" = "-h" && echo "http://xcat.org for support.\n" && return 0 + test "$i" = "--version" && echo "$VERSION" && exit 0 + test "$i" = "-v" && echo "$VERSION" && exit 0 + done + fi + return 1 +} + + + + +function getlock +{ + LOCK=$1 + TIMEOUT=$2 + let t=0 + + mkdir -p $LOCKDIR + + if [ -r $LOCKDIR/$LOCK.pid ] + then + PID=$(cat $LOCKDIR/$LOCK.pid) + while ps -p $PID >/dev/null 2>&1 + do + sleep 1 + let t=t+1 + if (($t -gt $TIMEOUT)) + then + return 1 + fi + done + fi + + echo "$$" > $LOCKDIR/$LOCK.pid + return 0 +} + +function lockstatus +{ + LOCK=$1 + + if [ -r $LOCKDIR/$LOCK.pid ] + then + PID=$(cat $LOCKDIR/$LOCK.pid) + if ps -p $PID >/dev/null 2>&1 + then + echo "locked by PID $PID" + ps -fp $PID | tail -1 + else + echo "stale lock PID $PID, remove $LOCKDIR/$LOCK.pid" + fi + + return 0 + fi + + echo "nolock" + return 0 +} + +function freelock +{ + LOCK=$1 + FORCE=$2 + + mkdir -p $LOCKDIR + + if [ "$FORCE" = "1" ] + then + rm -f $LOCKDIR/$LOCK.pid >/dev/null 2>&1 + return 0 + fi + + if [ -r $LOCKDIR/$LOCK.pid ] + then + PID=$(cat $LOCKDIR/$LOCK.pid) + if [ "$PID" = "$$" ] + then + rm -f $LOCKDIR/$LOCK.pid + else + return 1 + fi + fi + + return 0 +} + diff --git a/xCAT-server-2.0/usr/sbin/chtab b/xCAT-server-2.0/usr/sbin/chtab new file mode 100755 index 000000000..e21cac173 --- /dev/null +++ b/xCAT-server-2.0/usr/sbin/chtab @@ -0,0 +1,100 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +use xCAT::Table; +use xCAT::NodeRange; +use Getopt::Long; + +use strict; +#This or something like this must always be available and not depend on server +#Otherwise, can't set things to let server run in the first place + +sub usage { + print "Usage:\n"; + print " To add or update rows for tables: + chtab keycolname=keyvalue[,keycolname=keyvalue...] [tablename.colname=newvalue] [tablename.colname=newvalue]...\n"; + print " To delete rows from tables: + chtab -d|--delete keycolname=keyvalue[,keycolname=keyvalue...] tablename [tablename]...\n"; + print " To display usage and other information: + chtab [-h|--help|-v|--Version]\n\n"; + print " -d|--delete Delete the rows from a list of tables. + -v|--version Display the version of this command. + -h|--help Display this usage information. + keycolname=keyvalue a column name-and-value pair that identifies the rows in a table to be changed. + tablename.colname=newvalue the new value for the specified row and column of the table.\n"; +} + +my %tables; + +# options can be bundled up like -vV +Getopt::Long::Configure("bundling") ; +$Getopt::Long::ignorecase=0; + +# parse the options +if(!GetOptions( + 'd|delete' => \$::DELETE, + 'h|help' => \$::HELP, + 'v|version' => \$::VERSION,)) +{ + &usage; + exit(1); +} + +# display the usage if -h or --help is specified +if ($::HELP) { &usage; exit(0);} + +# display the version statement if -v or --verison is specified +if ($::VERSION) +{ + print "chtab version 1.0\n"; + exit(0); +} + +my $target = shift @ARGV; +unless ($target) { + usage; + exit(1); +} +my %keyhash; +my @keypairs=split(/,/,$target); +foreach (@keypairs) { + m/(.*)=(.*)/; + my $key=$1; + my $val=$2; + $keyhash{$key}=$val; +} + + +if ($::DELETE) { + #delete option is specified + my @tables_to_del=@ARGV; + for (@tables_to_del) { + $tables{$_} = xCAT::Table->new($_,-create => 1,-autocommit => 0); + $tables{$_}->delEntries(\%keyhash); + $tables{$_}->commit; + } +} +else { + #update or create option + my %tableupdates; + for (@ARGV) { + my $temp; + my $table; + my $column; + my $value; + ($table,$temp) = split('\.',$_,2); + ($column,$value) = split("=",$temp,2); + unless ($tables{$table}) { + $tables{$table} = xCAT::Table->new($table,-create => 1,-autocommit => 0); + } + $tableupdates{$table}{$column}=$value; + } + + #commit all the changes + foreach (keys %tables) { + if (exists($tableupdates{$_})) { + $tables{$_}->setAttribs(\%keyhash,\%{$tableupdates{$_}}); + } + $tables{$_}->commit; + } +} + diff --git a/xCAT-server-2.0/usr/sbin/mknb b/xCAT-server-2.0/usr/sbin/mknb new file mode 100755 index 000000000..6b3a84c1a --- /dev/null +++ b/xCAT-server-2.0/usr/sbin/mknb @@ -0,0 +1,57 @@ +#!/bin/bash +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +if [ $# -ne 1 ]; then + echo "Usage: mknb " + exit 1 +fi +if [ ! -d /usr/share/xcat/netboot/$1 ]; then + echo "Unsupported architecture: $1" + exit 1 +fi +if [ ! -r ~/.ssh/id_rsa.pub ]; then + #echo "Need to generate ssh keys" + ssh-keygen -t rsa -q -b 2048 -N "" -f ~/.ssh/id_rsa + #exit 1 +fi +mkdir -p /tmp/mknb.$$ +cp -a /usr/share/xcat/netboot/$1/nbroot/* /tmp/mknb.$$/ +mkdir -p /tmp/mknb.$$/root/.ssh +cp ~/.ssh/id_rsa.pub /tmp/mknb.$$/root/.ssh/authorized_keys +if [ -f /install/postscripts/hostkeys/ssh_host_key ]; then + cp /install/postscripts/hostkeys/ssh_host_key /tmp/mknb.$$/etc/ssh_host_key + cp /install/postscripts/hostkeys/ssh_host_rsa_key /tmp/mknb.$$/etc/ssh_host_rsa_key + cp /install/postscripts/hostkeys/ssh_host_dsa_key /tmp/mknb.$$/etc/ssh_host_dsa_key +fi +if [ ! -f /tmp/mknb.$$/etc/ssh_host_key ]; then + echo Generating SSH1 RSA Key... + /usr/bin/ssh-keygen -t rsa1 -f /tmp/mknb.$$/etc/ssh_host_key -C '' -N '' + echo Generating SSH2 RSA Key... + /usr/bin/ssh-keygen -t rsa -f /tmp/mknb.$$/etc/ssh_host_rsa_key -C '' -N '' + echo Generating SSH2 DSA Key... + /usr/bin/ssh-keygen -t dsa -f /tmp/mknb.$$/etc/ssh_host_dsa_key -C '' -N '' +fi +cd /tmp/mknb.$$ +echo "Packing nbfs.$1.gz" +find . | cpio -o -H newc | gzip -9 > /tftpboot/xcat/nbfs.$1.gz +cd - +rm -rf /tmp/mknb.$$ +if [ "$1" = "x86_64" -a ! -f /tftpboot/pxelinux.0 -a -f /usr/lib/syslinux/pxelinux.0 ]; then + cp /usr/lib/syslinux/pxelinux.0 /tftpboot/pxelinux.0 +fi +if [ "$1" = "x86_64" -a ! -f /tftpboot/pxelinux.cfg/default ]; then + mkdir -p /tftpboot/pxelinux.cfg + echo "default xCAT + LABEL xCAT + KERNEL xcat/nbk.x86_64 + APPEND initrd=xcat/nbfs.x86_64.gz xcatd="`gettab key=master site.value|awk -F: '{print $2}'`:`gettab key=xcatdport site.value|awk -F: '{print $2}'` > /tftpboot/pxelinux.cfg/default +fi +if [ "$1" = "ppc64" -a ! -f /tftpboot/etc/yaboot.conf ]; then + mkdir -p /tftpboot/etc + echo "timeout=5 + label=xcat + image=xcat/nbk.ppc64 + initrd=xcat/nbfs.ppc64.gz + append=\"xcatd="`gettab key=master site.value|awk -F: '{print $2}'`:`gettab key=xcatdport site.value|awk -F: '{print $2}'`\"> /tftpboot/etc/yaboot.conf +fi + + diff --git a/xCAT-server-2.0/usr/sbin/xcatd b/xCAT-server-2.0/usr/sbin/xcatd new file mode 100755 index 000000000..b7c32b282 --- /dev/null +++ b/xCAT-server-2.0/usr/sbin/xcatd @@ -0,0 +1,685 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html + +use Socket; +use IO::Socket; +use IO::Handle; +use IO::Select; +use IO::Socket::SSL; +use IO::Socket::INET; +use XML::Simple; +use xCAT::Table; +use Data::Dumper; +use Getopt::Long; +use Sys::Syslog; +use xCAT::NotifHandler; + +Getopt::Long::Configure("bundling"); +Getopt::Long::Configure("pass_through"); + + +use Storable qw(dclone); +use POSIX qw(WNOHANG setsid); +use strict; +my $pidfile; +GetOptions( + 'pidfile|p=s' => \$pidfile +); + +my $plugins_dir='/usr/lib/xcat/plugins'; +my $quit = 0; +my $port; +my $sport; +my $domain; +my $xcatdir; +my $sitetab=xCAT::Table->new('site'); +unless ($sitetab) { + print ("ERROR: Unable to open basic site table for configuration\n"); +} +my ($tmp) = $sitetab->getAttribs({'key'=>'xcatdport'},'value'); +unless ($tmp) { + die "ERROR:Need xcatdport defined in site table, try chtab key=xcatdport site.value=3001"; +} +$port = $tmp->{value}; +$sport = $tmp->{value}+1; + +($tmp) = $sitetab->getAttribs({'key'=>'xcatconfdir'},'value'); +$xcatdir = ($tmp ? $tmp->{value} : "/etc/xcat"); + +my $progname; +$SIG{PIPE} = sub { die "SIGPIPE $$progname encountered a broken pipe (probably Ctrl-C by client)" }; +sub daemonize { + chdir('/'); + umask 0; + my $pid; + defined($pid = fork) or die "Can't fork: $!"; + if ($pid) { + if ($pidfile) { + open(PFILE, '>', $pidfile); + print PFILE $pid; + close (PFILE); + } else { + printf ("xCATd starting as PID $pid \n"); + } + exit; + } + open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; + open STDOUT, '>/dev/null'; + open STDERR, '>/dev/null'; + $0='xcatd'; + $progname = \$0; + setsid or die "Can't start new session"; +} + +my %cmd_handlers; +sub do_installm_service { + #This function servers as a handler for messages from installing nodes + my $socket = IO::Socket::INET->new(LocalPort=>$sport, + Proto => 'tcp', + ReuseAddr => 1, + Listen => 64); + unless ($socket) { + syslog("local1|err","xcatd unable to open install monitor services on $sport"); + die; + } + until ($quit) { + $SIG{ALRM} = sub { die "XCATTIMEOUT"; }; + my $conn; + next unless $conn = $socket->accept; + my $client = gethostbyaddr(inet_aton($conn->peerhost),AF_INET); + $client =~ s/\..*//; + my $node; + ($node) = noderange($client); #ensure this is coming from a node IP at least + unless ($node) { #Means the source isn't a valid deal... + close($conn); + next; + } + eval { + alarm(2); + print $conn "ready\n"; + while (my $text = <$conn>) { + alarm(0); + print $conn "done\n"; + $text =~ s/\r//g; + if ($text =~ /next/) { + my %request = ( + command => [ 'nodeset' ], + node => [ $node ], + arg => [ 'next' ], + ); + close($conn); + my $pid=fork(); + unless ($pid) { #fork off the nodeset and potential slowness + plugin_command(\%request,undef,\&convey_response); + exit(0); + } + } + alarm(2); + } + alarm(0); + }; + if ($@) { + if ($@ =~ /XCATTIMEOUT/) { + syslog("local1|err","xcatd installmonitor timed out talking to $node"); + } else { + syslog("local1|err","xcatd: possible BUG encountered by xCAT install monitor service: ".$@); + } + } + } +} + + +sub do_udp_service { #This function opens up a UDP port + #It will do similar to the standard service, except: + #-Obviously, unencrypted and messages are not guaranteed + #-For that reason, more often than not plugins designed with + #-this method will not expect to have a callback + #Also, this throttles to handle one message at a time, so no forking either + #Explicitly, to handle whatever operations nodes periodically send during discover state + #Could be used for heartbeating and such as desired + my $socket = IO::Socket::INET->new(LocalPort => $port, + Proto => 'udp'); + openlog("xCAT UDP",'','local1'); + unless ($socket) { + syslog("err","xCAT UDP service unable to open port $port: $!"); + closelog(); + die "Unable to start UDP on $port"; + } + my $data; + my $part; + my $sport; + my $client; + my $peerhost; + until ($quit) { + eval { while ($part = $socket->recv($data,1500)) { + ($sport,$client) = sockaddr_in($part); + $peerhost=gethostbyaddr($client,AF_INET)."\n"; + my $req = eval { XMLin($data, SuppressEmpty=>undef,ForceArray=>1) }; + if ($req and $req->{command} and ($req->{command}->[0] eq "findme")) { + $req->{'!xcat_clienthost'}=gethostbyaddr($client,AF_INET)."\n"; + $req->{'!xcat_clientip'}=inet_ntoa($client); + $req->{'!xcat_clientport'}=$sport; + if (defined($cmd_handlers{"findme"})) { + plugin_command($req,undef,\&convey_response); + } + } + if ($quit) { last; } + } + }; + if ($@) { + syslog("local1|err","xcatd: possible BUG encountered by xCAT UDP service: ".$@); + } + } +} + + + +sub scan_plugins { + my @plugins=glob($plugins_dir."/*.pm"); + foreach (@plugins) { + /.*\/([^\/]*).pm$/; + my $modname = $1; + require "$_"; + no strict 'refs'; + my $cmd_adds=${"xCAT_plugin::".$modname."::"}{handled_commands}->(); + foreach (keys %$cmd_adds) { + my $value = $_; + if (defined($cmd_handlers{$_})) { + my $add=1; + #This next bit of code iterates through the handlers. + #If the value doesn't contain an equal, and has an equivalent entry added by + # another plugin already, don't add (otherwise would hit the DB multiple times) + # a better idea, restructure the cmd_handlers as a multi-level hash + # prove out this idea real quick before doing that + foreach (@{$cmd_handlers{$_}}) { + if (($_->[1] eq $cmd_adds->{$value}) and (($cmd_adds->{$value} !~ /=/) or ($_->[0] eq $modname))) { + $add = 0; + } + } + if ($add) { push @{$cmd_handlers{$_}},[$modname,$cmd_adds->{$_}]; } + #die "Conflicting handler information from $modname"; + } else { + $cmd_handlers{$_} = [ [$modname,$cmd_adds->{$_}] ]; + } + } + } +} +scan_plugins; +daemonize; +$SIG{CHLD} = sub { while (waitpid(-1,WNOHANG) > 0) {} }; +$SIG{TERM} = $SIG{INT} = sub { printf("Asked to quit...\n"); $quit++ }; + +my $pid = fork; +defined $pid or die "Unable to fork for UDP/TCP"; +unless ($pid) { + $$progname="xcatd: UDP listener"; + do_udp_service; + exit(0); +} +$pid = fork; +defined $pid or die "Unable to fork installmonitor"; +unless ($pid) { + $$progname="xcatd: install monitor"; + do_installm_service; + exit(0); +} +$$progname="xcatd: SSL listener"; +openlog("xCAT SSL","","local1"); +my $listener = IO::Socket::SSL->new( + LocalPort => $port, + Listen => 64, + Reuse => 1, + SSL_key_file=>$xcatdir."/cert/server-key.pem", + SSL_cert_file=>$xcatdir."/cert/server-cert.pem", + SSL_ca_file=>$xcatdir."/cert/ca.pem", + SSL_verify_mode=> 1 + ); + +unless ($listener) { + kill $pid; + syslog("err","xCAT service unable to open SSL services on $port: $!"); + closelog(); + die "ERROR:Unable to start xCAT service on port $port."; +} +closelog(); + +#setup signal in NotifHandler so that the cache can be updated +xCAT::NotifHandler::setup($$); + +my $peername; +until ($quit) { + next unless my $connection=$listener->accept; + my $child = fork(); #Yes we fork, IO::Socket::SSL is not threadsafe.. + + unless (defined $child) { + die "xCATd cannot fork"; + } + + if ($child == 0) { + $listener->close; + my $peerhost=undef; + my $peer=$connection->peer_certificate("owner"); + if ($peer) { + $peer =~ m/CN=([^\/]*)/; + $peername = $1; + } else { + $peername=undef; + } + my ($tmp) = $sitetab->getAttribs({'key'=>'domain'},'value'); + if (defined $tmp->{value}) { + $domain = $tmp->{value}; + } + $peerhost = gethostbyaddr(inet_aton($connection->peerhost),AF_INET); + $peerhost =~ s/\.$domain\.*$//; + $peerhost =~ s/-eth\d*$//; + $peerhost =~ s/-myri\d*$//; + $peerhost =~ s/-ib\d*$//; + #printf('info'.": xcatd: connection from ".($peername ? $peername . "@" . $peerhost : $peerhost)."\n"); + $$progname="xCATd SSL: Instance for ".($peername ? $peername ."@".$peerhost : $peerhost); + service_connection($connection,$peername,$peerhost); + exit(0); + } + $connection->close(SSL_no_shutdown => 1); #Without no shutdown, you can guess what the client ends up thinking.. +} +$listener->close; + + +my $parent_fd; +my %resps; + + + + + +sub plugin_command { + my $req = shift; + my $sock = shift; + my $callback = shift; + my %handler_hash; + use xCAT::NodeRange; + $Main::resps={}; + my @nodes; + if ($req->{node}) { + @nodes = @{$req->{node}}; + } elsif ($req->{noderange}) { + @nodes = noderange($req->{noderange}->[0]); + if (nodesmissed) { + my $rsp = {errorcode=>1,error=>"Invalid nodes in noderange:".join(',',nodesmissed)}; + if ($sock) { + print $sock XMLout($rsp,RootName=>'xcatresponse' ,NoAttr=>1); + } + return ($rsp); + } + } + if (@nodes) { $req->{node} = \@nodes; } + if (defined($cmd_handlers{$req->{command}->[0]})) { + my $hdlspec; + foreach (@{$cmd_handlers{$req->{command}->[0]}}) { + $hdlspec =$_->[1]; + my $ownmod = $_->[0]; + if ($hdlspec =~ /:/) { #Specificed a table lookup path for plugin name + my $table; + my $cols; + ($table,$cols) = split(/:/,$hdlspec); + my @colmns=split(/,/,$cols); + my @columns; + my $hdlrtable=xCAT::Table->new($table); + unless ($hdlrtable) { + #TODO: proper error handling + } + my $node; + my $colvals = {}; + foreach my $colu (@colmns) { + if ($colu =~ /=/) { #a value redirect to a pattern/specific name + my $coln; my $colv; + ($coln,$colv) = split(/=/,$colu,2); + $colvals->{$coln} = $colv; + push (@columns,$coln); + } else { + push (@columns,$colu); + } + } + + + unless (@nodes) { #register the plugin in the event of usage + $handler_hash{$ownmod} = 1; + } + foreach $node (@nodes) { + my $attribs = $hdlrtable->getNodeAttribs($node,\@columns); + unless (defined($attribs)) { next; } #TODO: This really ought to craft an unsupported response for this request + foreach (@columns) { + my $col=$_; + if (defined($attribs->{$col})) { + if ($colvals->{$col}) { #A pattern match style request. + if ($attribs->{$col} =~ /$colvals->{$col}/) { + $handler_hash{$ownmod}->{$node} = 1; + last; + } + } else { + $handler_hash{$attribs->{$col}}->{$node} = 1; + last; + } + } + } + } + } else { + unless (@nodes) { + $handler_hash{$hdlspec} = 1; + } + foreach (@nodes) { #Specified a specific plugin, not a table lookup + $handler_hash{$hdlspec}->{$_} = 1; + } + } + } + } else { + return 1; #TODO: error back that request has no known plugin for it + } + my $children=0; + $SIG{CHLD} = sub {while (waitpid(-1, WNOHANG) > 0) { $children--; } }; + my $check_fds; + if ($sock) { + $check_fds = new IO::Select; + } + foreach (keys %handler_hash) { + my $modname = $_; + if (-r $plugins_dir."/".$modname.".pm") { + require $plugins_dir."/".$modname.".pm"; + $children++; + my $pfd; #will be referenced for inter-process messaging. + my $child; + if ($sock) { #If $sock not passed in, don't fork.. + socketpair($pfd, $parent_fd,AF_UNIX,SOCK_STREAM,PF_UNSPEC) or die "socketpair: $!"; + #pipe($pfd,$cfd); + $parent_fd->autoflush(1); + $pfd->autoflush(1); + $child = fork; + } else { + $child = 0; + } + unless (defined $child) { die "Fork failed"; } + if ($child == 0) { + my $oldprogname=$$progname; + $$progname=$oldprogname.": $modname instance"; + if ($sock) { close $pfd; } + unless ($handler_hash{$_} == 1) { + my @nodes = sort {($a =~ /(\d+)/)[0] <=> ($b =~ /(\d+)/)[0] || $a cmp $b } (keys %{$handler_hash{$_}}); + $req->{node}=\@nodes; + } + no strict "refs"; + ${"xCAT_plugin::".$modname."::"}{process_request}->($req,$callback,\&do_request); + $$progname=$oldprogname; + if ($sock) { + close($parent_fd); + exit(0); + } + } else { + close $parent_fd; + $check_fds->add($pfd); + } + } + } + unless ($sock) { return $Main::resps }; + while ($children > 0) { + relay_fds($check_fds,$sock); + } + #while (relay_fds($check_fds,$sock)) {} + my %done; + $done{serverdone} = {}; + if ($req->{transid}) { + $done{transid}=$req->{transid}->[0]; + } + if ($sock) { print $sock XMLout(\%done,RootName => 'xcatresponse',NoAttr=>1); } +} + + +sub do_request { + my $req = shift; + my $second = shift; + my $rsphandler = \&build_response; + my $sock = undef; + if ($second) { + if (ref($second) eq "CODE") { + $rsphandler = $second; + } elsif (ref($second) eq "GLOB") { + $sock = $second; + } + } + + #my $sock = shift; #If no sock, will return a response hash + if ($cmd_handlers{$req->{command}->[0]}) { + return plugin_command($req,$sock,$rsphandler); + } elsif ($req->{command}->[0] eq "noderange" and $req->{noderange}) { + my @nodes = noderange($req->{noderange}->[0]); + my %resp; + if (nodesmissed) { + $resp{warning}="Invalid nodes in noderange:".join ',',nodesmissed; + } + $resp{serverdone} = {}; + @{$resp{node}}=@nodes; + if ($req->{transid}) { + $resp{transid}=$req->{transid}->[0]; + } + if ($sock) { + print $sock XMLout(\%resp,RootName => 'xcatresponse',NoAttr=>1); + } else { + return (\%resp); + } + } else { + my %resp=(error=>"Unsupported request"); + $resp{serverdone} = {}; + if ($req->{transid}) { + $resp{transid}=$req->{transid}->[0]; + } + if ($sock) { + print $sock XMLout(\%resp,RootName => 'xcatresponse',NoAttr=>1); + } else { + return (\%resp); + } + } +} + +sub convey_response { + my $resp=shift; + #TODO: This is where the following will/may happen: + #-Track transaction id + #-Save output for deferred commands + unless ($parent_fd) { + build_response($resp); + return; + } + print $parent_fd XMLout($resp,KeyAttr=>[], NoAttr=>1,RootName=>'xcatresponse'); + <$parent_fd>; #Block until parent acks data + #print "woo"; +# KeyAttr => [], NoAttr => 1) +} + +sub build_response { +# Handle responses from do_request calls made directly from a plugin +# Merge this response into the full response hash. We'll collect all +# the responses and ship it back on the return to the plugin. +# Note: Need to create a new "deep clone" copy of each response structure +# otherwise the next call will overwrite the reference we pushed on +# the response array + my $resp = shift; + foreach (keys %$resp) { + my $subresp = dclone($resp->{$_}); + push (@{$Main::resps->{$_}}, @{$subresp}); + } +} + +sub service_connection { + my $sock = shift; + my $peername = shift; + my $peerhost = shift; + my $peerport = $sock->peerport; + my %tables=(); + #some paranoid measures could reduce a third party abusing stage3 image to attempting to get USER/PASS for BMCs: + # -Well, minimally, ignore requests if requesting node is not in spconfig mode (stage3) + # -Option to generate a random password per 'getipmi' request. This reduces the exposure to a D.O.S. hopefully + #Give only 15 seconds of silence allowed or terminate connection. Using alarm since we are in thread-unsafe world anyway + my $timedout = 0; + + $SIG{ALRM} = sub { $timedout = 1; die; }; + eval { + my $request; + my $req=undef; + alarm(15); + while (<$sock>) { + alarm(0); + $request .= $_; + #$req = eval { XMLin($request, ForceArray => [ 'attribute' , 'attributepair' ]) }; + if ($request =~ m/<\/xcatrequest>/) { + $req = eval { XMLin($request, SuppressEmpty=>undef,ForceArray=>1) }; + #we have a full request.. + #printf $request."\n"; + $request=""; + if (validate($peername,$peerhost,$req)) { + $req->{'!xcat_authname'} = [$peername]; + $req->{'!xcat_clienthost'} = [$peerhost]; + $req->{'!xcat_clientport'}= [$peerport]; + $$progname="xCATd SSL: ".$req->{command}->[0]." for ".($peername ? $peername ."@".$peerhost : $peerhost); + if ($cmd_handlers{$req->{command}->[0]}) { + return plugin_command($req,$sock,\&convey_response); + } elsif ($req->{command}->[0] eq "noderange" and $req->{noderange}) { + my @nodes = noderange($req->{noderange}->[0]); + my %resp; + if (nodesmissed) { + $resp{warning}="Invalid nodes in noderange:".join ',',nodesmissed; + } + $resp{serverdone} = {}; + @{$resp{node}}=@nodes; + if ($req->{transid}) { + $resp{transid}=$req->{transid}->[0]; + } + print $sock XMLout(\%resp,RootName => 'xcatresponse',NoAttr=>1); + next; + } else { + my %resp=(error=>"Unsupported request"); + $resp{serverdone} = {}; + if ($req->{transid}) { + $resp{transid}=$req->{transid}->[0]; + } + print $sock XMLout(\%resp,RootName => 'xcatresponse',NoAttr=>1); + next; + } + } else { + my %resp=(error=>"Permission denied for request"); + $resp{serverdone} = {}; + if ($req->{transid}) { + $resp{transid}=$req->{transid}->[0]; + } + my $response=XMLout(\%resp,RootName =>'xcatresponse',NoAttr => 1); + print $sock $response; + next; + } + } + alarm(15); + } + }; + if ($@) { # The eval statement caught a program bug.. + unless ($@ =~ /^SIGPIPE/) { + syslog("local1|err","xcatd: possible BUG encountered by xCAT TCP service: ".$@); + } else { + syslog("local1|info","xcatd: Unexpected client disconnect"); + } + } + alarm(0); + foreach (keys %tables) { + $tables{$_}->commit; + } + $sock->close; + if ($timedout == 1) { + printf ("Client timeout"); + } +} + +sub relay_fds { #Relays file descriptors from pipes to children to the SSL socket + my $fds = shift; + my $sock = shift; + unless ($sock) { return 0; } + my $collate = ( scalar @_ > 0 ? shift : 0); + my @readyset = $fds->can_read(1); + my $rfh; + my $rc = @readyset; + my $text; + foreach $rfh (@readyset) { #go through each child, extract a complete, atomic message + my $line; + while ($line = <$rfh>) { #Will break on complete messages, avoid interleave + print $sock $line; + if ($line =~ /<\/xcatresponse>/) { + last; + } + } + if ($line) { + print $rfh "fin\n"; #Notify convey_response message done + } else { + $fds->remove($rfh); + close($rfh); + } + } + return $rc; +} + +sub validate { + #BIG TODO, make this do something meaningful + #here is where we check if $peername is allowed to do $request. $peername if set signifies client has a + #cert that the xCAT CA accepted. This will be a policy table with $peername as key + #things like 'stage2/stage3' and install images will have no client certificate. + #A client key for something that a third party could easily tftp down themselves means nothing + #however, privacy between the nodes can be maintained, and $peerhost will be checked just like 1.2.0. +# returns 1 if policy engine allows the action, 0 if denied + my $peername=shift; + my $peerhost=shift; + my $request=shift; + my $policytable = xCAT::Table->new('policy'); + unless ($policytable) { + syslog("err","Unable to open policy data, denying"); + return 0; + } + my @policies = $policytable->getTable; + my $rule; + foreach $rule (@policies) { + if ($rule->{name} and $rule->{name} ne '*') { + #TODO: more complex matching (lists, wildcards) + next unless ($peername eq $rule->{name}); + } + if ($rule->{time} and $rule->{time} ne '*') { + #TODO: time ranges + } + if ($rule->{host} and $rule->{host} ne '*') { + #TODO: more complex matching (lists, noderanges?, wildcards) + next unless ($peerhost eq $rule->{host}); + } + if ($rule->{commands} and $rule->{commands} ne '*') { + #TODO: syntax for multiple commands + next unless ($request->{command}->[0] eq $rule->{commands}); + } + if ($rule->{parameters} and $rule->{parameters} ne '*') { + next; #TODO: not ignore this field + } + if ($rule->{noderange} and $rule->{noderange} ne '*') { + next; #TODO: not ignore this field + } + # If we are still in, that means this rule is the first match and dictates behavior. + if ($rule->{rule}) { + if ($rule->{rule} =~ /allow/i or $rule->{rule} =~ /accept/i) { + my $logst = "xCAT: Allowing ".$request->{command}->[0]; + if ($peername) { $logst .= " for " . $peername }; + syslog("authpriv|info",$logst); + return 1; + } else { + my $logst = "xCAT: Denying ".$request->{command}->[0]; + if ($peername) { $logst .= " for " . $peername }; + syslog("authpriv|info",$logst); + return 0; + } + } else { #Shouldn't be possible.... + syslog("err","Impossible line in xcatd reached"); + return 0; + } + } + #Reached end of policy table, reject by default. + syslog("err","Request matched no policy rule: ".$request->{command}->[0]); + return 0; +} + diff --git a/xCAT-server-2.0/usr/share/xcat/ca/Makefile b/xCAT-server-2.0/usr/share/xcat/ca/Makefile new file mode 100644 index 000000000..d81bd2e18 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/ca/Makefile @@ -0,0 +1,73 @@ +# $Id: Makefile,v 1.4 2006/06/20 18:55:37 jmates Exp $ +# +# NOTE If running OpenSSL 0.9.8a or higher, see -newkey, below. +# +# Automates the setup of a custom Certificate Authority and provides +# routines for signing and revocation of certificates. To use, first +# customize the commands in this file and the settings in openssl.cnf, +# then run: +# +# make init +# +# Then, copy in certificate signing requests, and ensure their suffix is +# .csr before signing them with the following command: +# +# make sign +# +# To revoke a key, name the certificate file with the cert option +# as shown below: +# +# make revoke cert=foo.cert +# +# This will revoke the certificate and call gencrl; the revocation list +# will then need to be copied somehow to the various systems that use +# your CA cert. + +requests = *.csr + +sign: ${requests} + +# remove -batch option if want chance to not certify a particular request +${requests}: FORCE + @openssl ca -config openssl.cnf -in $@ -out ${@:.csr=.cert} + @[ -f ${@:.csr=.cert} ] && rm $@ + +revoke: + @test $${cert:?"usage: make revoke cert=certificate"} + @openssl ca -config openssl.cnf -revoke $(cert) + @$(MAKE) gencrl + +gencrl: + @openssl ca -config openssl.cnf -gencrl -out ca-crl.pem + +clean: + -rm ${requests} + +# creates required supporting files, CA key and certificate +init: + @test ! -f serial + @mkdir crl certs private + @chmod go-rwx private + @echo '01' > serial + @touch index + # NOTE use "-newkey rsa:2048" if running OpenSSL 0.9.8a or higher +# @openssl req -nodes -config openssl.cnf -days 2650 -x509 -newkey rsa:2048 -out ca-cert.pem -outform PEM + +help: + @echo make sign + @echo ' - signs all *.csr files in this directory' + @echo + @echo make revoke cert=filename + @echo ' - revokes certificate in named file and calls gencrl' + @echo + @echo make gencrl + @echo ' - updates Certificate Revocation List (CRL)' + @echo + @echo make clean + @echo ' - removes all *.csr files in this directory' + @echo + @echo make init + @echo ' - required initial setup command for new CA' + +# for legacy make support +FORCE: diff --git a/xCAT-server-2.0/usr/share/xcat/ca/openssl.cnf.tmpl b/xCAT-server-2.0/usr/share/xcat/ca/openssl.cnf.tmpl new file mode 100644 index 000000000..ba91c5ba2 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/ca/openssl.cnf.tmpl @@ -0,0 +1,313 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca' and 'req'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = ##XCATCADIR## # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several ctificates with same subject. +new_certs_dir = $dir/certs # default place for new certs. + +certificate = $dir/ca-cert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/ca-key.pem # The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extentions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +# copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 7300 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = sha1 # which md to use. +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = optional +stateOrProvinceName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = private/ca-key.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString. +# utf8only: only UTF8Strings. +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings +# so use this option with caution! +string_mask = nombstr + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +#countryName = Country Name (2 letter code) +#countryName_default = US +#countryName_min = 2 +#countryName_max = 2 + +#stateOrProvinceName = State or Province Name (full name) +#stateOrProvinceName_default = Some-State + +#localityName = Locality Name (eg, city) + +#0.organizationName = Organization Name (eg, company) +#0.organizationName_default = Internet Widgits Pty Ltd + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +#organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (eg, YOUR name) +commonName_max = 64 + +#emailAddress = Email Address +#emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +#challengePassword = A challenge password +#challengePassword_min = 4 +#challengePassword_max = 20 + +#unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer:always + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always,issuer:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo diff --git a/xCAT-server-2.0/usr/share/xcat/cons/blade b/xCAT-server-2.0/usr/share/xcat/cons/blade new file mode 100755 index 000000000..64cc8131e --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/cons/blade @@ -0,0 +1,45 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +use xCAT::Table; +use File::Basename; +my $scriptname = $0; + +$mptab = xCAT::Table->new('mp'); +unless ($mptab) { + sleep 5; #Try not to overwhelm logfiles... + die "mp table must be configured"; +} +$mpatab = xCAT::Table->new('mpa'); +$passtab = xCAT::Table->new('passwd'); + +my $username = "USERID"; +my $passsword = "PASSW0RD"; +my $mm; +my $slot; +my $dba; +if ($passtab) { + ($dba) = $passtab->getAttribs({key=>blade},qw(username password)); + if ($dba->{username}) { + $username = $dba->{username}; + } + if ($dba->{password}) { + $password = $dba->{password}; + } +} + +$dba = $mptab->getNodeAttribs($ARGV[0],[qw(mpa id)]); +$mm = $dba->{mpa}; +$slot = $dba->{id}; +if ($mpatab) { + ($dba) = $mpatab->getAttribs({mpa=>$mm},qw(username password)); + if ($dba) { + if ($dba->{username}) { $username = $dba->username; } + if ($dba->{password}) { $password = $dba->password; } + } +} +my $pathtochild= dirname($scriptname). "/"; +exec $pathtochild."blade.expect $mm $slot $username $password"; + + + + diff --git a/xCAT-server-2.0/usr/share/xcat/cons/blade.expect b/xCAT-server-2.0/usr/share/xcat/cons/blade.expect new file mode 100755 index 000000000..e4fe11cb7 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/cons/blade.expect @@ -0,0 +1,35 @@ +#!/usr/bin/env expect + +set timeout 45 + +set bcmm [lindex $argv 0] +set bayno [lindex $argv 1] +set username [lindex $argv 2] +set password [lindex $argv 3] +set cmdline "console -o -T blade\[$bayno\]" + +spawn telnet $bcmm +expect -re ".*username: " +send "$username\r" +expect -re ".*password: " +send "$password\r" +expect -re ".*system> " +send "$cmdline\r" +expect { + eof { + exit + } + -re ".*system> " { + sleep 5 + send "$cmdline\r" + } + "?" { + interact { + -o + "system> " { + sleep 5 + send "$cmdline\r" + } + } + } +} diff --git a/xCAT-server-2.0/usr/share/xcat/cons/ipmi b/xCAT-server-2.0/usr/share/xcat/cons/ipmi new file mode 100755 index 000000000..512b15665 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/cons/ipmi @@ -0,0 +1,29 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +use xCAT::Table; +my $dba; +my $ipmitab = xCAT::Table->new('ipmi'); +unless ($ipmitab) { sleep 5; die "Unable to open IPMI table"; } +my $passtab = xCAT::Table->new('passwd'); +my $username = 'USERID'; +my $password = 'PASSW0RD'; +my $node = $ARGV[0]; +my $bmc = $node; +if ($passtab) { + ($dba) = $passtab->getAttribs({key=>'ipmi'},qw(username password)); + if ($dba) { + if ($dba->{username}) { $username = $dba->{username}; } + if ($dba->{password}) { $password = $dba->{password}; } + } +} + +$dba = $ipmitab->getNodeAttribs($ARGV[0],[qw(bmc username password)]); +if ($dba) { + if ($dba->{bmc}) { $bmc = $dba->{bmc}; } + if ($dba->{username}) { $username = $dba->{username}; } + if ($dba->{password}) { $password = $dba->{password}; } +} +system "ipmitool -I lanplus -U $username -P $password -H $bmc sol deactivate"; #Stop any active session +exec "ipmitool -I lanplus -U $username -P $password -H $bmc sol activate"; + + diff --git a/xCAT-server-2.0/usr/share/xcat/install/centos/all.tmpl b/xCAT-server-2.0/usr/share/xcat/install/centos/all.tmpl new file mode 100644 index 000000000..01de93e02 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/install/centos/all.tmpl @@ -0,0 +1,184 @@ +#RedHat Enterprise Linux 4 AS Only +#egan@us.ibm.com +# + +lang en_US +langsupport en_US +network --bootproto dhcp + +# +# Where's the source? +# nfs --server hostname.of.server or IP --dir /path/to/RH/CD/image +# +nfs --server #XCATVAR:INSTALL_NFS# --dir #XCATVAR:INSTALL_SRC_DIR# + +#device ethernet e100 +keyboard "#TABLE:site.tab:keyboard:1#" + +# +# Clear the MBR +# +zerombr yes + +# +# Wipe out the disk +# +clearpart --all --initlabel +#clearpart --linux + +# +# Customize to fit your needs +# /boot is strongly recommended +# + +#No RAID +part /boot --size 50 --ondisk sda --fstype ext3 +part swap --size 1024 --ondisk sda +part / --size 6000 --grow --ondisk sda --fstype ext3 + +#RAID 0 /scr for performance +#part / --size 1024 --ondisk sda +#part swap --size 512 --ondisk sda +#part /var --size 1024 --ondisk sdb +#part swap --size 512 --ondisk sdb +#part raid.01 --size 1 --grow --ondisk sda +#part raid.02 --size 1 --grow --ondisk sdb +#raid /scr --level 0 --device md0 raid.01 raid.02 + +#Full RAID 1 Sample +#part raid.01 --size 50 --ondisk sda +#part raid.02 --size 50 --ondisk sdb +#raid /boot --level 1 --device md0 raid.01 raid.02 +# +#part raid.11 --size 1024 --ondisk sda +#part raid.12 --size 1024 --ondisk sdb +#raid / --level 1 --device md1 raid.11 raid.12 +# +#part raid.21 --size 1024 --ondisk sda +#part raid.22 --size 1024 --ondisk sdb +#raid /var --level 1 --device md2 raid.21 raid.22 +# +#part raid.31 --size 1024 --ondisk sda +#part raid.32 --size 1024 --ondisk sdb +#raid swap --level 1 --device md3 raid.31 raid.32 +# +#part raid.41 --size 1 --grow --ondisk sda +#part raid.42 --size 1 --grow --ondisk sdb +#raid /scr --level 1 --device md4 raid.41 raid.42 + +# +# bootloader config +# --append +# --useLilo +# --md5pass +# +bootloader + +# +# install or upgrade +# +install + +# +# text mode install (default is graphical) +# +text + +# +# firewall +# +firewall --disabled + +# +# mouse selection +# +#mouse genericps/2 --emulthree +mouse none + +# +# Select a zone +# Add the --utc switch if your hardware clock is set to GMT +# +#timezone US/Hawaii +#timezone US/Pacific +#timezone US/Mountain +#timezone US/Central +#timezone US/Eastern +timezone --utc #TABLE:site.tab:timezone:1# + +# +# Don't do X +# +skipx + +# +# Geez! +# +key --skip + +# +# To generate an encrypted root password use: +# +# perl -e 'print crypt("blah","Xa") . "\n";'p +# openssl passwd -apr1 -salt xxxxxxxx password +# +# where "blah" is your root password. +# +#rootpw --iscrypted XaLGAVe1C41x2 +#rootpw XaLGAVe1C41x2 --iscrypted +#rootpw #TABLE:passwd.tab:rootpw:1# +rootpw --iscrypted #COMMAND:perl -e 'print crypt("#TABLE:passwd.tab:rootpw:1#","Xa") . "\n";'p# + +# +# NIS setup: auth --enablenis --nisdomain sensenet +# --nisserver neptune --useshadow --enablemd5 +# +# OR +auth --useshadow --enablemd5 + +# +# SE Linux +# +selinux --disabled + +# +# Reboot after installation +# +reboot + +# +#end of section +# +%packages --resolvedeps +@Everything +@ Everything +@ System Tools +@ X Window System +@ Legacy Software Development +#kernel-smp +autofs +ksh +tcsh +ntp +tftp +xinetd +rsh +rsh-server +psacct +nfs-utils +net-snmp +rsync +yp-tools +ypserv +ypbind +m4 +sendmail-cf +gdb +binutils +openssh-server +util-linux +compat-libstdc++-33 +%pre +#COMMAND:genscript pre #ENV:OSVER# #ENV:ARCH## +%post +#COMMAND:genscript post #ENV:OSVER# #ENV:ARCH## diff --git a/xCAT-server-2.0/usr/share/xcat/install/centos/compute.tmpl b/xCAT-server-2.0/usr/share/xcat/install/centos/compute.tmpl new file mode 100644 index 000000000..17fc262c9 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/install/centos/compute.tmpl @@ -0,0 +1,178 @@ +#RedHat Enterprise Linux 4 AS Only +#egan@us.ibm.com +# + +lang en_US +langsupport en_US +network --bootproto dhcp + +# +# Where's the source? +# nfs --server hostname.of.server or IP --dir /path/to/RH/CD/image +# +#nfs --server #XCATVAR:INSTALL_NFS# --dir #XCATVAR:INSTALL_SRC_DIR# +url --url http://#TABLE:noderes:$NODE:nfsserver#/install/#TABLE:nodetype:$NODE:os#/#TABLE:nodetype:$NODE:arch# + +#device ethernet e100 +keyboard "us" + +# +# Clear the MBR +# +zerombr yes + +# +# Wipe out the disk +# +clearpart --all --initlabel +#clearpart --linux + +# +# Customize to fit your needs +# + +#No RAID +#/boot really significant for this sort of setup nowadays? +#part /boot --size 50 --fstype ext3 +part swap --size 1024 +part / --size 1 --grow --fstype ext3 + +#RAID 0 /scr for performance +#part / --size 1024 --ondisk sda +#part swap --size 512 --ondisk sda +#part /var --size 1024 --ondisk sdb +#part swap --size 512 --ondisk sdb +#part raid.01 --size 1 --grow --ondisk sda +#part raid.02 --size 1 --grow --ondisk sdb +#raid /scr --level 0 --device md0 raid.01 raid.02 + +#Full RAID 1 Sample +#part raid.01 --size 50 --ondisk sda +#part raid.02 --size 50 --ondisk sdb +#raid /boot --level 1 --device md0 raid.01 raid.02 +# +#part raid.11 --size 1024 --ondisk sda +#part raid.12 --size 1024 --ondisk sdb +#raid / --level 1 --device md1 raid.11 raid.12 +# +#part raid.21 --size 1024 --ondisk sda +#part raid.22 --size 1024 --ondisk sdb +#raid /var --level 1 --device md2 raid.21 raid.22 +# +#part raid.31 --size 1024 --ondisk sda +#part raid.32 --size 1024 --ondisk sdb +#raid swap --level 1 --device md3 raid.31 raid.32 +# +#part raid.41 --size 1 --grow --ondisk sda +#part raid.42 --size 1 --grow --ondisk sdb +#raid /scr --level 1 --device md4 raid.41 raid.42 + +# +# bootloader config +# --append +# --useLilo +# --md5pass +# +bootloader + +# +# install or upgrade +# +install + +# +# text mode install (default is graphical) +# +text + +# +# firewall +# +firewall --disabled + +# +# mouse selection +# +#mouse genericps/2 --emulthree +mouse none + +# +# Select a zone +# Add the --utc switch if your hardware clock is set to GMT +# +#timezone US/Hawaii +#timezone US/Pacific +#timezone US/Mountain +#timezone US/Central +#timezone US/Eastern +timezone --utc #TABLE:site:key=timezone:value# + +# +# Don't do X +# +skipx + + +# +# To generate an encrypted root password use: +# +# perl -e 'print crypt("blah","Xa") . "\n";'p +# openssl passwd -apr1 -salt xxxxxxxx password +# +# where "blah" is your root password. +# +#rootpw --iscrypted XaLGAVe1C41x2 +#rootpw XaLGAVe1C41x2 --iscrypted +rootpw --iscrypted #CRYPT:passwd:key=system,username=root:password# +#rootpw --iscrypted #COMMAND:perl -e 'print crypt("#TABLE:passwd.tab:rootpw:1#","Xa") . "\n";'p# + +# +# NIS setup: auth --enablenis --nisdomain sensenet +# --nisserver neptune --useshadow --enablemd5 +# +# OR +auth --useshadow --enablemd5 + +# +# SE Linux +# +selinux --disabled + +# +# Reboot after installation +# +reboot + +# +#end of section +# +%packages --resolvedeps +@ Network Servers +@ System Tools +@ X Window System +@ Legacy Software Development +autofs +tcsh +ntp +tftp +xinetd +rsh +rsh-server +psacct +nfs-utils +net-snmp +rsync +yp-tools +ypserv +ypbind +m4 +sendmail-cf +gdb +binutils +openssh-server +util-linux +compat-libstdc++-33 +%pre +#INCLUDE:../scripts/pre.rh# +%post +#INCLUDE:../scripts/post.rh# diff --git a/xCAT-server-2.0/usr/share/xcat/install/rh/compute.tmpl b/xCAT-server-2.0/usr/share/xcat/install/rh/compute.tmpl new file mode 100644 index 000000000..e967eb2f6 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/install/rh/compute.tmpl @@ -0,0 +1,180 @@ +#RedHat Enterprise Linux 4 AS Only +#egan@us.ibm.com +# + +lang en_US +langsupport en_US +network --bootproto dhcp + +# +# Where's the source? +# nfs --server hostname.of.server or IP --dir /path/to/RH/CD/image +# +#nfs --server #XCATVAR:INSTALL_NFS# --dir #XCATVAR:INSTALL_SRC_DIR# +url --url http://#TABLE:noderes:$NODE:nfsserver#/install/#TABLE:nodetype:$NODE:os#/#TABLE:nodetype:$NODE:arch# + +#device ethernet e100 +keyboard "us" + +# +# Clear the MBR +# +zerombr yes + +# +# Wipe out the disk +# +clearpart --all --initlabel +#clearpart --linux +key --skip + +# +# Customize to fit your needs +# + +#No RAID +#/boot really significant for this sort of setup nowadays? +#part /boot --size 50 --fstype ext3 +part swap --size 1024 +part / --size 1 --grow --fstype ext3 + +#RAID 0 /scr for performance +#part / --size 1024 --ondisk sda +#part swap --size 512 --ondisk sda +#part /var --size 1024 --ondisk sdb +#part swap --size 512 --ondisk sdb +#part raid.01 --size 1 --grow --ondisk sda +#part raid.02 --size 1 --grow --ondisk sdb +#raid /scr --level 0 --device md0 raid.01 raid.02 + +#Full RAID 1 Sample +#part raid.01 --size 50 --ondisk sda +#part raid.02 --size 50 --ondisk sdb +#raid /boot --level 1 --device md0 raid.01 raid.02 +# +#part raid.11 --size 1024 --ondisk sda +#part raid.12 --size 1024 --ondisk sdb +#raid / --level 1 --device md1 raid.11 raid.12 +# +#part raid.21 --size 1024 --ondisk sda +#part raid.22 --size 1024 --ondisk sdb +#raid /var --level 1 --device md2 raid.21 raid.22 +# +#part raid.31 --size 1024 --ondisk sda +#part raid.32 --size 1024 --ondisk sdb +#raid swap --level 1 --device md3 raid.31 raid.32 +# +#part raid.41 --size 1 --grow --ondisk sda +#part raid.42 --size 1 --grow --ondisk sdb +#raid /scr --level 1 --device md4 raid.41 raid.42 + +# +# bootloader config +# --append +# --useLilo +# --md5pass +# +bootloader + +# +# install or upgrade +# +install + +# +# text mode install (default is graphical) +# +text + +# +# firewall +# +firewall --disabled + +# +# mouse selection +# +#mouse genericps/2 --emulthree +mouse none + +# +# Select a zone +# Add the --utc switch if your hardware clock is set to GMT +# +#timezone US/Hawaii +#timezone US/Pacific +#timezone US/Mountain +#timezone US/Central +#timezone US/Eastern +timezone --utc #TABLE:site:key=timezone:value# + +# +# Don't do X +# +skipx + + +# +# To generate an encrypted root password use: +# +# perl -e 'print crypt("blah","Xa") . "\n";'p +# openssl passwd -apr1 -salt xxxxxxxx password +# +# where "blah" is your root password. +# +#rootpw --iscrypted XaLGAVe1C41x2 +#rootpw XaLGAVe1C41x2 --iscrypted +rootpw --iscrypted #CRYPT:passwd:key=system,username=root:password# +#rootpw --iscrypted #COMMAND:perl -e 'print crypt("#TABLE:passwd.tab:rootpw:1#","Xa") . "\n";'p# + +# +# NIS setup: auth --enablenis --nisdomain sensenet +# --nisserver neptune --useshadow --enablemd5 +# +# OR +auth --useshadow --enablemd5 + +# +# SE Linux +# +selinux --disabled + +# +# Reboot after installation +# +reboot + +# +#end of section +# +%packages --resolvedeps +@ Network Servers +@ System Tools +@ X Window System +@ Legacy Software Development +autofs +ksh +tcsh +ntp +tftp +xinetd +rsh +rsh-server +psacct +nfs-utils +net-snmp +rsync +yp-tools +ypserv +ypbind +m4 +sendmail-cf +gdb +binutils +openssh-server +util-linux +compat-libstdc++-33 +%pre +#INCLUDE:../scripts/pre.rh# +%post +#INCLUDE:../scripts/post.rh# diff --git a/xCAT-server-2.0/usr/share/xcat/install/scripts/chroot.sles b/xCAT-server-2.0/usr/share/xcat/install/scripts/chroot.sles new file mode 100644 index 000000000..9b258d5d3 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/install/scripts/chroot.sles @@ -0,0 +1,44 @@ + + + diff --git a/xCAT-server-2.0/usr/share/xcat/install/scripts/post.rh b/xCAT-server-2.0/usr/share/xcat/install/scripts/post.rh new file mode 100644 index 000000000..d833da66f --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/install/scripts/post.rh @@ -0,0 +1,58 @@ +# +# Setup hostname +# +echo "post scripts" >/root/post.log +export PRINIC=#TABLE:noderes:THISNODE:primarynic# +if [ -z "$PRINIC" ] +then + export PRINIC=eth0 +fi +IP=$(ifconfig $PRINIC | grep inet | awk '{print $2}' | awk -F: '{print $2}') +if [ -z $IP ] +then + dhclient eth0 + IP=$(ifconfig $PRINIC | grep inet | awk '{print $2}' | awk -F: '{print $2}') +fi +echo "search #TABLE:site:key=domain:value#" >/etc/resolv.conf +for i in $(echo #TABLE:site:key=nameservers:value# | tr ',' ' ') +do + echo "nameserver $i" +done >>/etc/resolv.conf +export HOSTNAME=$(host $IP 2>/dev/null | awk '{print $5}' | awk -F. '{print $1}') +hostname $HOSTNAME +# +# Run xCAT post install +# +export MASTER_IP="#XCATVAR:XCATMASTER#" +export MASTER_IPS="#XCATVAR:XCATMASTER#" +mkdir -p /xcatpost +RAND=$(perl -e 'print int(rand(50)). "\n"') +sleep $RAND +for i in $(seq 1 20) +do + GOTIT=0 + for i in $MASTER_IPS + do +# mount -r $i:$XCATROOT $XCATROOT + mount -o ro,nolock $i:/install/postscripts /xcatpost + if [ "$?" = "0" ] + then + GOTIT=1 + break + fi + done + if [ "$GOTIT" = "1" ] + then + break + fi + RAND=$(perl -e 'print int(rand(5)). "\n"') + sleep $RAND +done +#mount -r #XCATVAR:MASTER_IP#:$XCATROOT $XCATROOT +#mount -o ro,nolock #XCATVAR:MASTER_IP#:$XCATROOT $XCATROOT +#$XCATROOT/bin/postage +/xcatpost/#TABLE:nodelist:THISNODE:node# +cd / +umount /xcatpost +rmdir /xcatpost +exit 0 diff --git a/xCAT-server-2.0/usr/share/xcat/install/scripts/post.sles b/xCAT-server-2.0/usr/share/xcat/install/scripts/post.sles new file mode 100644 index 000000000..55bee93e1 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/install/scripts/post.sles @@ -0,0 +1,84 @@ + + + diff --git a/xCAT-server-2.0/usr/share/xcat/install/scripts/pre.rh b/xCAT-server-2.0/usr/share/xcat/install/scripts/pre.rh new file mode 100644 index 000000000..e9e27a521 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/install/scripts/pre.rh @@ -0,0 +1,123 @@ +if grep n8r /proc/cmdline >& /dev/null; +then + stty crtscts +fi +for x in 0 1 2 3 4 5 6 7 8 +do + mknod /dev/vcs$x c 7 $x + mknod /dev/vcsa$x c 7 $[$x+128] +done +chmod 644 /dev/vcs* +chown root /dev/vcs* + +cat >/tmp/foo.py < 0): + percent = int(((count - 2) * 100)/numpack + .5) + if(percent > 100): + percent = 100 + if(percent < 0): + percent = 0 + r2 = re.compile("^Installing (.*)\.") + m2 = r2.search(line) + if m2: + newline = m2.group(1) + newline = newline + " (" + newline = newline + str(percent) + newline = newline + "%)" +# newline = newline + " [" +# count = count - 2 +# newline = newline + str(count) +# newline = newline + "/" +# newline = newline + str(numpack) +# newline = newline + "]" + else: + newline = "prep" + line = "installing " + newline + if(line and post): + line = "installing " + line + if(not line): + line = "installing prep" + newSocket.send(line) + break +# if(command[0] == "sh"): #DEBUG purposes only, wide open root priv command here. +# newcommand = "" +# for i in command[1:]: +# newcommand = newcommand + i + " " +# output = os.popen(newcommand).read() +# newSocket.send(output) +# break + if(command[0] == "screendump"): + newcommand = "cat /dev/vcs" + for i in command[1:]: + newcommand = newcommand + i + output = os.popen(newcommand).read() + newSocket.send(output) + break + + newSocket.close() + +finally: + sock.close() +EOF + +chmod 755 /tmp/foo.py + +/tmp/foo.py >/foo.log 2>&1 & diff --git a/xCAT-server-2.0/usr/share/xcat/install/scripts/pre.sles b/xCAT-server-2.0/usr/share/xcat/install/scripts/pre.sles new file mode 100644 index 000000000..b2809a8c4 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/install/scripts/pre.sles @@ -0,0 +1,98 @@ + + + diff --git a/xCAT-server-2.0/usr/share/xcat/install/sles/compute.tmpl b/xCAT-server-2.0/usr/share/xcat/install/sles/compute.tmpl new file mode 100644 index 000000000..ad15d6cfe --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/install/sles/compute.tmpl @@ -0,0 +1,116 @@ + + + + + + true + true + + false + false + mbr + + + + GMT + #TABLE:site:key=timezone:value# + + + english-us + + en_US + + false + false + false + true + + + non + + + + + /dev/sda + true + all + + + ext2 + /boot + 131 + 1 + 50MB + + + swap + 2 + auto + + + ext3 + /var + 131 + 3 + 2GB + + + ext3 + / + 131 + 4 + max + + + + + + + base + base-32bit + 32bit + x11-32bit + x11 + + + xntp + rsync + + + + + + + root + #CRYPT:passwd:key=system,username=root:password# + true + + + + + + + true + true + local + linux + + + + dhcp + eth0 + onboot + + + + false + + + + + #INCLUDE:../scripts/pre.sles# + #INCLUDE:../scripts/chroot.sles# + #INCLUDE:../scripts/post.sles# + + + diff --git a/xCAT-server-2.0/usr/share/xcat/postscripts/enablesysreq b/xCAT-server-2.0/usr/share/xcat/postscripts/enablesysreq new file mode 100755 index 000000000..d2e992df3 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/postscripts/enablesysreq @@ -0,0 +1,19 @@ +#!/bin/ksh +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#egan@us.ibm.com +#(C)IBM Corp + +case "$OSVER" in + sles*|suse*|ul*) + perl -pi -e 's/^ENABLE_SYSRQ.*/ENABLE_SYSRQ="yes"/' /etc/sysconfig/sysctl + ;; + rh*) + if egrep "^kernel\.sysrq" /etc/sysctl.conf >/dev/null 2>&1 + then + perl -pi -e 's/^kernel\.sysrq.*/kernel.sysrq = 1/' /etc/sysctl.conf + else + echo "kernel.sysrq = 1" >>/etc/sysctl.conf + fi + ;; +esac + diff --git a/xCAT-server-2.0/usr/share/xcat/postscripts/reboot b/xCAT-server-2.0/usr/share/xcat/postscripts/reboot new file mode 100755 index 000000000..9c34fe8b6 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/postscripts/reboot @@ -0,0 +1,10 @@ +#!/bin/bash +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#egan@us.ibm.com +#(C)IBM Corp +# + +(sleep 75;/sbin/reboot) & + +exit 0 + diff --git a/xCAT-server-2.0/usr/share/xcat/postscripts/remoteshell b/xCAT-server-2.0/usr/share/xcat/postscripts/remoteshell new file mode 100755 index 000000000..a1da00400 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/postscripts/remoteshell @@ -0,0 +1,30 @@ +#!/bin/ksh +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#egan@us.ibm.com +#(C)IBM Corp +# + +if [ -r /etc/ssh/sshd_config ] +then + logger -t xcat "Install: setup /etc/ssh/sshd_config" + cp /etc/ssh/sshd_config /etc/ssh/sshd_config.ORIG + perl -pi -e 's/^X11Forwarding .*$/X11Forwarding yes/' /etc/ssh/sshd_config + perl -pi -e 's/^KeyRegenerationInterval .*$/KeyRegenerationInterval 0/' /etc/ssh/sshd_config + perl -pi -e 's/(.*MaxStartups.*)/#\1/' /etc/ssh/sshd_config + echo "MaxStartups 1024" >>/etc/ssh/sshd_config + echo "PasswordAuthentication no" >>/etc/ssh/sshd_config +fi + +if [ -d /xcatpost/.ssh ] +then + logger -t xcat "Install: setup root .ssh" + cd /xcatpost/.ssh + mkdir -p /root/.ssh + cp -f * /root/.ssh + chmod 700 /root/.ssh + chmod 600 /root/.ssh/* +fi + + +exit 0 + diff --git a/xCAT-server-2.0/usr/share/xcat/postscripts/resyslog b/xCAT-server-2.0/usr/share/xcat/postscripts/resyslog new file mode 100755 index 000000000..8b1c3e47f --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/postscripts/resyslog @@ -0,0 +1,11 @@ +#!/bin/ksh +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#egan@us.ibm.com +#(C)IBM Corp +# + + +echo "*.* @$(getent hosts $MASTER | awk '{print $1}')" >/etc/syslog.conf + +exit 0 + diff --git a/xCAT-server-2.0/usr/share/xcat/postscripts/serialconsole b/xCAT-server-2.0/usr/share/xcat/postscripts/serialconsole new file mode 100755 index 000000000..7640f6d7e --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/postscripts/serialconsole @@ -0,0 +1,23 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +GRUB="" +for i in /boot/grub/grub.conf /boot/grub/menu.lst +do + if [ -r "$i" ] + then + logger -t xcat "Install: got GRUB at $i" + GRUB=$i + GRUBDIR=$(dirname $GRUB) + GRUBFILE=$(basename $GRUB) + break + fi +done + +if [ -n "$GRUB" ] +then + cd $GRUBDIR + perl -pi -e 's/^gfxmenu/#gfxmenu/' $GRUBFILE + perl -pi -e 's/^color/#color/' $GRUBFILE + perl -pi -e 's/^serial/#serial/' $GRUBFILE + perl -pi -e 's/^terminal/#terminal/' $GRUBFILE + perl -pi -e 's/quiet//g' $GRUBFILE +fi diff --git a/xCAT-server-2.0/usr/share/xcat/postscripts/syslog b/xCAT-server-2.0/usr/share/xcat/postscripts/syslog new file mode 100755 index 000000000..e291a5f3c --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/postscripts/syslog @@ -0,0 +1,40 @@ +#!/bin/ksh +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#egan@us.ibm.com +#(C)IBM Corp +# + +HARD_SYSLOG=$1 + +if [ -n "$HARD_SYSLOG" ] +then + MASTER=$HARD_SYSLOG +fi + +mv -f /etc/syslog.conf /etc/syslog.conf.ORIG +echo "*.* @$(getent hosts $MASTER | awk '{print $1}')" >/etc/syslog.conf + +case $OSVER in + sles[89]|suse8*|suse9*|suse10|ul*) + if grep 'SYSLOGD_PARAMS="-m0' /etc/sysconfig/syslog >/dev/null 2>&1 + then + : + else + perl -pi -e 's/SYSLOGD_PARAMS="/SYSLOGD_PARAMS="-m0 /' /etc/sysconfig/syslog + fi + /etc/init.d/syslog restart + ;; + rh*) + /etc/rc.d/init.d/syslog start + ;; + sles10) + echo 'destination loghost { udp("10.64.0.1" port(514)); };' >> /etc/syslog-ng/syslog-ng.conf + echo 'log { source(src); destination(loghost); };' >> /etc/syslog-ng/syslog-ng.conf + /etc/init.d/syslog restart + ;; +esac + +logger -t xcat "Install: syslog setup" + +exit 0 + diff --git a/xCAT-server-2.0/usr/share/xcat/postscripts/updateflag.awk b/xCAT-server-2.0/usr/share/xcat/postscripts/updateflag.awk new file mode 100755 index 000000000..6d6930934 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/postscripts/updateflag.awk @@ -0,0 +1,25 @@ +#!/bin/awk -f +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html + +BEGIN { + xcatdhost = ARGV[1] + xcatdport = 3002 + + + ns = "/inet/tcp/0/" ARGV[1] "/" xcatdport + + while(1) { + if((ns |& getline) > 0) + print $0 | "logger -t xcat" + + if($0 == "ready") + print "next" |& ns + if($0 == "done") + break + } + + close(ns) + + exit 0 +} + diff --git a/xCAT-server-2.0/usr/share/xcat/scripts/bitscount.pl b/xCAT-server-2.0/usr/share/xcat/scripts/bitscount.pl new file mode 100755 index 000000000..e249a5784 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/scripts/bitscount.pl @@ -0,0 +1,12 @@ +#!/usr/bin/perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#egan@us.ibm.com + +$bits = ((2 ** 32) - 1) << (32 - $ARGV[0]); + +printf "%d.",($bits & 0xff000000) >> 24; +printf "%d.",($bits & 0x00ff0000) >> 16; +printf "%d.",($bits & 0x0000ff00) >> 8; +printf "%d\n",($bits & 0x000000ff); + +exit; diff --git a/xCAT-server-2.0/usr/share/xcat/scripts/countbits.pl b/xCAT-server-2.0/usr/share/xcat/scripts/countbits.pl new file mode 100755 index 000000000..d915bf339 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/scripts/countbits.pl @@ -0,0 +1,9 @@ +#!/usr/bin/perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#egan@us.ibm.com + +$mask = pack("CCCC",$ARGV[0],$ARGV[1],$ARGV[2],$ARGV[3]); +$bits += unpack("%32b*", $mask); + +print "$bits\n"; + diff --git a/xCAT-server-2.0/usr/share/xcat/scripts/ipcalc.ksh b/xCAT-server-2.0/usr/share/xcat/scripts/ipcalc.ksh new file mode 100755 index 000000000..4d45f2226 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/scripts/ipcalc.ksh @@ -0,0 +1,184 @@ +#!/bin/ksh +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#egan@us.ibm.com +#(C)IBM Corp + +# +#RH ipcalc "cleanroom" clone in ksh +#no --silent option, just 2>/dev/null +# + +MYNAME=$0 + +function validateip +{ + set -A ipa $(echo $* | tr '.' ' ') + integer nipa=${#ipa[*]} + if ((nipa != 4)) + then + return 1 + fi + integer a=${ipa[0]} + for j in 0 1 2 3 + do + integer a=${ipa[$j]} + if ((a > 255)) + then + return 4 + fi + done +} + +function usage { + echo "\nUsage: ${MYNAME} [--hostname] [--broadcast] [--network] [--netmask] ip [netmask]\n" +} + +if [ "$#" = "0" ] +then + usage >&2 + exit 1 +fi + +HOSTNAME=0 +BROADCAST=0 +NETWORK=0 +NETMASK=0 + +for i in $* +do + case "$i" in + -*) + case "$i" in + "--hostname") + HOSTNAME=1 + shift + ;; + "--broadcast") + BROADCAST=1 + shift + ;; + "--network") + NETWORK=1 + shift + ;; + "--netmask") + NETMASK=1 + shift + ;; + *) + usage >&2 + exit 1 + ;; + esac + ;; + esac +done + +if [ -z "$1" ] +then + usage >&2 + exit 1 +fi +IP=$1 +shift + +NM="" +if [ ! -z "$1" ] +then + NM=$1 + shift +fi + +if [ "$#" -gt "0" ] +then + usage >&2 + exit 1 +fi + +if validateip $IP +then + : +else + echo "$0: bad ip $IP" >&2 + exit 1 +fi + +if [ ! -z "$NM" ] +then + if validateip $NM + then + : + else + echo "$0: bad netmask $NM" >&2 + exit 1 + fi +fi + +if [ "$BROADCAST" = "1" -o "$NETWORK" = "1" ] +then + if [ -z "$NM" ] + then + echo "$0: netmask expected" >&2 + exit 1 + fi +fi + +if [ "$HOSTNAME" = "1" ] +then + if host $IP >/dev/null 2>&1 + then + HOSTNAME=$(host $IP 2>/dev/null | awk '{print $5}' | awk -F. '{print $1}') + echo "HOSTNAME=$HOSTNAME" + else + echo "$0: cannot find hostname for $IP: Unknown host" >&2 + fi +fi + +if [ "$BROADCAST" = "1" -o "$NETWORK" = "1" ] +then + set -A ipa $(echo $IP | tr '.' ' ') + set -A nma $(echo $NM | tr '.' ' ') + NW="" + BC="" + for j in 0 1 2 3 + do + integer a=${ipa[$j]} + integer b=${nma[$j]} + integer c=a\&b + NW="$NW$c." + integer d=b\^255 + integer e=c\|d + BC="$BC$e." + done + NW=$(echo $NW | sed 's/.$//') + BC=$(echo $BC | sed 's/.$//') + if [ "$BROADCAST" = "1" ] + then + echo "BROADCAST=$BC" + fi + if [ "$NETWORK" = "1" ] + then + echo "NETWORK=$NW" + fi +fi + +if [ "$NETMASK" = "1" ] +then + if [ -z "$NM" ] + then + integer ipa=$(echo $IP | awk -F. '{print $1}') + NM=255.255.0.0 + if((ipa < 128)) + then + NM=255.0.0.0 + fi + if((ipa > 191)) + then + NM=255.255.255.0 + fi + fi + echo "NETMASK=$NM" +fi + +exit 0 + diff --git a/xCAT-server-2.0/usr/share/xcat/scripts/setup-local-client.sh b/xCAT-server-2.0/usr/share/xcat/scripts/setup-local-client.sh new file mode 100755 index 000000000..67b351682 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/scripts/setup-local-client.sh @@ -0,0 +1,54 @@ + +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +# not sure about this logic +# - it seems most(or all) of the time when using a socket +# the gettab call will fail since the certificates are +# not ready or have changed? +# - how would the user specify some other XCATDIR? + +#XCATDIR=`gettab key=xcatconfdir site.value` + +if [ -z "$XCATDIR" ]; then + XCATDIR=/etc/xcat +fi +if [ -z "$1" ]; then + set `whoami` +fi +CNA="$*" +USERHOME=`getent passwd $1|awk -F: '{print $6}'` +XCATCADIR=$XCATDIR/ca + +if [ -e $USERHOME/.xcat ]; then + exit 0 + echo -n "$USERHOME/.xcat already exists, delete and start over (y/n)?" + read ANSWER + if [ "$ANSWER" != "y" ]; then + echo "Aborting at user request" + exit 0 + fi + rm -rf $USERHOME/.xcat +fi +mkdir -p $USERHOME/.xcat +cd $USERHOME/.xcat +openssl genrsa -out client-key.pem 2048 +openssl req -config $XCATCADIR/openssl.cnf -new -key client-key.pem -out client-req.pem -subj "/CN=$CNA" +cp client-req.pem $XCATDIR/ca/root.csr +cd - +cd $XCATDIR/ca + +# - "make sign" doesn't work on my AIX test system???? +# - seems to be a problem with the use of the wildcard in the Makefile +# - calling cmds directly instead - should be safe +# make sign +openssl ca -config openssl.cnf -in root.csr -out root.cert +if [ -f root.cert ]; then + rm root.csr +fi + +cp root.cert $USERHOME/.xcat/client-cert.pem +cp ca-cert.pem $USERHOME/.xcat/ca.pem +chown $1 -R $USERHOME/.xcat +find $USERHOME/.xcat -type f -exec chmod 600 {} \; +find $USERHOME/.xcat -type d -exec chmod 700 {} \; +cd - + diff --git a/xCAT-server-2.0/usr/share/xcat/scripts/setup-server-cert.sh b/xCAT-server-2.0/usr/share/xcat/scripts/setup-server-cert.sh new file mode 100755 index 000000000..ff861e1fe --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/scripts/setup-server-cert.sh @@ -0,0 +1,43 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#XCATDIR=`gettab key=xcatconfdir site.value` +if [ -z "$XCATDIR" ]; then + XCATDIR=/etc/xcat +fi +if [ -z "$1" ]; then + echo "Usage: $0 servername" +fi +CNA=$* + +XCATCADIR=$XCATDIR/ca + +if [ -e $XCATDIR/cert ]; then + echo -n "$XCATDIR/cert already exists, delete and start over (y/n)?" + read ANSWER + if [ "$ANSWER" != "y" ]; then + echo "Aborting at user request" + exit 0 + fi + rm -rf $XCATDIR/cert +fi +mkdir -p $XCATDIR/cert +cd $XCATDIR/cert +openssl genrsa -out server-key.pem 2048 +openssl req -config $XCATCADIR/openssl.cnf -new -key server-key.pem -out server-req.pem -subj "/CN=$CNA" +cp server-req.pem $XCATDIR/ca/`hostname`.csr +cd - +cd $XCATDIR/ca + +# - "make sign" doesn't seem to work on my AIX system??? +# - seems to be a problem with the use of the wildcard in the Makefile +# - call cmds directly instead - seems safe +# make sign + +openssl ca -config openssl.cnf -in `hostname`.csr -out `hostname`.cert +if [ -f `hostname`.cert ]; then + rm `hostname`.csr +fi + +cp `hostname`.cert $XCATDIR/cert/server-cert.pem +cp ca-cert.pem $XCATDIR/cert/ca.pem +cd - + diff --git a/xCAT-server-2.0/usr/share/xcat/scripts/setup-xcat-ca.sh b/xCAT-server-2.0/usr/share/xcat/scripts/setup-xcat-ca.sh new file mode 100755 index 000000000..2fb03dc39 --- /dev/null +++ b/xCAT-server-2.0/usr/share/xcat/scripts/setup-xcat-ca.sh @@ -0,0 +1,31 @@ +#XCATDIR=`gettab key=xcatconfdir site.value` +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +if [ -z "$XCATDIR" ]; then + XCATDIR=/etc/xcat +fi +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi +CNA="$*" + +XCATCADIR=$XCATDIR/ca + +if [ -e $XCATDIR/ca ]; then + echo -n "Existing xCAT certificate authority detected at $XCATDIR/ca, delete? (y/n):" + read ANSWER + if [ $ANSWER != 'y' ]; then + echo "Aborting install at user request" + exit 0; + fi + rm -rf $XCATDIR/ca + mkdir -p $XCATDIR/ca +else + mkdir -p $XCATDIR/ca +fi +sed -e "s@##XCATCADIR##@$XCATCADIR@" /usr/share/xcat/ca/openssl.cnf.tmpl > $XCATCADIR/openssl.cnf +cp /usr/share/xcat/ca/Makefile $XCATCADIR/ +cd $XCATCADIR +make init +openssl req -nodes -config openssl.cnf -days 2650 -x509 -newkey rsa:2048 -out ca-cert.pem -outform PEM -subj /CN="$CNA" +cd - diff --git a/xCAT-server-2.0/usr/svn-commit.tmp b/xCAT-server-2.0/usr/svn-commit.tmp new file mode 100644 index 000000000..f06f74d0f --- /dev/null +++ b/xCAT-server-2.0/usr/svn-commit.tmp @@ -0,0 +1,4 @@ +Remove the obsolete addnode +--This line, and those below, will be ignored-- + +D sbin/addnode diff --git a/xCAT-server-2.0/xCAT-server.spec b/xCAT-server-2.0/xCAT-server.spec new file mode 100644 index 000000000..15baf091a --- /dev/null +++ b/xCAT-server-2.0/xCAT-server.spec @@ -0,0 +1,130 @@ +Summary: Server and configuration utilities of the xCAT management project +Name: xCAT-server +Version: 2.0 +Release: snap%(date +"%Y%m%d%H%M") +License: EPL +Group: Applications/System +Source: xCAT-server-2.0.tar.gz +Packager: IBM Corp. +Vendor: IBM Corp. +Distribution: %{?_distribution:%{_distribution}}%{!?_distribution:%{_vendor}} +Prefix: %{_prefix} +BuildRoot: /var/tmp/%{name}-%{version}-%{release}-root + +# AIX will build with an arch of "ppc" +# also need to fix Requires for AIX +%ifos linux +BuildArch: noarch +Requires: perl-IO-Socket-SSL perl-XML-Simple +%endif + +Requires: perl-xCAT = %{version} +Requires: xCAT-client = %{version} + +Provides: xCAT-server = %{version} + +%description +xCAT-server provides the core server and configuration management components of xCAT. This package should be installed on your management server + +%prep +%setup -q +%build +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/usr/sbin +mkdir -p $RPM_BUILD_ROOT/usr/share/xcat/install +mkdir -p $RPM_BUILD_ROOT/usr/share/xcat/ca +mkdir -p $RPM_BUILD_ROOT/usr/share/xcat/scripts +mkdir -p $RPM_BUILD_ROOT/usr/share/xcat/cons +mkdir -p $RPM_BUILD_ROOT/usr/lib/xcat/plugins +mkdir -p $RPM_BUILD_ROOT/opt/csm/pm/dsh/Context +mkdir -p $RPM_BUILD_ROOT/usr/lib/xcat/monitoring/samples + + +cp -a usr/share/xcat/install/* $RPM_BUILD_ROOT/usr/share/xcat/install/ +cp usr/sbin/* $RPM_BUILD_ROOT/usr/sbin +chmod 755 $RPM_BUILD_ROOT/usr/sbin/* + +cp usr/share/xcat/ca/* $RPM_BUILD_ROOT/usr/share/xcat/ca +chmod 644 $RPM_BUILD_ROOT/usr/share/xcat/ca/* + +cp usr/share/xcat/scripts/* $RPM_BUILD_ROOT/usr/share/xcat/scripts +cp usr/share/xcat/cons/* $RPM_BUILD_ROOT/usr/share/xcat/cons +chmod 755 $RPM_BUILD_ROOT/usr/share/xcat/cons/* + +cp usr/lib/xcat/plugins/* $RPM_BUILD_ROOT/usr/lib/xcat/plugins +chmod 644 $RPM_BUILD_ROOT/usr/lib/xcat/plugins/* + +cp usr/lib/xcat/dsh/Context/* $RPM_BUILD_ROOT/opt/csm/pm/dsh/Context +chmod 644 $RPM_BUILD_ROOT/opt/csm/pm/dsh/Context/* + +cp -r usr/lib/xcat/monitoring/* $RPM_BUILD_ROOT/usr/lib/xcat/monitoring +chmod 644 $RPM_BUILD_ROOT/usr/lib/xcat/monitoring/* + +chmod 755 $RPM_BUILD_ROOT/usr/lib/xcat/monitoring/samples +#cp usr/lib/xcat/monitoring/samples/* $RPM_BUILD_ROOT/usr/lib/xcat/monitoring/samples +chmod 644 $RPM_BUILD_ROOT/usr/lib/xcat/monitoring/samples/* + +cp usr/lib/xcat/shfunctions $RPM_BUILD_ROOT/usr/lib/xcat +chmod 644 $RPM_BUILD_ROOT/usr/lib/xcat/shfunctions +mkdir -p $RPM_BUILD_ROOT/etc/xcat +mkdir -p $RPM_BUILD_ROOT/etc/init.d +cp etc/init.d/xcatd $RPM_BUILD_ROOT/etc/init.d +cp etc/xcat/postscripts.rules $RPM_BUILD_ROOT/etc/xcat/ + + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +%doc README +%doc LICENSE.html +/usr/sbin/* +/usr/share/xcat/install +/usr/share/xcat/ca/* +/usr/share/xcat/scripts/* +/usr/share/xcat/cons/* +/usr/lib/xcat/plugins/* +/usr/lib/xcat/monitoring +/usr/lib/xcat/shfunctions +/opt/csm +/etc/xcat +/etc/init.d/xcatd + +%changelog +* Wed May 2 2007 - Norm Nott +- Made changes to make this work on AIX + +* Tue Feb 27 2007 Jarrod Johnson +- Spawn server rpm for the server half of things, fix requires + +* Tue Feb 20 2007 Jarrod Johnson +- Start core rpm for 1.3 work + +%post + +if [ -x /usr/lib/lsb/install_initd ]; then + /usr/lib/lsb/install_initd /etc/init.d/xcatd +elif [ -x /sbin/chkconfig ]; then + /sbin/chkconfig --add xcatd +else + echo "Unable to register init scripts on this system" +fi +if [ "$1" = "2" ]; then #only on upgrade... + /etc/init.d/xcatd restart +fi + +%preun +if [ $1 == 0 ]; then #This means only on -e + /etc/init.d/xcatd stop + if [ -x /usr/lib/lsb/remove_initd ]; then + /usr/lib/lsb/install_initd /etc/init.d/xcatd + elif [ -x /sbin/chkconfig ]; then + /sbin/chkconfig --del xcatd + fi +fi + + + + diff --git a/xCAT-server-2.0/xcatd.rc b/xCAT-server-2.0/xcatd.rc new file mode 100755 index 000000000..3a761c043 --- /dev/null +++ b/xCAT-server-2.0/xcatd.rc @@ -0,0 +1,90 @@ +#!/bin/bash +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +# chkconfig: 345 85 60 +# description: xCAT management service +# processname: xcatd + +### BEGIN INIT INFO +# Provides: xcatd +# Default-Start: 3 4 5 +# Default-stop: 0 1 2 6 +# Short-Description: xCATd +# Description: xCAT management service +### END INIT INFO + + +MStatus() +{ + ps ax|grep -v grep|grep xcatd: >& /dev/null + if [ "$?" = "0" ]; then + RVAL=0 + echo "xCAT service is running" + else + RVAL=3 + echo "xCAT service is not running" + fi + return $RVAL +} + +if [ -f /lib/lsb/init-functions ]; then + . /lib/lsb/init-functions + START_DAEMON=start_daemon + STATUS=MStatus + LOG_SUCCESS=log_success_msg + LOG_FAILURE=log_failure_msg + LOG_WARNING=log_warning_message +elif [ -f /etc/init.d/functions ]; then + echo RH + . /etc/init.d/functions + START_DAEMON=daemon + STATUS=status + LOG_SUCCESS=success + LOG_FAILURE=failure + LOG_WARNING=passed +else + echo "Error, don't know how to start on this platform" + exit 1 +fi + +case $1 in +status) + $STATUS + ;; +stop) + echo -n "Stopping xCATD" + $STATUS >& /dev/null + if [ "$?" != "0" ]; then + echo -n "xCATd not running, not stopping" + $LOG_WARNING + exit 1 + fi + kill -TERM -`cat /var/run/xcatd.pid` + usleep 100000 + $STATUS >& /dev/null + if [ "$?" == "0" ]; then + kill -KILL -`cat /var/run/xcatd.pid` + fi + usleep 100000 + $STATUS >& /dev/null + if [ "$?" == "0" ]; then + $LOG_FAILURE + exit 1 + fi + $LOG_SUCCESS + rm /var/run/xcatd.pid + ;; +start) + $STATUS >& /dev/null + if [ "$?" == "0" ]; then + echo -n "xCATd already running" + $LOG_WARNING + exit + fi + echo -n "Starting xCATd" + xcatd -p /var/run/xcatd.pid && $LOG_SUCCESS || $LOG_FAILURE + ;; +esac + + + + diff --git a/xCAT-web/.htpasswd b/xCAT-web/.htpasswd new file mode 100644 index 000000000..64f3fa432 --- /dev/null +++ b/xCAT-web/.htpasswd @@ -0,0 +1 @@ +root:FY0Bc7abHEm5w diff --git a/xCAT-web/.project b/xCAT-web/.project new file mode 100644 index 000000000..a0cbb5ccb --- /dev/null +++ b/xCAT-web/.project @@ -0,0 +1,17 @@ + + + xCAT-web + + + + + + net.sourceforge.phpeclipse.parserbuilder + + + + + + net.sourceforge.phpeclipse.phpnature + + diff --git a/xCAT-web/Archive/images/Thumbs.db b/xCAT-web/Archive/images/Thumbs.db new file mode 100644 index 000000000..1cd46099a Binary files /dev/null and b/xCAT-web/Archive/images/Thumbs.db differ diff --git a/xCAT-web/Archive/images/arrow.gif b/xCAT-web/Archive/images/arrow.gif new file mode 100644 index 000000000..49c9c1693 Binary files /dev/null and b/xCAT-web/Archive/images/arrow.gif differ diff --git a/xCAT-web/Archive/images/bar.gif b/xCAT-web/Archive/images/bar.gif new file mode 100644 index 000000000..ffb1186b3 Binary files /dev/null and b/xCAT-web/Archive/images/bar.gif differ diff --git a/xCAT-web/Archive/images/baractive.gif b/xCAT-web/Archive/images/baractive.gif new file mode 100644 index 000000000..582be90df Binary files /dev/null and b/xCAT-web/Archive/images/baractive.gif differ diff --git a/xCAT-web/Archive/images/baractive_big.gif b/xCAT-web/Archive/images/baractive_big.gif new file mode 100644 index 000000000..8e1e9e94b Binary files /dev/null and b/xCAT-web/Archive/images/baractive_big.gif differ diff --git a/xCAT-web/Archive/images/barul.gif b/xCAT-web/Archive/images/barul.gif new file mode 100644 index 000000000..b186ccc4b Binary files /dev/null and b/xCAT-web/Archive/images/barul.gif differ diff --git a/xCAT-web/Archive/images/bg.gif b/xCAT-web/Archive/images/bg.gif new file mode 100644 index 000000000..8d7381718 Binary files /dev/null and b/xCAT-web/Archive/images/bg.gif differ diff --git a/xCAT-web/Archive/images/bg_header.jpg b/xCAT-web/Archive/images/bg_header.jpg new file mode 100644 index 000000000..d2075a371 Binary files /dev/null and b/xCAT-web/Archive/images/bg_header.jpg differ diff --git a/xCAT-web/Archive/images/content.gif b/xCAT-web/Archive/images/content.gif new file mode 100644 index 000000000..2b0945c60 Binary files /dev/null and b/xCAT-web/Archive/images/content.gif differ diff --git a/xCAT-web/Archive/images/footer.gif b/xCAT-web/Archive/images/footer.gif new file mode 100644 index 000000000..1a67615a6 Binary files /dev/null and b/xCAT-web/Archive/images/footer.gif differ diff --git a/xCAT-web/Archive/images/h3bg.gif b/xCAT-web/Archive/images/h3bg.gif new file mode 100644 index 000000000..363734b03 Binary files /dev/null and b/xCAT-web/Archive/images/h3bg.gif differ diff --git a/xCAT-web/Archive/images/h3bg_new.gif b/xCAT-web/Archive/images/h3bg_new.gif new file mode 100644 index 000000000..2204cdeec Binary files /dev/null and b/xCAT-web/Archive/images/h3bg_new.gif differ diff --git a/xCAT-web/Archive/images/header.gif b/xCAT-web/Archive/images/header.gif new file mode 100644 index 000000000..b7f2f28b6 Binary files /dev/null and b/xCAT-web/Archive/images/header.gif differ diff --git a/xCAT-web/Archive/images/header_bar.gif b/xCAT-web/Archive/images/header_bar.gif new file mode 100644 index 000000000..53cf8b4ed Binary files /dev/null and b/xCAT-web/Archive/images/header_bar.gif differ diff --git a/xCAT-web/Archive/images/header_bg.gif b/xCAT-web/Archive/images/header_bg.gif new file mode 100644 index 000000000..3c460af48 Binary files /dev/null and b/xCAT-web/Archive/images/header_bg.gif differ diff --git a/xCAT-web/Archive/images/header_bg2.gif b/xCAT-web/Archive/images/header_bg2.gif new file mode 100644 index 000000000..c4e2c0c92 Binary files /dev/null and b/xCAT-web/Archive/images/header_bg2.gif differ diff --git a/xCAT-web/Archive/images/header_bg3.gif b/xCAT-web/Archive/images/header_bg3.gif new file mode 100644 index 000000000..a5721ff76 Binary files /dev/null and b/xCAT-web/Archive/images/header_bg3.gif differ diff --git a/xCAT-web/Archive/images/hmenuarrow.gif b/xCAT-web/Archive/images/hmenuarrow.gif new file mode 100644 index 000000000..9eee603d0 Binary files /dev/null and b/xCAT-web/Archive/images/hmenuarrow.gif differ diff --git a/xCAT-web/Archive/images/hmenuarrowhover.gif b/xCAT-web/Archive/images/hmenuarrowhover.gif new file mode 100644 index 000000000..24987bde6 Binary files /dev/null and b/xCAT-web/Archive/images/hmenuarrowhover.gif differ diff --git a/xCAT-web/Archive/images/horizontal_bar.gif b/xCAT-web/Archive/images/horizontal_bar.gif new file mode 100644 index 000000000..41fa4b511 Binary files /dev/null and b/xCAT-web/Archive/images/horizontal_bar.gif differ diff --git a/xCAT-web/Archive/images/image.gif b/xCAT-web/Archive/images/image.gif new file mode 100644 index 000000000..1f9087568 Binary files /dev/null and b/xCAT-web/Archive/images/image.gif differ diff --git a/xCAT-web/Archive/images/image_big.gif b/xCAT-web/Archive/images/image_big.gif new file mode 100644 index 000000000..524124273 Binary files /dev/null and b/xCAT-web/Archive/images/image_big.gif differ diff --git a/xCAT-web/Archive/images/intro.jpg b/xCAT-web/Archive/images/intro.jpg new file mode 100644 index 000000000..151f5a5f2 Binary files /dev/null and b/xCAT-web/Archive/images/intro.jpg differ diff --git a/xCAT-web/Archive/images/leftintrobg.gif b/xCAT-web/Archive/images/leftintrobg.gif new file mode 100644 index 000000000..5eaa40b9b Binary files /dev/null and b/xCAT-web/Archive/images/leftintrobg.gif differ diff --git a/xCAT-web/Archive/images/logo.gif b/xCAT-web/Archive/images/logo.gif new file mode 100644 index 000000000..af8c5bc78 Binary files /dev/null and b/xCAT-web/Archive/images/logo.gif differ diff --git a/xCAT-web/Archive/images/menufooter.gif b/xCAT-web/Archive/images/menufooter.gif new file mode 100644 index 000000000..886d96071 Binary files /dev/null and b/xCAT-web/Archive/images/menufooter.gif differ diff --git a/xCAT-web/Archive/images/menuheader.gif b/xCAT-web/Archive/images/menuheader.gif new file mode 100644 index 000000000..a8d78c42b Binary files /dev/null and b/xCAT-web/Archive/images/menuheader.gif differ diff --git a/xCAT-web/Archive/images/menulink.gif b/xCAT-web/Archive/images/menulink.gif new file mode 100644 index 000000000..c24fb69a2 Binary files /dev/null and b/xCAT-web/Archive/images/menulink.gif differ diff --git a/xCAT-web/Archive/images/package.gif b/xCAT-web/Archive/images/package.gif new file mode 100644 index 000000000..7a98856f2 Binary files /dev/null and b/xCAT-web/Archive/images/package.gif differ diff --git a/xCAT-web/Archive/images/persian-cat.gif b/xCAT-web/Archive/images/persian-cat.gif new file mode 100644 index 000000000..56b3f4fb3 Binary files /dev/null and b/xCAT-web/Archive/images/persian-cat.gif differ diff --git a/xCAT-web/Archive/images/rightcontent.gif b/xCAT-web/Archive/images/rightcontent.gif new file mode 100644 index 000000000..43ab86599 Binary files /dev/null and b/xCAT-web/Archive/images/rightcontent.gif differ diff --git a/xCAT-web/Archive/images/rightfooter.gif b/xCAT-web/Archive/images/rightfooter.gif new file mode 100644 index 000000000..90a95f712 Binary files /dev/null and b/xCAT-web/Archive/images/rightfooter.gif differ diff --git a/xCAT-web/Archive/images/rightheader.gif b/xCAT-web/Archive/images/rightheader.gif new file mode 100644 index 000000000..dad16525c Binary files /dev/null and b/xCAT-web/Archive/images/rightheader.gif differ diff --git a/xCAT-web/Archive/images/style.css b/xCAT-web/Archive/images/style.css new file mode 100644 index 000000000..4abecdc94 --- /dev/null +++ b/xCAT-web/Archive/images/style.css @@ -0,0 +1,157 @@ +body { + background-color: #ffffff; + color:#666666; + font-family:Arial,Helv,sans-serif; + font-size:11px; + margin:0; + padding:0; +} + +a:link,a:visited { + color: #FFF; + font-weight: + bold; text-decoration:none; +} + +a:hover { + color: #fff; + text-decoration:underline; +} + +.content_title { + position: absolute; + padding-top: 18px; + padding-left: 17px; + font-size:14px; + margin-bottom: 1px; + font-weight:bold; + width: 400px; + color: #FFF; + background: url(header.gif) no-repeat top left; + height: 21px; +} + +#content_title a, #content_title a:visited { + color: #FFF; +} + +h3 { + margin-top: 0px; + margin-bottom: 0px; + padding-top: 15px; + padding-left: 18px; + font-size:12px; + color: #fff; + background: url(rightheader.gif) no-repeat top left; + height: 20px; +} + +#zaglavlje { + font-size: 20px; + font-weight: bold; + margin-bottom: 0px; + padding-top: 3px; + padding-left: 26px; + color: #fff; + height: 73px; + background: url(rightcontent.gif) no-repeat; +} + +#logo { +padding-top: 25px; +} + +#lijeva_kolona { + float: left; + width: 130px; +} + +#lijeva_kolona a, #lijeva_kolona a:visited { + display: block; + background: url(menulink.gif); + padding-top: 3px; + padding-left: 20px; + height: 20px; + color: #ccc; +} + +#lijeva_kolona a:hover { + color: #FFF; + text-decoration: none; +} + +#sadrzaj { + width: 750px; + margin: 5px auto; + background-color: #fff; + color: #333; + line-height: 130%; +} + +#desna_kolona { + float: right; + width: 240px; + background: #FFF url(rightcontent.gif); + text-align:justify; + color: #ccc; +} + +.srednja_kolona { + margin-left: 135px; + margin-right: 240px; + max-width: 34em; + background: url(content.gif) top left; + text-align:justify; +} + +#menuheader { + background: url(menuheader.gif) no-repeat; + height: 23px; +} + +#menufooter { + background: url(menufooter.gif) no-repeat left top; + height: 21px; +} + +.text { + padding: 40px 18px 25px 18px; + color: #FFF; + background: url(footer.gif) no-repeat left bottom; +} + +.text_desno { + padding: 0px 18px 25px 18px; + color: #e2e2e2; + background: url(rightfooter.gif) no-repeat left bottom; +} + +#info { + color: #ccc; + background: #5982B3; + font-size: 9px; + margin-top: 5px; + margin-bottom: 5px; + padding: 5px; + border: 1px solid #6F92BD; +} + +#hmenu { + padding: 13px 0 5px 0; + float: right; +} + +#hmenu a { + padding: 12px 13px 2px 11px; + text-decoration: none; + color: #414141; + background: #ccc url(hmenuarrow.gif) no-repeat top center; + margin: 0 0 3px 1px; + border-bottom: 4px solid #BCBCBC; +} + +#hmenu a:hover { + background: #3C6491 url(hmenuarrowhover.gif) no-repeat top center; + color: #FFFFFF; + border-bottom: 4px solid #2F4A6C; +} diff --git a/xCAT-web/Archive/images/xCAT_icon.gif b/xCAT-web/Archive/images/xCAT_icon.gif new file mode 100644 index 000000000..ce87160e8 Binary files /dev/null and b/xCAT-web/Archive/images/xCAT_icon.gif differ diff --git a/xCAT-web/Archive/index.html b/xCAT-web/Archive/index.html new file mode 100644 index 000000000..ae18890c1 --- /dev/null +++ b/xCAT-web/Archive/index.html @@ -0,0 +1,129 @@ + + + + + + + xCAT Clustering Administration + + + + + + + + + + + + + + + + + + + + + +
   
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/xCAT-web/Archive/index2.html b/xCAT-web/Archive/index2.html new file mode 100644 index 000000000..dbfe80ebf --- /dev/null +++ b/xCAT-web/Archive/index2.html @@ -0,0 +1,155 @@ + + + + + + + xCAT Clustering Administration + + + + + + + + + + + + + + + + + + + + + + + +
   
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/xCAT-web/Archive/style.css b/xCAT-web/Archive/style.css new file mode 100644 index 000000000..5e023d92a --- /dev/null +++ b/xCAT-web/Archive/style.css @@ -0,0 +1,222 @@ +/* + + template: Artificial Intelligence + author: luka cvrk (www.solucija.com) + +*/ + +body { + margin: 0; + padding: 10; + background: #fff url(images/bg.gif) repeat-x; + font: normal .7em Tahoma, Verdana, Arial, Helvetica, Sans-Serif; + line-height: 1.6em; + color: #333; +} + +/* LINKS AND HEADINGS STYLING */ + +a { color: #546F92; background: inherit; } +a:hover { color: #808080; background: inherit; } +h1 { font: bold 2em "Trebuchet MS", Helvetica, Sans-Serif; margin: 0; color: #fff; background-color:transparent; } +h2 { font: bold 1.4em "Trebuchet MS", Helvetica, Sans-Serif; } +h3 { clear: both; font-size: 1.1em; color: #000; background: #fff url(images/h3bg.gif) no-repeat center left; padding: 0 14px; margin: 0;} +.blue { color: #546F92; background: inherit; } +.image { float: left; border: 2px solid #e2e2e2; padding: 3px; margin: 0 15px 10px 0; } +.no-border {border:0;} + +/* CONTENT WRAPPER */ + +#main { + width: 900px; + margin: 25px auto 0 auto; +} + +/* TOP LEFT LOGO */ + +#logo { + float: left; +} + #logo h1 { color: #FFFFFF; background: inherit;} + #logo h3 { color: #999999; background: inherit;} + +/* TOP RIGHT HORIZONTAL MENU */ + +#menu { + float: right; + /*background: #fff url(images/barul.gif) no-repeat bottom right; */ + color: #808080; + padding: 18px 1px 11px 0; + margin: 0; +} + #menu li { + padding: 14px 18px 14px 18px; + color: #444; + /*background: #fff url(images/bar.gif) no-repeat bottom left; */ + display: inline; + } + #menu li.active { + /*background: #fff url(images/baractive.gif) no-repeat bottom left; */ + color: #FFFF00; + font-weight: bold; + padding: 14px 18px 14px 18px; + margin: 0 1px 0 0; + } + #menu li a { + /*background: #f8f8f8; */ + color: #808080; + text-decoration: font: bold 2em "Trebuchet MS", Helvetica, Sans-Serif; margin: 0; color: #fff; background-color:transparent; + } + #menu li a:hover { + color: #000; + /*background: #f8f8f8; */ + } + .key { text-decoration: underline; } /* ACCESKEY UNDERLINED LETTER */ + +/* LEFT INTRO SIDE */ + +#intro_left { + clear: left; + float: left; + width: 250px; + background: #8D9CAF url(images/leftintrobg.gif) repeat-x; + color: #E5EAF2; + margin: 0 0 1px 0; +} + #intro_left a { color: #fff; background: inherit; } + #intro_left p { padding: 14px 15px; margin: 0; } + + /* LEFT VERTICAL MENU */ + + #menu_left { + float: left; + /*margin: 0 0 6px 0; */ + + /*border-bottom: 2px solid #e2e2e2*/ + + /*display: block; */ + color: #3F5B7F; + background: #C2CEDE; + font-weight: bold; + /*padding: 7px 0px 7px 0px; */ + font-size:12px; + } + #menu_left li { list-style: none; } + #menu_left a:link,a:visited { + display: block; + color: #3F5B7F; + background: #C2CEDE; + width: auto; + height:auto; + padding: 0px; + /*margin: 0 0 1px 0; */ + font-weight: bold; + text-decoration: none; + } + #menu_left a:hover { background: #9EB1C9 ; color: #fff; text-decoration: underline; } + + #menu_sub_left { + float: left; + /*margin: 0; */ + /*padding: 0 0 1px 0; */ + /*border-bottom: 2px solid #e2e2e2; */ + font-size:11px; + } + #menu_sub_left li { list-style: none; } + #menu_sub_left a:link, a:visited { + display: block; + color: #3F5B7F; + background: #CCCCCC ; + width: auto; + height:100%; + padding: 0px 0px 0px 23px; + margin: 0; + font-weight: bold; + text-decoration: none; + } + #menu_sub_left a:hover { background: #999999; color: #fff; } + #menu_sub_left a.current { background: #999999; color: #fff; } + + +/* RIGHT INTRO SIDE */ + +#intro_right { + float: right; + width: 340px; + padding: 25px 280px 0 20px; + background: #BCCEDA url(images/intro.jpg) no-repeat; + color: #547592; + margin: 0 0 6px 0; + height: 174px; + border: 2px solid #e2e2e2; +} + #intro_right h1 { margin: 0 0 20px 0; } + #intro_right .white { color: #FFF; background-color:transparent; } + +/* LEFT SIDE */ + +#left { + float: left; + width: 250px; +} + .box { + padding: 4px 15px; + border: 1px solid #ccc; + margin: 0 0 10px 0; + } + .note { + padding: 15px 15px 20px 100px; + border: 1px solid #CFCB66; + margin: 0 0 5px 0; + background: #FFFFAD url(images/package.gif) no-repeat; + color: #585616; + } + +/* RIGHT SIDE */ + +#right { + float: right; + width: 643px; +} + .leftcol { + float: left; + padding: 12px 15px; + width: 285px; + border:#ccc 1px solid; + text-align: justify; + margin: 0 0 10px 0; + } + .rightcol { + float: right; + padding: 10px 15px; + width: 285px; + border:#ccc 1px solid; + margin: 0 0 10px 0; + } + .special { + clear: both; + margin:0px 0 20px 0; + padding: 5px 20px; + border: 1px solid #ccc; + background: #eee; + color: #444; + } + +/* FOOTER */ + +#footer { clear: both; padding: 5px; border-top: 1px solid #ccc; } + +#main_table{ + /*padding:10px; */ + border-collapse:collapse; + border: 1px solid; + +} + +#header_bar{ + padding:5px 0 5px 0; +} +td.BorderMe{ + border:1px dotted #CCC; + +} \ No newline at end of file diff --git a/xCAT-web/bin/getattrs b/xCAT-web/bin/getattrs new file mode 100644 index 000000000..cdd0a19ed --- /dev/null +++ b/xCAT-web/bin/getattrs @@ -0,0 +1,71 @@ +#!/usr/bin/perl +use strict; +use Getopt::Long; + +$::XCATROOT = ""; +GetOptions('web' => \$::HTML, + 'h|help' => \$::HELP); + +if($::HELP){ + print "$0 --help prints this help message\n"; + print "$0 prints attributes with nodes\n"; + print "$0 --html prints attributes with nodes with html parameters\n\n" ; + exit 0; +} + + +if(-f "/etc/sysconfig/xcat"){ + $::XCATROOT=`awk -F= '{print \$2}' /etc/sysconfig/xcat`; + chomp($::XCATROOT); + if($::XCATROOT eq ""){ + print "XCATROOT not defined in /etc/sysconfig/xcat!\n"; + exit; + } +}else{ + print "file /etc/sysconfig/xcat doesn't exist\n"; + exit; +} +#print "$::XCATROOT\n"; +my $f = "$::XCATROOT/etc/nodelist.tab"; +unless(-f "$f"){ + print "$f doesn't exist!\n"; + exit 1; +} +my $nh; +open(FILE, "$f") or die "can't open $f!"; +while(my $line = ){ + chomp $line; + # skip lines that start with # or with a space and then # + if($line =~ /^\s*#/){ next; } + + # skip lines that are blank + if($line =~ /^$/){ next; } + + my ($node,$attrs) = split(/\s+/, $line); + #print "$node\n"; + my @attrs = split(/,/, $attrs); + foreach my $a (@attrs){ + #print "$a\n"; + if($a ne ""){ + push @{$nh->{$a}}, $node; + } + } +} +if($::HTML){ print "
    \n"; } +foreach (sort keys %$nh){ + if($::HTML){ + print "
  • $_\n"; + print "
      \n"; + foreach(sort @{$nh->{$_}}){ + print "
    • $_
    • \n"; + } + print "
  • \n"; + + }else{ + print $_ . ":\n"; + foreach(@{$nh->{$_}}){ + print "\t$_\n"; + } + } +} +if($::HTML){ print "
\n"; } diff --git a/xCAT-web/cmds/grpattr b/xCAT-web/cmds/grpattr new file mode 100644 index 000000000..fc1899679 --- /dev/null +++ b/xCAT-web/cmds/grpattr @@ -0,0 +1,87 @@ +#!/usr/bin/perl + +# Display some aggregate information about each group, gathered from the db and its nodes + +use strict; +use Getopt::Std; + +sub usage { print "Usage: grpattr [-h] [-v]\n"; exit (scalar(@_) ? $_[0] : 1); } + +if (! getopts('hv') ) { usage(); } +if ($::opt_h) { usage(0); } +#if (scalar(@ARGV) < 1) { &usage; } +#$::VERBOSE = $::opt_v; + +# Get the status of each node and save in a hash +my @ns = runcmd("/opt/xcat/bin/nodestat '/[A-Za-z].*'"); +my %nodestat; +foreach my $line (@ns) { + my ($node, $stat) = split(/[:\s]+/, $line); + $nodestat{$node} = $stat; +} +if ($::opt_v) { foreach my $n (keys %nodestat) { print "$n: $nodestat{$n}\n"; } } + + +# Get the list of nodes/groups and aggregate the status of the group using the status of each node +#TODO: this must be changed for xcat 2.0 +my @nodelist = runcmd("egrep '^[A-Za-z]' /opt/xcat/etc/nodelist.tab"); +verbose("Found ", scalar(@nodelist), " items in nodelist.tab\n"); +my %groups; +foreach my $line (@nodelist) { + my ($node, $groupstr) = split(/\s+/, $line); + my @grouplist = split(/[,\s]+/, $groupstr); + foreach my $g (@grouplist) { + $groups{$g} = minstat($groups{$g}, $nodestat{$node}); + } +} +foreach my $g (keys %groups) { print "$g: $groups{$g}\n"; } + +exit; + +# For 2 status strings from nodestat, return the "lowest". +sub minstat { + my %statnum = ( 'unknown' => 0, + 'noping' => 1, + 'ping' => 2, + 'snmp' => 3, + 'sshd' => 4, + 'pbs' => 5, + 'ready ww' => 6 + ); + my %statstr = ( 0 => 'unknown', + 1 => 'noping', + 2 => 'ping', + 3 => 'snmp', + 4 => 'sshd', + 5 => 'pbs', + 6 => 'ready ww' + ); + my ($s1, $s2) = @_; + + # if either value is empty, just return the other one + if (!length($s1)) { return $s2; } + if (!length($s2)) { return $s1; } + + my $n1 = defined($statnum{$s1}) ? $statnum{$s1} : 0; + my $n2 = defined($statnum{$s2}) ? $statnum{$s2} : 0; + if ($n1 < $n2) { return $statstr{$n1}; } + else { return $statstr{$n2}; } +} + +# Assign a numerical order number to each status string +sub statnum { + my $s = shift @_; + if ($s eq 'noping') { return 1; } + elsif ($s eq 'ping') { return 2; } +} + +sub runcmd { + my $cmd = shift @_; + my @out = `$cmd`; + my $rc = $? >> 8; + if ($rc) { print join('',@out); return; } + chop(@out); + return @out; +} + +sub verbose { if ($::opt_v) { print @_; } } diff --git a/xCAT-web/css/clickTree.css b/xCAT-web/css/clickTree.css new file mode 100644 index 000000000..2b4dd3984 --- /dev/null +++ b/xCAT-web/css/clickTree.css @@ -0,0 +1,64 @@ +ul.clickTree { + list-style: none; + margin: 0; + padding: 0; +} + +ul.clickTree li { + padding-left: 20px; +} + +ul.clickTree ul { + /*padding-left: 10px; */ + list-style: none; +} + + +ul.clickTree li.firstItem { + background: url(http://wopr.dfw.ibm.com/xcat/images/middle-line.gif) center left no-repeat; + /*padding-left: 20px;*/ +} +ul.clickTree li.lastItem { + background: url(http://wopr.dfw.ibm.com/xcat/images/line.gif) center left no-repeat; + /* padding-left: 20px; */ +} +ul.clickTree li.middleItem { + background: url(http://wopr.dfw.ibm.com/xcat/images/middle-line.gif) center left no-repeat; + /* padding-left: 20px;*/ +} +ul.clickTree .unchecked { + background: url(http://wopr.dfw.ibm.com/xcat/images/chkbox-uncheck.gif) center left no-repeat; + /* padding-left: 20px; */ +} +ul.clickTree .checked { + background: url(http://wopr.dfw.ibm.com/xcat/images/chkbox-check.gif) center left no-repeat; + /* padding-left: 20px; */ +} + +.nodeIcon { + background: url(http://wopr.dfw.ibm.com/xcat/images/node.gif) center left no-repeat; + padding-left: 20px; + +} + +.nodeGroupIcon { + background: url(http://wopr.dfw.ibm.com/xcat/images/burst.gif) center left no-repeat; + padding-left: 20px; + +} +.plus { + background: url(http://wopr.dfw.ibm.com/xcat/images/plus.gif) center left no-repeat; + padding-left: 20px; + cursor: pointer; +} + +.minus { + background: url(http://wopr.dfw.ibm.com/xcat/images/minus.gif) center left no-repeat; + padding-left: 20px; + cursor: pointer; + +} + +/* Actually show and hide sublists */ +ul.clickTree li.open ul { display: block; } +ul.clickTree li.closed ul { display: none; } diff --git a/xCAT-web/css/xcat.css b/xCAT-web/css/xcat.css new file mode 100644 index 000000000..046b466cf --- /dev/null +++ b/xCAT-web/css/xcat.css @@ -0,0 +1,155 @@ +#content .block { + width: 400px; + height: 350px; + position: absolute; + overflow: hidden; + left: 287px; + top: 183px; +} + + +#content .block h3 { + margin: 0; + padding: 5px; + font-size: 14px; + color: #fff; + background-color: #2153AA; +} + + + +#content .block .block-toggle { + background-image: url('http://wopr.dfw.ibm.com/xcat/images/close2.gif'); + display: block; + float: right; + width: 14px; + height: 14px; + background-repeat: no-repeat; + background-position: center; + cursor: pointer; +} + + +#content .block .block-close { + background-image: url('http://wopr.dfw.ibm.com/xcat/images/close2.gif'); + display: block; + float: right; + width: 14px; + height: 14px; + background-repeat: no-repeat; + background-position: center; + cursor: pointer; +} + +#content .handle { + cursor: move; + +} + +#content .block .block-toggle span { + display: none; +} + + +#content .blockContent { + width: 400px; + height: 330px; + border: 0px; + padding: 0px; + background: #ffffee; + position: relative; + border: 1px dotted #2153AA; +} + +#content .blockColumn1{ + width: 200px; + padding: 0 0 0 0; + height: 330px; + position: relative; + float: left; +} + + +#content .blockColumn2{ + width: 200px; + height: 330px; + padding: 0 0 0 0; + position: relative; + float: left; + left: 201px; +} + + +#content .blockColumnHeader { + background: #aaa; + width: 200px; + margin: 0 0 0 0; + height: 20px; + font-weight: bold; + font-size: 16px; + color: #666; + padding-left: 5px; + text-align: center; + position: absolute; + bottom: 300px; +} + + +#content .floatPaneColumnContent1 { + overflow-y: auto; + width: 200px; + height: 290px; + position: absolute; + bottom: 10px; + +} + +#content .floatPaneColumnContent2 { + width: 200px; + height: 290px; + position: absolute; + bottom: 10px; +} + +/* Command Window */ +#content .cmd { + width: 400px; + height: 350px; + background: #bbb; + position: relative; + overflow: hidden; + left: 30%; + top: 40px; +} + + +#content .cmdLine { + text-align: center; + font-size: 14px; + font-weight: bold; + padding: 0 0 0 0; +} + +#content .cmdContainer { + overflow-y: auto; + padding: 0 0 0 0; + width: 400px; + height: 300px; +} + +#content .ListLine0 { + background: #e0f8f8; + padding-left: 10px; + width: 390px; +} +#content .ListLine1 { + background: #b0f8f8; + padding-left: 10px; + width: 390px; +} + +.pageScroll { + width: 400px; + height: 330px; + overflow: scroll; +} diff --git a/xCAT-web/css/xcattop.css b/xCAT-web/css/xcattop.css new file mode 100644 index 000000000..bb3325c38 --- /dev/null +++ b/xCAT-web/css/xcattop.css @@ -0,0 +1,131 @@ +.grid { + background: #fff; + border: 1px solid #eee; + text-align: center; + height: 400px; + padding: 2px 2px 2px 2px; + margin: 2px 40px 2px 2px; + +} + +.offLineNode { + background: #aaa; + color: #444; + width: 80px; + height: 20px; + margin: 2px; + font-family: arial; + font-size: 10px; + text-align: center; + overflow: hidden; + float: left; +} + +.pingableNode { + background: #66CC99; + color: #444; + width: 80px; + height: 20px; + margin: 2px; + font-family: arial; + font-size: 10px; + text-align: center; + overflow: hidden; + float: left; +} + +.legendBox { + padding: 2px 2px 2px 2px; + margin-left: 10px; + margin-top: 5px; + margin-bottom: 10px; + border: 2px solid #eee; + position: relative; + width: 800px; + text-align: center; + float: left; + +} +.mHead { + + font-family: arial; + +} +.subBox { + border: 1px solid #eee; + margin-left: 20px; + margin-right: 20px; + width: 100px; + height: 20px; +} + +.legendDescription { + font-size: 10px; + font-family: arial; + color: #999; +} +.unknown { + background: #aaa; + height: 20px; + width: 40px; +} +.pingable { + background: #66CC99; + height: 20px; + width: 40px; +} + +.online { + background: #6699CC; + height: 20px; + width: 40px; +} + +/* grid stuff */ +#box { + position: relative; + top: 50px; + left: 20px; +} +#boxNav { + position: relative; + left: 0px; + top: 100px; + + +} + +#boxNavPrev { + position: absolute; + top: 75px; + left: 5px; + z-index: 100; +} + +#boxNavNext { + position: absolute; + top: 75px; + right: 5px; + z-index: 100; +} + +#boxMotionContainer { + margin: 0pt 30px; + padding: 0px; + overflow: hidden; + position: relative; +} + +#boxMovingGallery { + position: relative; +} + +.boxNav { + padding: 0 0 0 0; + margin: 0 0 0 0; + position: absolute; + text-align: center; + top: 0px; + left: 0px; +} + diff --git a/xCAT-web/css/xcattopStyles.css b/xCAT-web/css/xcattopStyles.css new file mode 100644 index 000000000..12dacc85e --- /dev/null +++ b/xCAT-web/css/xcattopStyles.css @@ -0,0 +1,85 @@ +.grid { + background: #fff; + border: 1px solid #eee; + width: boxWidth ?>px; + text-align: center; + height: 400px; + padding: 2px 2px 2px 2px; + margin: 2px 40px 2px 2px; + +} + +.offLineNode { + background: #aaa; + color: #444; + width: 80px; + height: 20px; + margin: 2px; + font-family: arial; + font-size: 10px; + text-align: center; + overflow: hidden; + float: left; +} + +.pingableNode { + background: #66CC99; + color: #444; + width: 80px; + height: 20px; + margin: 2px; + font-family: arial; + font-size: 10px; + text-align: center; + overflow: hidden; + float: left; +} + +.legendBox { + padding: 2px 2px 2px 2px; + margin-left: 10px; + margin-top: 5px; + margin-bottom: 10px; + border: 2px solid #eee; + position: relative; + width: 800px; + text-align: center; + float: left; + +} +.mHead { + + font-family: arial; + +} +.subBox { + border: 1px solid #eee; + margin-left: 20px; + margin-right: 20px; + width: 100px; + height: 20px; +} + +.legendDescription { + font-size: 10px; + font-family: arial; + color: #999; +} +.unknown { + background: #aaa; + height: 20px; + width: 40px; +} +.pingable { + background: #66CC99; + height: 20px; + width: 40px; +} + +.online { + background: #6699CC; + height: 20px; + width: 40px; +} + + diff --git a/xCAT-web/dsh.php b/xCAT-web/dsh.php new file mode 100644 index 000000000..307db36f9 --- /dev/null +++ b/xCAT-web/dsh.php @@ -0,0 +1,195 @@ + + + +
+ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Run Command on Group + + + + Run Command on node: + + +
+

Select a previous command from the history, or enter the command and options + below.  Then click on Run Cmd.

+ +
Command History: + +    Selecting one of these commands will fill in the fields below. +

+

+
Command:  +
+ Copy command to nodes  (The command specified above will 1st be copied + to /tmp on the nodes and executed from there.)
+ Use parallel shell (psh) command
Options:
Streaming modeSpecifies that output is returned as it becomes available from each target, instead of waiting for the command_list to be completed on a target before returning output.
VerifyVerifies that nodes are responding before sending the command to them.
Collaspe Identical OutputAutomatically pipe the dsh output into dshbak which will only display output once for all the nodes that display identical output. See the dshbak man page for more info.
Fanout:The maximum number of nodes the command should be run on concurrently. When the command finishes on 1 of the nodes, it will be started on an additional node (the default is 64).
UserID:The user id to use to run the command on the nodes.
Remote Shell:The remote shell program to use to run the command on the nodes, for example /usr/bin/ssh. (The default is stored by the csmconfig command or DSH_REMOTE_CMD enviroment variable).
Shell Options:Options to pass to the remote shell being used.
MonitorPrints the results of monitoring for each node in the form of the starting and completion messages for each node.
Code ReturnPrints the return code of the last command that was run remotely. The return code is appended at the end of the output for each node.
+ + +
+ Tips: +
    +
  • See the psh man page for more information about this command.
  • +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/xCAT-web/dsh_action.php b/xCAT-web/dsh_action.php new file mode 100644 index 000000000..05aad75e2 --- /dev/null +++ b/xCAT-web/dsh_action.php @@ -0,0 +1,208 @@ + + +
+Command executed: ". $cmd ."

"; + + $exp_cmd = "export DSH_CONTEXT=XCAT XCATROOT=/opt/xcat; "; + + if ($copy == "on"){ //using dcp/prcp + + //extract the script name from the command + $script = strtok($cmd,' '); + + //copy the command to the remote node + $source = "/opt/xcat/bin/" . $script; //copy from + $target = "/tmp"; //copy to + if ($psh == "off"){ + $copy_cmd = $exp_cmd . "sudo " . $copy_cmd . $node_group . " " . $source . " " . $target; + }else{ + $copy_cmd = "sudo " . $copy_cmd . $source . " " . $node_group . ":" . $target; + } + runcmd($copy_cmd,1, $outp); + + if ($psh != "on"){ + $command_string = $exp_cmd . "sudo ". $command. $node_group . " /tmp/" . $cmd; + }else{ + $command_string = "sudo " . $command . $node_group . " /tmp/" . $cmd; + } + + } + else{ + if ($psh != "on"){ + $command_string = $exp_cmd . "sudo ". $command. $node_group . " " . $cmd; + }else{ + $command_string = "sudo " . $command . $node_group . " " . $cmd; + } + } + + //using dshbak to format the output + $command_string .= " | $SYSTEMROOT/dshbak "; + + if ($collapse == "on") $command_string .= "-c "; + + echo " Full command (for TEST purpose): " . $command_string . "
"; + echo "

Command Ouput:

"; //output will be returned from the runcmd function call + + //run the script + if ($ret_code == "on"){ + $rc = runcmd($command_string,0, $outp); //mode 0 + if ($rc == 0){ + foreach ($outp as $key => $val){ + echo $val. "
"; + } + } + + }else{ + //$rc = runcmd($command_string,1, $outp); //streaming mode COMMENTED AS BY SOME WAY THIS DOESN'T WORK YET + $rc = runcmd($command_string,0, $outp); //mode 0 + if ($rc == 0){ + foreach ($outp as $key => $val){ + echo $val. "
"; + } + } + } + + + + + /*************************************************************************************** + * TEST PART + * for now just use system command on the server, not using psh to run cmds on the nodes + ***************************************************************************************/ + + //test case 1: mode 1 (or -1) + /*$rc = runcmd($cmd,1, $outp); + echo "
RC to caller: " . $rc . "
";*/ + //passed + + //test case 2: mode 3 + /*$rc = runcmd($cmd,3, $outp, array('NoRedirectStdErr' => TRUE)); + echo "RC to caller: " . $rc . "
"; + echo "Ouput file handle: " . $outp . "
"; + echo "Results: " . "
"; + if ($outp){ + while (!feof($outp)){ + $read = fgets($outp); + echo $read; + } + }*/ + //passed + + //testcase 3: mode 0 + /*$rc = runcmd($cmd,0, $outp, array('NoRedirectStdErr' => TRUE)); + if ($rc == 0) echo "Results: "; + foreach ($outp as $key => $val){ + echo $val. ";"; + }*/ + //passed + + //testcase 4: mode 2 + /*$rc = runcmd($cmd,2, $outp, array('NoRedirectStdErr' => TRUE)); + echo "Results: " . $outp;*/ + //passed + + + + /* + //history cookie + if ($rc == 0){ // no error + if (isset($_COOKIE["history"])){ //append to the old cookie + //avoid repetitive entries when user hit "Refresh" button + if (strstr($_COOKIE["history"], ';') == FALSE){ //just have one entry in the history cookie + if (strcmp($_COOKIE["history"],$cmd_text) <> 0){ + $cmd_history = $_COOKIE["history"] . ";" . $cmd_text; + setcookie("history",$cmd_history,$expire_time); + } + } else { + $string = $_COOKIE["history"]; + $token = strtok($string, ';'); + $is_repetitive = 0; + if (strcmp($token,$cmd_text) <> 0) $is_repetitive = 1; + while (FALSE !== ($token = strtok(';'))) { + if (strcmp($token,$cmd_text) <> 0) $is_repetitive = 1; + + } + if ($is_repetitive == 0){ //the new command is not repetitive + $cmd_history = $_COOKIE["history"] . ";" . $cmd_text; + setcookie("history",$cmd_history,$expire_time); + } + } + } + else{ //first time, write new + $cmd_history = $cmd_text; + setcookie("history",$cmd_history,$expire_time); //cookie lasts 30 days + } + + }*/ + +?> +
+ + + diff --git a/xCAT-web/etc/apache2/conf.d/xcat.conf b/xCAT-web/etc/apache2/conf.d/xcat.conf new file mode 100644 index 000000000..54f17aaf0 --- /dev/null +++ b/xCAT-web/etc/apache2/conf.d/xcat.conf @@ -0,0 +1,7 @@ +Alias /xcat "/opt/xcat/web" + + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + diff --git a/xCAT-web/functions.js b/xCAT-web/functions.js new file mode 100644 index 000000000..d77f83f65 --- /dev/null +++ b/xCAT-web/functions.js @@ -0,0 +1,81 @@ + +function setCookies(form) +{ +var value = (form.rack.checked?1:0) + '&' + form.nodegrps.value + '&' + form.nodeRange.value; +setCookie('mainpage', value); +} + +function setCookie(name, value) +{ +value = escape(value); +value = value.replace(/\+/g, '%2B'); // The un_urlize() function in webmin works slightly differently than javascript escape() +document.cookie = name + '=' + value + ';expires=' + (new Date("December 31, 2023")).toGMTString(); +} + +function selectAll(element, objectName) +{ + var sel = element.checked; + var form = element.form; + var searchstr = '^' + objectName + '\\d'; + for(var i = 0; i < form.length; i++) + { + var e = form.elements[i]; + if (e.type == "checkbox" && e.name.search(searchstr) > -1) { e.checked = sel; } + } +} + +function isSelected(form, objectName) +{ +var searchstr = '^' + objectName + '\\d'; +for(var i = 0; i < form.length; i++) + { + var e = form.elements[i]; + if (e.type == "checkbox" && e.name.search(searchstr) > -1 && e.checked) { return true; } + } +return false; +} + +function numSelected(form, objectName) +{ +var searchstr = '^' + objectName + '\\d'; +var j = 0; +for(var i = 0; i < form.length; i++) + { + var e = form.elements[i]; + if (e.type == "checkbox" && e.name.search(searchstr) > -1 && e.checked) + { + if (++j == 2) { return j; } + } + } +return j; +} + +function toggleSection(para, tableId) +{ + var t; var i; + var imageId = tableId + '-im'; + if (document.all) { t = document.all[tableId]; i = document.all[imageId]; } // IE 4+ + else if (document.getElementById) { t = document.getElementById(tableId); i = document.getElementById(imageId); } // Netscape 6 + else { alert('Cannot expand or collapse sections in this version of your browser.'); return false; } + if (!t) { alert('Error: section ' + tableId + ' not found.'); return false; } + + if (!t.style.display || t.style.display == 'inline') // the inner table is currently visible + { + t.style.display = 'none'; + para.title = 'Click to expand section'; + //if (txt) { txt = txt.replace(/^-/i, '+'); } + i.src = 'images/plus-sign.gif'; + //i.alt = '+'; + } + else // the inner table is currently invisible + { + t.style.display = 'inline'; + para.title = 'Click to collapse section'; + //if (txt) { txt = txt.replace(/^\+/i, '-'); } + i.src = 'images/minus-sign.gif'; + //i.alt = '-'; + } + + return true; +} + diff --git a/xCAT-web/functions.php b/xCAT-web/functions.php new file mode 100644 index 000000000..4f009f3f3 --- /dev/null +++ b/xCAT-web/functions.php @@ -0,0 +1,412 @@ + + + + + +<?php echo $title; ?> + + + + + + + + + + + + + + + + + + + + + +\n"; } +if ($javascript) { echo "\n"; } +?> + + + + + + + +
+getValue("IMAGEDIR"); + +// A few constants +$colTxt = "Click to collapse section"; +$exTxt = "Click to expand section"; +$bulgif = "$imagedir/h3bg_new.gif"; +$minusgif = "$imagedir/minus-sign.gif"; +$plusgif = "$imagedir/plus-sign.gif"; + + +/** ---------------------------------------------------------------------------------------------- + Function to run the commands on the remote nodes. Four arguments: + 1. The command + 2. Mode: + 0: If successful, return output to a reference variable in the caller function, with the newlines removed. + Otherwise, print the error msg to the screen + 2: Like mode 0, if successful, return output to a reference variable in the caller function, with the newlines removed. + But error msgs are output to reference variable in the caller function + 1: Long running cmd, intermediate results/errors are ouput as the command is executed + -1: Like mode 1 + 3: Long running cmd. Results/errors are output to a file and return a file handle to the caller function + 3. Reference variable to hold the output returned to caller function + 4. Reference to an options hash, e.g. { NoVerbose => 1, NoRedirectStderr => 1 } + Return status: 0 - successful, error - 1 +------------------------------------------------------------------------------------------------*/ +function runcmd ($cmd, $mode, &$output, $options=NULL){ + + //Set error output to the same source as standard output (on Linux) + if (strstr($cmd,'2>&1') == FALSE && !$options["NoRedirectStdErr"]) + $cmd .= ' 2>&1'; + + $ret_stat = ""; + $arr_output = NULL; + if ($mode == 3){ + $handle = popen($cmd, "r"); + if($handle){ + $output = $handle; //return file handle to caller + return 0; //successful + }else{ + echo "Piping command into a file failed"; + return 1; + } + }elseif ($mode == 0 || $mode == 2 ){ + exec($cmd,$arr_output,$ret_stat); + if ($ret_stat == 0){ + $output = $arr_output; + } else { + //output the error msg to the screen + if ($mode == 0) echo $arr_output[0]; + //output error msg to the caller function + elseif ($mode == 2) $output = $arr_output[0]; + } + }elseif ($mode == 1 || $mode == -1){ + system($cmd,$ret_stat); + } + return $ret_stat; +} +?> + + +> +

onclick="toggleSection(this,'')" ondblclick="toggleSection(this,'')"> + id=>

+ + width="100%" cellpadding="0" cellspacing="0" border=0 style=> + + $link) { + if ($key == $currentLink){ + echo "\n"; + }else{ + echo "\n"; + } +} +?> +
+ + $val) { trace("$key = $val"); } + trace('
$_ENV:'); + foreach ($_ENV as $key => $val) { trace("$key = $val"); } + trace('
$_REQUEST:'); + foreach ($_REQUEST as $key => $val) { trace("$key = $val"); } + if (isset($_SESSION)) { + + trace('
$_SESSION:'); + foreach ($_SESSION as $key => $val) { trace("$key = $val"); } + } +} + +# Returns true if the given rpm file is already installed at this version or higher. +function isInstalled($rpmfile) { //------------------------------------ + $aixrpmopt = isAIX() ? '--ignoreos' : ''; + $lang = isWindows() ? '' : 'LANG=C'; + $rc = runcmd("$lang /bin/rpm -U $aixrpmopt --test $rpmfile", 2, $out); + # The rc is not reliable in this case because it will be 1 if it is already installed + # of if there is some other problem like a dependency is not satisfied. So we parse the + # output instead. + if (preg_grep('/package .* already installed/', $out)) { return 1; } + else { return 0; } + } + + + +// Debug output ------------------------------------ +define("TRACE", "1"); +function trace($str) { if (TRACE) { echo "

$str

\n"; } } + + +// Take in a hostname or ip address and return the fully qualified primary hostname. If resolution fails, +// it just returns what it was given. +function resolveHost($host) { //------------------------------------ + if (isIPAddr($host)) { // IP address + $hostname = gethostbyaddr($host); + return $hostname; + } + else { //todo: implement resolution of hostname to full primary hostname with just one call + $ip = gethostbyname($host); + if (!isIPAddr($ip)) { return $host; } + $hostname = gethostbyaddr($ip); + return $hostname; + } +} + +//------------------------------------ +function isIPAddr ($host) { return preg_match('/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/', $host); } + + +// Returns the URL for the requested documentation. This provides 1 level of indirection in case the location +// of some of the documentation changes. +function getDocURL($book, $section) { //------------------------------------ + $web = array ( + 'docs' => "http://xcat.org/doc", + 'forum' => "http://xcat.org/mailman/listinfo/xcat-user", + 'updates' => "http://www.alphaworks.ibm.com/tech/xCAT", + 'opensrc' => "http://www-rcf.usc.edu/~garrick", + 'wiki' => "http://www.asianlinux.org/xcat", + ); + $man = array (); + $howto = array (); + $rsctadmin = array ( + 0 => "http://publib.boulder.ibm.com/infocenter/clresctr/vxrx/topic/com.ibm.cluster.rsct.doc/rsct_aix5l53", + 1 => "$rsctadmin[0]/bl5adm1002.html", + 'sqlExpressions' => "$rsctadmin[0]/bl5adm1042.html#ussexp", + 'conditions' => "$rsctadmin[0]/bl5adm1041.html#cmrcond", + 'responses' => "$rsctadmin[0]/bl5adm1041.html#cmrresp", + 'resourceClasses' => "$rsctadmin[0]/bl5adm1039.html#lavrc", + ); + + $rsctref = array ( + 0 => "http://publib.boulder.ibm.com/infocenter/clresctr/vxrx/topic/com.ibm.cluster.rsct.doc/rsct_linux151", + 1 => "$rsctref[0]/bl5trl1002.html", + 'errm' => "$rsctref[0]/bl5trl1067.html#errmcmd", + 'errmScripts' => "$rsctref[0]/bl5trl1081.html#errmscr", + 'sensor' => "$rsctref[0]/bl5trl1088.html#srmcmd", + 'auditlog' => "$rsctref[0]/bl5trl1095.html#audcmd", + 'lscondresp' => "$rsctref[0]/bl5trl1071.html#lscondresp", + 'startcondresp' => "$rsctref[0]/bl5trl1079.html#startcondresp", + ); + + if ($book) { + if (!$section) { $section = 1; } // link to whole book if no section specified + $url = &$$book; + return $url[$section]; + } + else { // produce html for a page that contains all the links above, for testing purposes + return ''; //todo: + } +} + + +// This returns important display info about each type of hardware, so we can easily add new hw types. +function getHWTypeInfo($hwtype, $attr) { //------------------------------------ + //todo: get the aliases to be keys in this hash too + $hwhash = array ( + 'x335' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8676' ), + 'x336' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8837' ), + 'x306' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8836' ), + 'x306m' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8849,8491' ), + 'x3550' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'7978' ), + 'e325' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8835' ), + 'e326' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'8848' ), + 'e326m' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'7969' ), + 'e327' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'' ), + + 'x340' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'8656' ), + 'x342' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'8669' ), + 'x345' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'8670' ), + 'x346' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'8840' ), + 'x3650' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'7979' ), + + 'x360' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'8686' ), + 'x365' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'8862' ), + 'x366' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'8863' ), + 'x3850' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'8863' ), + 'x445' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'' ), + 'x450' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'' ), + 'x455' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'' ), + 'x460' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'' ), + 'x3950' => array ( 'image'=>'342.gif', 'rackimage'=>'x366-front', 'u'=>3, 'aliases'=>'' ), + + 'hs20' => array ( 'image'=>'blade.gif', 'rackimage'=>'blade-front', 'u'=>7, 'aliases'=>'8678,8843,7981' ), # removed 8832 because it is older and it made this entry to wide in the drop down boxes + 'js20' => array ( 'image'=>'blade.gif', 'rackimage'=>'blade-front', 'u'=>7, 'aliases'=>'8842' ), + 'js21' => array ( 'image'=>'blade.gif', 'rackimage'=>'blade-front', 'u'=>7, 'aliases'=>'8844' ), + 'ls20' => array ( 'image'=>'blade.gif', 'rackimage'=>'blade-front', 'u'=>7, 'aliases'=>'8850' ), + 'hs40' => array ( 'image'=>'blade2.gif', 'rackimage'=>'blade2-front', 'u'=>7, 'aliases'=>'8839' ), + + 'p5-505' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'9115' ), + 'p5-505Q' => array ( 'image'=>'330.gif', 'rackimage'=>'x335-front', 'u'=>1, 'aliases'=>'9115' ), + + 'p5-510' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'9110' ), + 'p5-510Q' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'9110' ), + 'p5-575' => array ( 'image'=>'342.gif', 'rackimage'=>'x345-front', 'u'=>2, 'aliases'=>'9118' ), + + 'p5-520' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9111' ), + 'p5-520Q' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9131' ), + 'p5-550' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9113' ), + 'p5-550Q' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9133' ), + 'p5-560' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9116' ), + 'p5-560Q' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9116' ), + 'p5-570' => array ( 'image'=>'520.gif', 'rackimage'=>'p5-520-front', 'u'=>4, 'aliases'=>'9117' ), + + 'p5-590' => array ( 'image'=>'590.gif', 'rackimage'=>'p5-590-front', 'u'=>42, 'aliases'=>'9119' ), + 'p5-595' => array ( 'image'=>'590.gif', 'rackimage'=>'p5-590-front', 'u'=>42, 'aliases'=>'9119' ), + + 'p710' => array ( 'image'=>'342.gif', 'rackimage'=>'x335-front', 'u'=>2, 'aliases'=>'9123' ), + 'p720' => array ( 'image'=>'520.gif', 'rackimage'=>'x345-front', 'u'=>4, 'aliases'=>'9124' ), + + 'p610' => array ( 'image'=>'520.gif', 'rackimage'=>'x335-front', 'u'=>5, 'aliases'=>'7028' ), + 'p615' => array ( 'image'=>'520.gif', 'rackimage'=>'x335-front', 'u'=>4, 'aliases'=>'7029' ), + 'p630' => array ( 'image'=>'520.gif', 'rackimage'=>'x335-front', 'u'=>4, 'aliases'=>'7026' ), + 'p640' => array ( 'image'=>'520.gif', 'rackimage'=>'x335-front', 'u'=>5, 'aliases'=>'7026' ), + 'p650' => array ( 'image'=>'520.gif', 'rackimage'=>'x335-front', 'u'=>8, 'aliases'=>'7038' ), + 'p655' => array ( 'image'=>'590.gif', 'rackimage'=>'x335-front', 'u'=>42, 'aliases'=>'7039' ), + 'p670' => array ( 'image'=>'590.gif', 'rackimage'=>'x335-front', 'u'=>42, 'aliases'=>'7040' ), + 'p690' => array ( 'image'=>'590.gif', 'rackimage'=>'x335-front', 'u'=>42, 'aliases'=>'7040' ), + ); + $info = $hwhash[strtolower($hwtype)]; + if (isset($attr)) { return $info[$attr]; } + else { return $info; } +} + + +// Returns the image that should be displayed for this type of hw. Gets this from getHWTypeInfo() +function getHWTypeImage($hwtype, $powermethod) { //------------------------------------ + # 1st try to match the hw type + $info = getHWTypeInfo($hwtype, 'image'); + if ($info) { return $info['image']; } + + # No matches yet. Use the power method to get a generic type. + if (isset($powermethod)) { + $powermethod = strtolower($powermethod); + if ($powermethod == 'blade') { return getHWTypeInfo('hs20', 'image'); } + elseif ($powermethod == 'hmc') { return getHWTypeInfo('p5-520', 'image'); } + elseif ($powermethod == 'bmc') { return getHWTypeInfo('x335','image'); } + elseif ($powermethod == 'xseries') { return getHWTypeInfo('x335', 'image'); } + } + + # As a last resort, return the most common node image + return getHWTypeInfo('x335', 'image'); +} + + +// Returns the specified user preference value. Not finished. +function getPref($key) { //------------------------------------ + if ($key == 'MaxNodesDisplayed') { return 50; } + return ''; +} + + +// Returns a list of some or all of the nodes in the cluster. Pass in either a group name or node range, +// or NULL for each to get all nodes. Not finished. +function getNodes($group, $noderange) { //------------------------------------ + //my ($hostname, $type, $osname, $distro, $version, $mode, $status, $conport, $hcp, $nodeid, $pmethod, $location, $comment) = split(/:\|:/, $na); + for ($i = 1; $i <= 10; $i++) { + $nodes[] = array('hostname'=>"node$i.cluster.com", 'type'=>'x3655', 'osname'=>'Linux', 'distro'=>'RedHat', 'version'=>'4.5', 'status'=>1, + 'conport'=>$i, 'hcp'=>"node$i-bmc.cluster.com", 'nodeid'=>'', 'pmethod'=>'bmc', 'location'=>"frame=1 u=$", 'comment'=>''); + } + return $nodes; +} + + +// Returns the node groups defined in the cluster. Not finished. +function getGroups() { //------------------------------------ + return array('AllNodes','group1','group2'); +} + +// Returns true if we are running on AIX ------------------------------------ +function isAIX() { } //todo: implement + +// Returns true if we are running on Linux ------------------------------------ +function isLinux() { } //todo: implement + +// Returns true if we are running on Windows ------------------------------------ +function isWindows() { return array_key_exists('WINDIR', $_SERVER); } + + +// Create file folder-like tabs. Tablist is an array of label/url pairs. +function insertTabs ($tablist, $currentTabIndex) { //------------------------------------ + echo ""; + foreach ($tablist as $key => $tab) { + if ($key != 0) { echo ""; } + if ($currentTabIndex == $key) { + echo ""; + } + else { + echo ""; + } + } + echo "
$tab[0]$tab[0]
\n"; +} + + +// Create the Action buttons in a table. Buttonlist is an array of arrays of button attribute strings. +function insertButtons ($buttonsets) { //------------------------------------ + foreach ($buttonsets as $buttonlist) { + echo "
"; + foreach ($buttonlist as $button) { echo ""; } + echo "
\n"; + } +} + + +?> diff --git a/xCAT-web/globalconfig.php b/xCAT-web/globalconfig.php new file mode 100644 index 000000000..bf18fd2c7 --- /dev/null +++ b/xCAT-web/globalconfig.php @@ -0,0 +1,31 @@ +setValue("settingName", "settingValue"); + +$config = &Config::getInstance(); +$config->setValue("XCATROOT", $XCATROOT); +$config->setValue("TOPDIR", $TOPDIR); +$config->setValue("CURRDIR", $CURRDIR); +$config->setValue("IMAGEDIR", $IMAGEDIR); +?> diff --git a/xCAT-web/globals.inc b/xCAT-web/globals.inc new file mode 100644 index 000000000..3ef53124f --- /dev/null +++ b/xCAT-web/globals.inc @@ -0,0 +1,6 @@ + diff --git a/xCAT-web/header.inc b/xCAT-web/header.inc new file mode 100644 index 000000000..ed29da30a --- /dev/null +++ b/xCAT-web/header.inc @@ -0,0 +1,39 @@ +"; +$p = 1; +foreach ($menuArray as $key => $value){ + if($p == 1){ + echo "
  • $key\n"; + $p++; + + }else{ + echo "
  • $key\n"; + $p++; + } + echo "
      "; + $q = 0; + foreach($value as $k => $u){ + if($p == 2){ + #echo "
    • $k
    • "; + echo "
    • $k
    • "; + }else{ + #echo "
    • $k
    • "; + echo "
    • $k
    • "; + } + $q++; + } + echo "
    "; + echo "
  • "; +} +echo ""; +} + +function printNodeList(){ + system("bin/getattrs --web"); +} + +?> diff --git a/xCAT-web/images/0-percent.gif b/xCAT-web/images/0-percent.gif new file mode 100644 index 000000000..2915edd46 Binary files /dev/null and b/xCAT-web/images/0-percent.gif differ diff --git a/xCAT-web/images/330.gif b/xCAT-web/images/330.gif new file mode 100644 index 000000000..2b9f404c5 Binary files /dev/null and b/xCAT-web/images/330.gif differ diff --git a/xCAT-web/images/342.gif b/xCAT-web/images/342.gif new file mode 100644 index 000000000..83a5712ea Binary files /dev/null and b/xCAT-web/images/342.gif differ diff --git a/xCAT-web/images/520.gif b/xCAT-web/images/520.gif new file mode 100644 index 000000000..4ea11fc46 Binary files /dev/null and b/xCAT-web/images/520.gif differ diff --git a/xCAT-web/images/590.gif b/xCAT-web/images/590.gif new file mode 100644 index 000000000..9808a47de Binary files /dev/null and b/xCAT-web/images/590.gif differ diff --git a/xCAT-web/images/70-percent.gif b/xCAT-web/images/70-percent.gif new file mode 100644 index 000000000..e27b764d9 Binary files /dev/null and b/xCAT-web/images/70-percent.gif differ diff --git a/xCAT-web/images/Shortcut to images.lnk b/xCAT-web/images/Shortcut to images.lnk new file mode 100644 index 000000000..d75eb5225 Binary files /dev/null and b/xCAT-web/images/Shortcut to images.lnk differ diff --git a/xCAT-web/images/Thumbs.db b/xCAT-web/images/Thumbs.db new file mode 100644 index 000000000..d04552cae Binary files /dev/null and b/xCAT-web/images/Thumbs.db differ diff --git a/xCAT-web/images/aix-s.gif b/xCAT-web/images/aix-s.gif new file mode 100644 index 000000000..a0d01b1bc Binary files /dev/null and b/xCAT-web/images/aix-s.gif differ diff --git a/xCAT-web/images/ajax-loader.gif b/xCAT-web/images/ajax-loader.gif new file mode 100644 index 000000000..5e62d01af Binary files /dev/null and b/xCAT-web/images/ajax-loader.gif differ diff --git a/xCAT-web/images/arrow.gif b/xCAT-web/images/arrow.gif new file mode 100644 index 000000000..49c9c1693 Binary files /dev/null and b/xCAT-web/images/arrow.gif differ diff --git a/xCAT-web/images/arrow.jpg b/xCAT-web/images/arrow.jpg new file mode 100644 index 000000000..59d878101 Binary files /dev/null and b/xCAT-web/images/arrow.jpg differ diff --git a/xCAT-web/images/back-grey-arrow.gif b/xCAT-web/images/back-grey-arrow.gif new file mode 100644 index 000000000..9a3e41324 Binary files /dev/null and b/xCAT-web/images/back-grey-arrow.gif differ diff --git a/xCAT-web/images/bar.gif b/xCAT-web/images/bar.gif new file mode 100644 index 000000000..ffb1186b3 Binary files /dev/null and b/xCAT-web/images/bar.gif differ diff --git a/xCAT-web/images/bar2.gif b/xCAT-web/images/bar2.gif new file mode 100644 index 000000000..d8d32416a Binary files /dev/null and b/xCAT-web/images/bar2.gif differ diff --git a/xCAT-web/images/baractive.gif b/xCAT-web/images/baractive.gif new file mode 100644 index 000000000..582be90df Binary files /dev/null and b/xCAT-web/images/baractive.gif differ diff --git a/xCAT-web/images/baractive2.gif b/xCAT-web/images/baractive2.gif new file mode 100644 index 000000000..d47465839 Binary files /dev/null and b/xCAT-web/images/baractive2.gif differ diff --git a/xCAT-web/images/baractive_big.gif b/xCAT-web/images/baractive_big.gif new file mode 100644 index 000000000..8e1e9e94b Binary files /dev/null and b/xCAT-web/images/baractive_big.gif differ diff --git a/xCAT-web/images/barul.gif b/xCAT-web/images/barul.gif new file mode 100644 index 000000000..b186ccc4b Binary files /dev/null and b/xCAT-web/images/barul.gif differ diff --git a/xCAT-web/images/bg.gif b/xCAT-web/images/bg.gif new file mode 100644 index 000000000..8d7381718 Binary files /dev/null and b/xCAT-web/images/bg.gif differ diff --git a/xCAT-web/images/bg_header.jpg b/xCAT-web/images/bg_header.jpg new file mode 100644 index 000000000..d2075a371 Binary files /dev/null and b/xCAT-web/images/bg_header.jpg differ diff --git a/xCAT-web/images/blade-front-blue.jpg b/xCAT-web/images/blade-front-blue.jpg new file mode 100644 index 000000000..b7972542c Binary files /dev/null and b/xCAT-web/images/blade-front-blue.jpg differ diff --git a/xCAT-web/images/blade-front-green.jpg b/xCAT-web/images/blade-front-green.jpg new file mode 100644 index 000000000..945ac8927 Binary files /dev/null and b/xCAT-web/images/blade-front-green.jpg differ diff --git a/xCAT-web/images/blade-front-red.jpg b/xCAT-web/images/blade-front-red.jpg new file mode 100644 index 000000000..d0af03eae Binary files /dev/null and b/xCAT-web/images/blade-front-red.jpg differ diff --git a/xCAT-web/images/blade-front-yellow.jpg b/xCAT-web/images/blade-front-yellow.jpg new file mode 100644 index 000000000..be0a9c5db Binary files /dev/null and b/xCAT-web/images/blade-front-yellow.jpg differ diff --git a/xCAT-web/images/blade.gif b/xCAT-web/images/blade.gif new file mode 100644 index 000000000..04fb095e8 Binary files /dev/null and b/xCAT-web/images/blade.gif differ diff --git a/xCAT-web/images/blade2.gif b/xCAT-web/images/blade2.gif new file mode 100644 index 000000000..38ae2824a Binary files /dev/null and b/xCAT-web/images/blade2.gif differ diff --git a/xCAT-web/images/blue-ball-m.gif b/xCAT-web/images/blue-ball-m.gif new file mode 100644 index 000000000..e92461890 Binary files /dev/null and b/xCAT-web/images/blue-ball-m.gif differ diff --git a/xCAT-web/images/blue-ball-s.gif b/xCAT-web/images/blue-ball-s.gif new file mode 100644 index 000000000..8e9e4b360 Binary files /dev/null and b/xCAT-web/images/blue-ball-s.gif differ diff --git a/xCAT-web/images/blue-percent.gif b/xCAT-web/images/blue-percent.gif new file mode 100644 index 000000000..71e2bb602 Binary files /dev/null and b/xCAT-web/images/blue-percent.gif differ diff --git a/xCAT-web/images/bt.gif b/xCAT-web/images/bt.gif new file mode 100644 index 000000000..d2bd805ab Binary files /dev/null and b/xCAT-web/images/bt.gif differ diff --git a/xCAT-web/images/bullet.gif b/xCAT-web/images/bullet.gif new file mode 100644 index 000000000..712d01b8e Binary files /dev/null and b/xCAT-web/images/bullet.gif differ diff --git a/xCAT-web/images/canvas.mif b/xCAT-web/images/canvas.mif new file mode 100644 index 000000000..3406bd868 Binary files /dev/null and b/xCAT-web/images/canvas.mif differ diff --git a/xCAT-web/images/card.gif b/xCAT-web/images/card.gif new file mode 100644 index 000000000..4ab1ceabb Binary files /dev/null and b/xCAT-web/images/card.gif differ diff --git a/xCAT-web/images/chassis.gif b/xCAT-web/images/chassis.gif new file mode 100644 index 000000000..65cb582f7 Binary files /dev/null and b/xCAT-web/images/chassis.gif differ diff --git a/xCAT-web/images/checked-box.gif b/xCAT-web/images/checked-box.gif new file mode 100644 index 000000000..46300b318 Binary files /dev/null and b/xCAT-web/images/checked-box.gif differ diff --git a/xCAT-web/images/content.gif b/xCAT-web/images/content.gif new file mode 100644 index 000000000..2b0945c60 Binary files /dev/null and b/xCAT-web/images/content.gif differ diff --git a/xCAT-web/images/csmlogo.gif b/xCAT-web/images/csmlogo.gif new file mode 100644 index 000000000..ec97e2574 Binary files /dev/null and b/xCAT-web/images/csmlogo.gif differ diff --git a/xCAT-web/images/footer.gif b/xCAT-web/images/footer.gif new file mode 100644 index 000000000..1a67615a6 Binary files /dev/null and b/xCAT-web/images/footer.gif differ diff --git a/xCAT-web/images/green-ball-m.gif b/xCAT-web/images/green-ball-m.gif new file mode 100644 index 000000000..104a96694 Binary files /dev/null and b/xCAT-web/images/green-ball-m.gif differ diff --git a/xCAT-web/images/green-ball-s.gif b/xCAT-web/images/green-ball-s.gif new file mode 100644 index 000000000..2c5e3eb08 Binary files /dev/null and b/xCAT-web/images/green-ball-s.gif differ diff --git a/xCAT-web/images/green-percent.gif b/xCAT-web/images/green-percent.gif new file mode 100644 index 000000000..0cbc533e7 Binary files /dev/null and b/xCAT-web/images/green-percent.gif differ diff --git a/xCAT-web/images/grey_arrow_l.gif b/xCAT-web/images/grey_arrow_l.gif new file mode 100644 index 000000000..9186c351e Binary files /dev/null and b/xCAT-web/images/grey_arrow_l.gif differ diff --git a/xCAT-web/images/grey_arrow_r.gif b/xCAT-web/images/grey_arrow_r.gif new file mode 100644 index 000000000..fbf408358 Binary files /dev/null and b/xCAT-web/images/grey_arrow_r.gif differ diff --git a/xCAT-web/images/h3bg.gif b/xCAT-web/images/h3bg.gif new file mode 100644 index 000000000..363734b03 Binary files /dev/null and b/xCAT-web/images/h3bg.gif differ diff --git a/xCAT-web/images/h3bg_new.gif b/xCAT-web/images/h3bg_new.gif new file mode 100644 index 000000000..2204cdeec Binary files /dev/null and b/xCAT-web/images/h3bg_new.gif differ diff --git a/xCAT-web/images/header.gif b/xCAT-web/images/header.gif new file mode 100644 index 000000000..b7f2f28b6 Binary files /dev/null and b/xCAT-web/images/header.gif differ diff --git a/xCAT-web/images/header_bar.gif b/xCAT-web/images/header_bar.gif new file mode 100644 index 000000000..53cf8b4ed Binary files /dev/null and b/xCAT-web/images/header_bar.gif differ diff --git a/xCAT-web/images/header_bg.gif b/xCAT-web/images/header_bg.gif new file mode 100644 index 000000000..3c460af48 Binary files /dev/null and b/xCAT-web/images/header_bg.gif differ diff --git a/xCAT-web/images/header_bg2.gif b/xCAT-web/images/header_bg2.gif new file mode 100644 index 000000000..c4e2c0c92 Binary files /dev/null and b/xCAT-web/images/header_bg2.gif differ diff --git a/xCAT-web/images/header_bg3.gif b/xCAT-web/images/header_bg3.gif new file mode 100644 index 000000000..345806644 Binary files /dev/null and b/xCAT-web/images/header_bg3.gif differ diff --git a/xCAT-web/images/hmenuarrow.gif b/xCAT-web/images/hmenuarrow.gif new file mode 100644 index 000000000..9eee603d0 Binary files /dev/null and b/xCAT-web/images/hmenuarrow.gif differ diff --git a/xCAT-web/images/hmenuarrowhover.gif b/xCAT-web/images/hmenuarrowhover.gif new file mode 100644 index 000000000..24987bde6 Binary files /dev/null and b/xCAT-web/images/hmenuarrowhover.gif differ diff --git a/xCAT-web/images/horizontal_bar.gif b/xCAT-web/images/horizontal_bar.gif new file mode 100644 index 000000000..41fa4b511 Binary files /dev/null and b/xCAT-web/images/horizontal_bar.gif differ diff --git a/xCAT-web/images/icon.gif b/xCAT-web/images/icon.gif new file mode 100644 index 000000000..405da5ad1 Binary files /dev/null and b/xCAT-web/images/icon.gif differ diff --git a/xCAT-web/images/image.gif b/xCAT-web/images/image.gif new file mode 100644 index 000000000..1f9087568 Binary files /dev/null and b/xCAT-web/images/image.gif differ diff --git a/xCAT-web/images/image_big.gif b/xCAT-web/images/image_big.gif new file mode 100644 index 000000000..524124273 Binary files /dev/null and b/xCAT-web/images/image_big.gif differ diff --git a/xCAT-web/images/intro.jpg b/xCAT-web/images/intro.jpg new file mode 100644 index 000000000..151f5a5f2 Binary files /dev/null and b/xCAT-web/images/intro.jpg differ diff --git a/xCAT-web/images/invisible.gif b/xCAT-web/images/invisible.gif new file mode 100644 index 000000000..0dece09d0 Binary files /dev/null and b/xCAT-web/images/invisible.gif differ diff --git a/xCAT-web/images/leftintrobg.gif b/xCAT-web/images/leftintrobg.gif new file mode 100644 index 000000000..5eaa40b9b Binary files /dev/null and b/xCAT-web/images/leftintrobg.gif differ diff --git a/xCAT-web/images/logo.gif b/xCAT-web/images/logo.gif new file mode 100644 index 000000000..af8c5bc78 Binary files /dev/null and b/xCAT-web/images/logo.gif differ diff --git a/xCAT-web/images/menufooter.gif b/xCAT-web/images/menufooter.gif new file mode 100644 index 000000000..886d96071 Binary files /dev/null and b/xCAT-web/images/menufooter.gif differ diff --git a/xCAT-web/images/menuheader.gif b/xCAT-web/images/menuheader.gif new file mode 100644 index 000000000..a8d78c42b Binary files /dev/null and b/xCAT-web/images/menuheader.gif differ diff --git a/xCAT-web/images/menulink.gif b/xCAT-web/images/menulink.gif new file mode 100644 index 000000000..c24fb69a2 Binary files /dev/null and b/xCAT-web/images/menulink.gif differ diff --git a/xCAT-web/images/minus-sign.gif b/xCAT-web/images/minus-sign.gif new file mode 100644 index 000000000..f11eb89b4 Binary files /dev/null and b/xCAT-web/images/minus-sign.gif differ diff --git a/xCAT-web/images/p5-520-front-blue.jpg b/xCAT-web/images/p5-520-front-blue.jpg new file mode 100644 index 000000000..d52d5b087 Binary files /dev/null and b/xCAT-web/images/p5-520-front-blue.jpg differ diff --git a/xCAT-web/images/p5-520-front-green.jpg b/xCAT-web/images/p5-520-front-green.jpg new file mode 100644 index 000000000..4f65fc41c Binary files /dev/null and b/xCAT-web/images/p5-520-front-green.jpg differ diff --git a/xCAT-web/images/p5-520-front-red.jpg b/xCAT-web/images/p5-520-front-red.jpg new file mode 100644 index 000000000..f5ffd150d Binary files /dev/null and b/xCAT-web/images/p5-520-front-red.jpg differ diff --git a/xCAT-web/images/p5-520-front-yellow.jpg b/xCAT-web/images/p5-520-front-yellow.jpg new file mode 100644 index 000000000..5f5da3314 Binary files /dev/null and b/xCAT-web/images/p5-520-front-yellow.jpg differ diff --git a/xCAT-web/images/p5-590-front-blue.jpg b/xCAT-web/images/p5-590-front-blue.jpg new file mode 100644 index 000000000..16fa3bd6a Binary files /dev/null and b/xCAT-web/images/p5-590-front-blue.jpg differ diff --git a/xCAT-web/images/package.gif b/xCAT-web/images/package.gif new file mode 100644 index 000000000..7a98856f2 Binary files /dev/null and b/xCAT-web/images/package.gif differ diff --git a/xCAT-web/images/persian-cat.gif b/xCAT-web/images/persian-cat.gif new file mode 100644 index 000000000..56b3f4fb3 Binary files /dev/null and b/xCAT-web/images/persian-cat.gif differ diff --git a/xCAT-web/images/plus-sign.gif b/xCAT-web/images/plus-sign.gif new file mode 100644 index 000000000..de79ca593 Binary files /dev/null and b/xCAT-web/images/plus-sign.gif differ diff --git a/xCAT-web/images/qmark.gif b/xCAT-web/images/qmark.gif new file mode 100644 index 000000000..6a7a914ce Binary files /dev/null and b/xCAT-web/images/qmark.gif differ diff --git a/xCAT-web/images/red-ball-m.gif b/xCAT-web/images/red-ball-m.gif new file mode 100644 index 000000000..5c43da016 Binary files /dev/null and b/xCAT-web/images/red-ball-m.gif differ diff --git a/xCAT-web/images/red-ball-s.gif b/xCAT-web/images/red-ball-s.gif new file mode 100644 index 000000000..56323f213 Binary files /dev/null and b/xCAT-web/images/red-ball-s.gif differ diff --git a/xCAT-web/images/redhat-s.gif b/xCAT-web/images/redhat-s.gif new file mode 100644 index 000000000..6d1e16145 Binary files /dev/null and b/xCAT-web/images/redhat-s.gif differ diff --git a/xCAT-web/images/redhat.gif b/xCAT-web/images/redhat.gif new file mode 100644 index 000000000..05b495cef Binary files /dev/null and b/xCAT-web/images/redhat.gif differ diff --git a/xCAT-web/images/rightcontent.gif b/xCAT-web/images/rightcontent.gif new file mode 100644 index 000000000..43ab86599 Binary files /dev/null and b/xCAT-web/images/rightcontent.gif differ diff --git a/xCAT-web/images/rightfooter.gif b/xCAT-web/images/rightfooter.gif new file mode 100644 index 000000000..90a95f712 Binary files /dev/null and b/xCAT-web/images/rightfooter.gif differ diff --git a/xCAT-web/images/rightheader.gif b/xCAT-web/images/rightheader.gif new file mode 100644 index 000000000..dad16525c Binary files /dev/null and b/xCAT-web/images/rightheader.gif differ diff --git a/xCAT-web/images/style.css b/xCAT-web/images/style.css new file mode 100644 index 000000000..4abecdc94 --- /dev/null +++ b/xCAT-web/images/style.css @@ -0,0 +1,157 @@ +body { + background-color: #ffffff; + color:#666666; + font-family:Arial,Helv,sans-serif; + font-size:11px; + margin:0; + padding:0; +} + +a:link,a:visited { + color: #FFF; + font-weight: + bold; text-decoration:none; +} + +a:hover { + color: #fff; + text-decoration:underline; +} + +.content_title { + position: absolute; + padding-top: 18px; + padding-left: 17px; + font-size:14px; + margin-bottom: 1px; + font-weight:bold; + width: 400px; + color: #FFF; + background: url(header.gif) no-repeat top left; + height: 21px; +} + +#content_title a, #content_title a:visited { + color: #FFF; +} + +h3 { + margin-top: 0px; + margin-bottom: 0px; + padding-top: 15px; + padding-left: 18px; + font-size:12px; + color: #fff; + background: url(rightheader.gif) no-repeat top left; + height: 20px; +} + +#zaglavlje { + font-size: 20px; + font-weight: bold; + margin-bottom: 0px; + padding-top: 3px; + padding-left: 26px; + color: #fff; + height: 73px; + background: url(rightcontent.gif) no-repeat; +} + +#logo { +padding-top: 25px; +} + +#lijeva_kolona { + float: left; + width: 130px; +} + +#lijeva_kolona a, #lijeva_kolona a:visited { + display: block; + background: url(menulink.gif); + padding-top: 3px; + padding-left: 20px; + height: 20px; + color: #ccc; +} + +#lijeva_kolona a:hover { + color: #FFF; + text-decoration: none; +} + +#sadrzaj { + width: 750px; + margin: 5px auto; + background-color: #fff; + color: #333; + line-height: 130%; +} + +#desna_kolona { + float: right; + width: 240px; + background: #FFF url(rightcontent.gif); + text-align:justify; + color: #ccc; +} + +.srednja_kolona { + margin-left: 135px; + margin-right: 240px; + max-width: 34em; + background: url(content.gif) top left; + text-align:justify; +} + +#menuheader { + background: url(menuheader.gif) no-repeat; + height: 23px; +} + +#menufooter { + background: url(menufooter.gif) no-repeat left top; + height: 21px; +} + +.text { + padding: 40px 18px 25px 18px; + color: #FFF; + background: url(footer.gif) no-repeat left bottom; +} + +.text_desno { + padding: 0px 18px 25px 18px; + color: #e2e2e2; + background: url(rightfooter.gif) no-repeat left bottom; +} + +#info { + color: #ccc; + background: #5982B3; + font-size: 9px; + margin-top: 5px; + margin-bottom: 5px; + padding: 5px; + border: 1px solid #6F92BD; +} + +#hmenu { + padding: 13px 0 5px 0; + float: right; +} + +#hmenu a { + padding: 12px 13px 2px 11px; + text-decoration: none; + color: #414141; + background: #ccc url(hmenuarrow.gif) no-repeat top center; + margin: 0 0 3px 1px; + border-bottom: 4px solid #BCBCBC; +} + +#hmenu a:hover { + background: #3C6491 url(hmenuarrowhover.gif) no-repeat top center; + color: #FFFFFF; + border-bottom: 4px solid #2F4A6C; +} diff --git a/xCAT-web/images/suse-s.gif b/xCAT-web/images/suse-s.gif new file mode 100644 index 000000000..2f0f32172 Binary files /dev/null and b/xCAT-web/images/suse-s.gif differ diff --git a/xCAT-web/images/switch.gif b/xCAT-web/images/switch.gif new file mode 100644 index 000000000..33d5e1320 Binary files /dev/null and b/xCAT-web/images/switch.gif differ diff --git a/xCAT-web/images/tab-current.gif b/xCAT-web/images/tab-current.gif new file mode 100644 index 000000000..bc4642f30 Binary files /dev/null and b/xCAT-web/images/tab-current.gif differ diff --git a/xCAT-web/images/tab.gif b/xCAT-web/images/tab.gif new file mode 100644 index 000000000..7bcbc9daf Binary files /dev/null and b/xCAT-web/images/tab.gif differ diff --git a/xCAT-web/images/unchecked-box.gif b/xCAT-web/images/unchecked-box.gif new file mode 100644 index 000000000..f45ab03b9 Binary files /dev/null and b/xCAT-web/images/unchecked-box.gif differ diff --git a/xCAT-web/images/white-ball-m.gif b/xCAT-web/images/white-ball-m.gif new file mode 100644 index 000000000..d902183b7 Binary files /dev/null and b/xCAT-web/images/white-ball-m.gif differ diff --git a/xCAT-web/images/white-percent.gif b/xCAT-web/images/white-percent.gif new file mode 100644 index 000000000..5a03f3547 Binary files /dev/null and b/xCAT-web/images/white-percent.gif differ diff --git a/xCAT-web/images/x335-front-blue.jpg b/xCAT-web/images/x335-front-blue.jpg new file mode 100644 index 000000000..99fb682a7 Binary files /dev/null and b/xCAT-web/images/x335-front-blue.jpg differ diff --git a/xCAT-web/images/x335-front-green.jpg b/xCAT-web/images/x335-front-green.jpg new file mode 100644 index 000000000..a8c6545ea Binary files /dev/null and b/xCAT-web/images/x335-front-green.jpg differ diff --git a/xCAT-web/images/x335-front-red.jpg b/xCAT-web/images/x335-front-red.jpg new file mode 100644 index 000000000..7435b1fce Binary files /dev/null and b/xCAT-web/images/x335-front-red.jpg differ diff --git a/xCAT-web/images/x335-front-yellow.jpg b/xCAT-web/images/x335-front-yellow.jpg new file mode 100644 index 000000000..9803c9aab Binary files /dev/null and b/xCAT-web/images/x335-front-yellow.jpg differ diff --git a/xCAT-web/images/x345-front-blue.jpg b/xCAT-web/images/x345-front-blue.jpg new file mode 100644 index 000000000..95bf3146f Binary files /dev/null and b/xCAT-web/images/x345-front-blue.jpg differ diff --git a/xCAT-web/images/x345-front-green.jpg b/xCAT-web/images/x345-front-green.jpg new file mode 100644 index 000000000..a36fbe32f Binary files /dev/null and b/xCAT-web/images/x345-front-green.jpg differ diff --git a/xCAT-web/images/x345-front-red.jpg b/xCAT-web/images/x345-front-red.jpg new file mode 100644 index 000000000..34b215ae8 Binary files /dev/null and b/xCAT-web/images/x345-front-red.jpg differ diff --git a/xCAT-web/images/x345-front-yellow.jpg b/xCAT-web/images/x345-front-yellow.jpg new file mode 100644 index 000000000..d5a90972f Binary files /dev/null and b/xCAT-web/images/x345-front-yellow.jpg differ diff --git a/xCAT-web/images/xCAT_icon.gif b/xCAT-web/images/xCAT_icon.gif new file mode 100644 index 000000000..ce87160e8 Binary files /dev/null and b/xCAT-web/images/xCAT_icon.gif differ diff --git a/xCAT-web/images/xcatlogo.gif b/xCAT-web/images/xcatlogo.gif new file mode 100644 index 000000000..1ba861bc3 Binary files /dev/null and b/xCAT-web/images/xcatlogo.gif differ diff --git a/xCAT-web/images/yellow-ball-m.gif b/xCAT-web/images/yellow-ball-m.gif new file mode 100644 index 000000000..82cf85d89 Binary files /dev/null and b/xCAT-web/images/yellow-ball-m.gif differ diff --git a/xCAT-web/images/yellow-ball-s.gif b/xCAT-web/images/yellow-ball-s.gif new file mode 100644 index 000000000..2c3668157 Binary files /dev/null and b/xCAT-web/images/yellow-ball-s.gif differ diff --git a/xCAT-web/index.php b/xCAT-web/index.php new file mode 100644 index 000000000..75b61b1df --- /dev/null +++ b/xCAT-web/index.php @@ -0,0 +1,85 @@ + +
    +

    Cluster Groups and Nodes

    + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +getAllGroupNames(); +$nodeGroupNames = $xcmdr->getAllXCATGroups(); + +// Print the HTML for each of them +foreach($nodeGroupNames as $key => $nodeGroupName) { + echo HTMLProducer::getXCATGroupTableRow($nodeGroupName); +} + +echo HTMLProducer::getXCATNodeTableFooter(); +?> + + + +
    +
    + + + + + + + + + + + + + +
    Node is good (Status is ready/pbs/sshd)
    Node is bad (Status is 'noping')
    Other status (unknown/node unavailable...)
    +
    + + \ No newline at end of file diff --git a/xCAT-web/javascripts/debug.js b/xCAT-web/javascripts/debug.js new file mode 100644 index 000000000..3d8f33883 --- /dev/null +++ b/xCAT-web/javascripts/debug.js @@ -0,0 +1,137 @@ +var debugWindow = null; +function debug(text, reverse) { + if (debugWindow == null) + return; + + time = "-"; //new Date(); + if (reverse) { + $('debug').innerHTML = time + " " + text + "
    "+ $('debug').innerHTML; + debugWindow.getContent().scrollTop=0; + } + else { + $('debug').innerHTML += time + " " + text + "
    "; + debugWindow.getContent().scrollTop=10000; // Far away + } +} + +function hideDebug() { + if (debugWindow) { + debugWindow.destroy(); + debugWindow = null; + } +} + +function showDebug(bShow) { + if (debugWindow == null) { + debugWindow = new Window('debug_window', {className: 'dialog',width:250, height:100, right:4, bottom:42, zIndex:1000, opacity:1, showEffect: Element.show, resizable: true, title: "Debug"}) + debugWindow.getContent().innerHTML = "
    "; + date=new Date; + date.setMonth(date.getMonth()+3); + + //debugWindow.setCookie(null, date); + } + if( typeof bShow == 'undefined' || bShow)debugWindow.show() +} + + +function clearDebug() { + if (debugWindow == null) + return; + $('debug').innerHTML = ""; +} + +/** + * document.createElement convenience wrapper + * + * The data parameter is an object that must have the "tag" key, containing + * a string with the tagname of the element to create. It can optionally have + * a "children" key which can be: a string, "data" object, or an array of "data" + * objects to append to this element as children. Any other key is taken as an + * attribute to be applied to this tag. + * + * Available under an MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * @param {Object} data The data representing the element to create + * @return {Element} The element created. + */ +function $E(data) { + var el; + if ('string'==typeof data) { + el=document.createTextNode(data); + } else { + //create the element + el=document.createElement(data.tag); + delete(data.tag); + + //append the children + if ('undefined'!=typeof data.children) { + if ('string'==typeof data.children ||'undefined'==typeof data.children.length) { + //strings and single elements + el.appendChild($E(data.children)); + } else { + //arrays of elements + for (var i=0, child=null; 'undefined'!=typeof (child=data.children[i]); i++) { + el.appendChild($E(child)); + } + } + delete(data.children); + } + + //any other data is attributes + for (attr in data) { + el[attr]=data[attr]; + } + } + + return el; +} + +// FROM Nick Hemsley +var Debug = { + inspectOutput: function (container, within) { + within = within || debugWindow.getContent() + + if (debugWindow == null) + return; + + within.appendChild(container) + }, + + inspect: function(object) { + var cont = $E({tag: "div", className: "inspector"}) + Debug.inspectObj(object, cont) + debugWindow.getContent().appendChild(cont) + }, + + inspectObj: function (object, container) { + for (prop in object) { + Debug.inspectOutput(Debug.inspectable(object, prop), container) + } + }, + + inspectable: function(object, prop) { + cont = $E({tag: 'div', className: 'inspectable', children: [prop + " value: " + object[prop] ]}) + cont.toInspect = object[prop] + Event.observe(cont, 'click', Debug.inspectClicked, false) + return cont + }, + + inspectClicked: function(e) { + Debug.inspectContained(Event.element(e)) + Event.stop(e) + }, + + inspectContained: function(container) { + if (container.opened) { + container.parentNode.removeChild(container.opened) + delete(container.opened) + } else { + sibling = container.parentNode.insertBefore($E({tag: "div", className: "child"}), container.nextSibling) + if (container.toInspect) + Debug.inspectObj(container.toInspect, sibling) + container.opened = sibling + } + } +} +var inspect = Debug.inspect; diff --git a/xCAT-web/javascripts/effects.js b/xCAT-web/javascripts/effects.js new file mode 100644 index 000000000..233aba86f --- /dev/null +++ b/xCAT-web/javascripts/effects.js @@ -0,0 +1,1094 @@ +// script.aculo.us effects.js v1.7.1_beta1, Mon Mar 12 14:40:50 +0100 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Contributors: +// Justin Palmer (http://encytemedia.com/) +// Mark Pilgrim (http://diveintomark.org/) +// Martin Bialasinki +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { + var color = '#'; + if(this.slice(0,4) == 'rgb(') { + var cols = this.slice(4,this.length-1).split(','); + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); + } else { + if(this.slice(0,1) == '#') { + if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if(this.length==7) color = this.toLowerCase(); + } + } + return(color.length==7 ? color : (arguments[0] || this)); +} + +/*--------------------------------------------------------------------------*/ + +Element.collectTextNodes = function(element) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); + }).flatten().join(''); +} + +Element.collectTextNodesIgnoreClass = function(element, className) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? + Element.collectTextNodesIgnoreClass(node, className) : '')); + }).flatten().join(''); +} + +Element.setContentZoom = function(element, percent) { + element = $(element); + element.setStyle({fontSize: (percent/100) + 'em'}); + if(Prototype.BrowserFeatures.WebKit) window.scrollBy(0,0); + return element; +} + +Element.getInlineOpacity = function(element){ + return $(element).style.opacity || ''; +} + +Element.forceRerendering = function(element) { + try { + element = $(element); + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch(e) { } +}; + +/*--------------------------------------------------------------------------*/ + +Array.prototype.call = function() { + var args = arguments; + this.each(function(f){ f.apply(this, args) }); +} + +/*--------------------------------------------------------------------------*/ + +var Effect = { + _elementDoesNotExistError: { + name: 'ElementDoesNotExistError', + message: 'The specified DOM element does not exist, but is required for this effect to operate' + }, + tagifyText: function(element) { + if(typeof Builder == 'undefined') + throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); + + var tagifyStyle = 'position:relative'; + if(Prototype.Browser.IE) tagifyStyle += ';zoom:1'; + + element = $(element); + $A(element.childNodes).each( function(child) { + if(child.nodeType==3) { + child.nodeValue.toArray().each( function(character) { + element.insertBefore( + Builder.node('span',{style: tagifyStyle}, + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if(((typeof element == 'object') || + (typeof element == 'function')) && + (element.length)) + elements = element; + else + elements = $(element).childNodes; + + var options = Object.extend({ + speed: 0.1, + delay: 0.0 + }, arguments[2] || {}); + var masterDelay = options.delay; + + $A(elements).each( function(element, index) { + new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); + }); + }, + PAIRS: { + 'slide': ['SlideDown','SlideUp'], + 'blind': ['BlindDown','BlindUp'], + 'appear': ['Appear','Fade'] + }, + toggle: function(element, effect) { + element = $(element); + effect = (effect || 'appear').toLowerCase(); + var options = Object.extend({ + queue: { position:'end', scope:(element.id || 'global'), limit: 1 } + }, arguments[2] || {}); + Effect[element.visible() ? + Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); + } +}; + +var Effect2 = Effect; // deprecated + +/* ------------- transitions ------------- */ + +Effect.Transitions = { + linear: Prototype.K, + sinoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + 0.5; + }, + reverse: function(pos) { + return 1-pos; + }, + flicker: function(pos) { + var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; + return (pos > 1 ? 1 : pos); + }, + wobble: function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; + }, + pulse: function(pos, pulses) { + pulses = pulses || 5; + return ( + Math.round((pos % (1/pulses)) * pulses) == 0 ? + ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : + 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) + ); + }, + none: function(pos) { + return 0; + }, + full: function(pos) { + return 1; + } +}; + +/* ------------- core effects ------------- */ + +Effect.ScopedQueue = Class.create(); +Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { + initialize: function() { + this.effects = []; + this.interval = null; + }, + _each: function(iterator) { + this.effects._each(iterator); + }, + add: function(effect) { + var timestamp = new Date().getTime(); + + var position = (typeof effect.options.queue == 'string') ? + effect.options.queue : effect.options.queue.position; + + switch(position) { + case 'front': + // move unstarted effects after this effect + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { + e.startOn += effect.finishOn; + e.finishOn += effect.finishOn; + }); + break; + case 'with-last': + timestamp = this.effects.pluck('startOn').max() || timestamp; + break; + case 'end': + // start effect after last queued effect has finished + timestamp = this.effects.pluck('finishOn').max() || timestamp; + break; + } + + effect.startOn += timestamp; + effect.finishOn += timestamp; + + if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) + this.effects.push(effect); + + if(!this.interval) + this.interval = setInterval(this.loop.bind(this), 15); + }, + remove: function(effect) { + this.effects = this.effects.reject(function(e) { return e==effect }); + if(this.effects.length == 0) { + clearInterval(this.interval); + this.interval = null; + } + }, + loop: function() { + var timePos = new Date().getTime(); + for(var i=0, len=this.effects.length;i= this.startOn) { + if(timePos >= this.finishOn) { + this.render(1.0); + this.cancel(); + this.event('beforeFinish'); + if(this.finish) this.finish(); + this.event('afterFinish'); + return; + } + var pos = (timePos - this.startOn) / this.totalTime, + frame = Math.round(pos * this.totalFrames); + if(frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + cancel: function() { + if(!this.options.sync) + Effect.Queues.get(typeof this.options.queue == 'string' ? + 'global' : this.options.queue.scope).remove(this); + this.state = 'finished'; + }, + event: function(eventName) { + if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if(this.options[eventName]) this.options[eventName](this); + }, + inspect: function() { + var data = $H(); + for(property in this) + if(typeof this[property] != 'function') data[property] = this[property]; + return '#'; + } +} + +Effect.Parallel = Class.create(); +Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + this.effects.invoke('render', position); + }, + finish: function(position) { + this.effects.each( function(effect) { + effect.render(1.0); + effect.cancel(); + effect.event('beforeFinish'); + if(effect.finish) effect.finish(position); + effect.event('afterFinish'); + }); + } +}); + +Effect.Event = Class.create(); +Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), { + initialize: function() { + var options = Object.extend({ + duration: 0 + }, arguments[0] || {}); + this.start(options); + }, + update: Prototype.emptyFunction +}); + +Effect.Opacity = Class.create(); +Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + // make this work on IE on elements without 'layout' + if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + var options = Object.extend({ + from: this.element.getOpacity() || 0.0, + to: 1.0 + }, arguments[1] || {}); + this.start(options); + }, + update: function(position) { + this.element.setOpacity(position); + } +}); + +Effect.Move = Class.create(); +Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + x: 0, + y: 0, + mode: 'relative' + }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Bug in Opera: Opera returns the "real" position of a static element or + // relative element that does not have top/left explicitly set. + // ==> Always set top and left for position relative elements in your stylesheets + // (to 0 if you do not need them) + this.element.makePositioned(); + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); + if(this.options.mode == 'absolute') { + // absolute movement, so we need to calc deltaX and deltaY + this.options.x = this.options.x - this.originalLeft; + this.options.y = this.options.y - this.originalTop; + } + }, + update: function(position) { + this.element.setStyle({ + left: Math.round(this.options.x * position + this.originalLeft) + 'px', + top: Math.round(this.options.y * position + this.originalTop) + 'px' + }); + } +}); + +// for backwards compatibility +Effect.MoveBy = function(element, toTop, toLeft) { + return new Effect.Move(element, + Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); +}; + +Effect.Scale = Class.create(); +Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { + initialize: function(element, percent) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or {} with provided values + scaleFrom: 100.0, + scaleTo: percent + }, arguments[2] || {}); + this.start(options); + }, + setup: function() { + this.restoreAfterFinish = this.options.restoreAfterFinish || false; + this.elementPositioning = this.element.getStyle('position'); + + this.originalStyle = {}; + ['top','left','width','height','fontSize'].each( function(k) { + this.originalStyle[k] = this.element.style[k]; + }.bind(this)); + + this.originalTop = this.element.offsetTop; + this.originalLeft = this.element.offsetLeft; + + var fontSize = this.element.getStyle('font-size') || '100%'; + ['em','px','%','pt'].each( function(fontSizeType) { + if(fontSize.indexOf(fontSizeType)>0) { + this.fontSize = parseFloat(fontSize); + this.fontSizeType = fontSizeType; + } + }.bind(this)); + + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; + + this.dims = null; + if(this.options.scaleMode=='box') + this.dims = [this.element.offsetHeight, this.element.offsetWidth]; + if(/^content/.test(this.options.scaleMode)) + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; + if(!this.dims) + this.dims = [this.options.scaleMode.originalHeight, + this.options.scaleMode.originalWidth]; + }, + update: function(position) { + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if(this.options.scaleContent && this.fontSize) + this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); + }, + finish: function(position) { + if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle); + }, + setDimensions: function(height, width) { + var d = {}; + if(this.options.scaleX) d.width = Math.round(width) + 'px'; + if(this.options.scaleY) d.height = Math.round(height) + 'px'; + if(this.options.scaleFromCenter) { + var topd = (height - this.dims[0])/2; + var leftd = (width - this.dims[1])/2; + if(this.elementPositioning == 'absolute') { + if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; + if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; + } else { + if(this.options.scaleY) d.top = -topd + 'px'; + if(this.options.scaleX) d.left = -leftd + 'px'; + } + } + this.element.setStyle(d); + } +}); + +Effect.Highlight = Class.create(); +Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Prevent executing on elements not in the layout flow + if(this.element.getStyle('display')=='none') { this.cancel(); return; } + // Disable background image during the effect + this.oldStyle = {}; + if (!this.options.keepBackgroundImage) { + this.oldStyle.backgroundImage = this.element.getStyle('background-image'); + this.element.setStyle({backgroundImage: 'none'}); + } + if(!this.options.endcolor) + this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); + if(!this.options.restorecolor) + this.options.restorecolor = this.element.getStyle('background-color'); + // init color calculations + this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); + this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); + }, + update: function(position) { + this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ + return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); + }, + finish: function() { + this.element.setStyle(Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = Class.create(); +Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + this.start(arguments[1] || {}); + }, + setup: function() { + Position.prepare(); + var offsets = Position.cumulativeOffset(this.element); + if(this.options.offset) offsets[1] += this.options.offset; + var max = window.innerHeight ? + window.height - window.innerHeight : + document.body.scrollHeight - + (document.documentElement.clientHeight ? + document.documentElement.clientHeight : document.body.clientHeight); + this.scrollStart = Position.deltaY; + this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; + }, + update: function(position) { + Position.prepare(); + window.scrollTo(Position.deltaX, + this.scrollStart + (position*this.delta)); + } +}); + +/* ------------- combination effects ------------- */ + +Effect.Fade = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + var options = Object.extend({ + from: element.getOpacity() || 1.0, + to: 0.0, + afterFinishInternal: function(effect) { + if(effect.options.to!=0) return; + effect.element.hide().setStyle({opacity: oldOpacity}); + }}, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Appear = function(element) { + element = $(element); + var options = Object.extend({ + from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), + to: 1.0, + // force Safari to render floated elements properly + afterFinishInternal: function(effect) { + effect.element.forceRerendering(); + }, + beforeSetup: function(effect) { + effect.element.setOpacity(effect.options.from).show(); + }}, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Puff = function(element) { + element = $(element); + var oldStyle = { + opacity: element.getInlineOpacity(), + position: element.getStyle('position'), + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height + }; + return new Effect.Parallel( + [ new Effect.Scale(element, 200, + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], + Object.extend({ duration: 1.0, + beforeSetupInternal: function(effect) { + Position.absolutize(effect.effects[0].element) + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().setStyle(oldStyle); } + }, arguments[1] || {}) + ); +} + +Effect.BlindUp = function(element) { + element = $(element); + element.makeClipping(); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + restoreAfterFinish: true, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }, arguments[1] || {}) + ); +} + +Effect.BlindDown = function(element) { + element = $(element); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + } + }, arguments[1] || {})); +} + +Effect.SwitchOff = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + return new Effect.Appear(element, Object.extend({ + duration: 0.4, + from: 0, + transition: Effect.Transitions.flicker, + afterFinishInternal: function(effect) { + new Effect.Scale(effect.element, 1, { + duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); + } + }) + } + }, arguments[1] || {})); +} + +Effect.DropOut = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left'), + opacity: element.getInlineOpacity() }; + return new Effect.Parallel( + [ new Effect.Move(element, {x: 0, y: 100, sync: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 }) ], + Object.extend( + { duration: 0.5, + beforeSetup: function(effect) { + effect.effects[0].element.makePositioned(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); + } + }, arguments[1] || {})); +} + +Effect.Shake = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left') }; + return new Effect.Move(element, + { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { + effect.element.undoPositioned().setStyle(oldStyle); + }}) }}) }}) }}) }}) }}); +} + +Effect.SlideDown = function(element) { + element = $(element).cleanWhitespace(); + // SlideDown need to have the content of the element wrapped in a container element with fixed height! + var oldInnerBottom = element.down().getStyle('bottom'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: window.opera ? 0 : 1, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if(window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } + }, arguments[1] || {}) + ); +} + +Effect.SlideUp = function(element) { + element = $(element).cleanWhitespace(); + var oldInnerBottom = element.down().getStyle('bottom'); + return new Effect.Scale(element, window.opera ? 0 : 1, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + restoreAfterFinish: true, + beforeStartInternal: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if(window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom}); + effect.element.down().undoPositioned(); + } + }, arguments[1] || {}) + ); +} + +// Bug in opera makes the TD containing this element expand for a instance after finish +Effect.Squish = function(element) { + return new Effect.Scale(element, window.opera ? 1 : 0, { + restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }); +} + +Effect.Grow = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.full + }, arguments[1] || {}); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = dims.width; + initialMoveY = moveY = 0; + moveX = -dims.width; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = dims.height; + moveY = -dims.height; + break; + case 'bottom-right': + initialMoveX = dims.width; + initialMoveY = dims.height; + moveX = -dims.width; + moveY = -dims.height; + break; + case 'center': + initialMoveX = dims.width / 2; + initialMoveY = dims.height / 2; + moveX = -dims.width / 2; + moveY = -dims.height / 2; + break; + } + + return new Effect.Move(element, { + x: initialMoveX, + y: initialMoveY, + duration: 0.01, + beforeSetup: function(effect) { + effect.element.hide().makeClipping().makePositioned(); + }, + afterFinishInternal: function(effect) { + new Effect.Parallel( + [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), + new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), + new Effect.Scale(effect.element, 100, { + scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, + sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) + ], Object.extend({ + beforeSetup: function(effect) { + effect.effects[0].element.setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); + } + }, options) + ) + } + }); +} + +Effect.Shrink = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.none + }, arguments[1] || {}); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = dims.width; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = dims.height; + break; + case 'bottom-right': + moveX = dims.width; + moveY = dims.height; + break; + case 'center': + moveX = dims.width / 2; + moveY = dims.height / 2; + break; + } + + return new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), + new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), + new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) + ], Object.extend({ + beforeStartInternal: function(effect) { + effect.effects[0].element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } + }, options) + ); +} + +Effect.Pulsate = function(element) { + element = $(element); + var options = arguments[1] || {}; + var oldOpacity = element.getInlineOpacity(); + var transition = options.transition || Effect.Transitions.sinoidal; + var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; + reverser.bind(transition); + return new Effect.Opacity(element, + Object.extend(Object.extend({ duration: 2.0, from: 0, + afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } + }, options), {transition: reverser})); +} + +Effect.Fold = function(element) { + element = $(element); + var oldStyle = { + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height }; + element.makeClipping(); + return new Effect.Scale(element, 5, Object.extend({ + scaleContent: false, + scaleX: false, + afterFinishInternal: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleY: false, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().setStyle(oldStyle); + } }); + }}, arguments[1] || {})); +}; + +Effect.Morph = Class.create(); +Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + style: {} + }, arguments[1] || {}); + if (typeof options.style == 'string') { + if(options.style.indexOf(':') == -1) { + var cssText = '', selector = '.' + options.style; + $A(document.styleSheets).reverse().each(function(styleSheet) { + if (styleSheet.cssRules) cssRules = styleSheet.cssRules; + else if (styleSheet.rules) cssRules = styleSheet.rules; + $A(cssRules).reverse().each(function(rule) { + if (selector == rule.selectorText) { + cssText = rule.style.cssText; + throw $break; + } + }); + if (cssText) throw $break; + }); + this.style = cssText.parseStyle(); + options.afterFinishInternal = function(effect){ + effect.element.addClassName(effect.options.style); + effect.transforms.each(function(transform) { + if(transform.style != 'opacity') + effect.element.style[transform.style] = ''; + }); + } + } else this.style = options.style.parseStyle(); + } else this.style = $H(options.style) + this.start(options); + }, + setup: function(){ + function parseColor(color){ + if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; + color = color.parseColor(); + return $R(0,2).map(function(i){ + return parseInt( color.slice(i*2+1,i*2+3), 16 ) + }); + } + this.transforms = this.style.map(function(pair){ + var property = pair[0], value = pair[1], unit = null; + + if(value.parseColor('#zzzzzz') != '#zzzzzz') { + value = value.parseColor(); + unit = 'color'; + } else if(property == 'opacity') { + value = parseFloat(value); + if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + } else if(Element.CSS_LENGTH.test(value)) { + var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); + value = parseFloat(components[1]); + unit = (components.length == 3) ? components[2] : null; + } + + var originalValue = this.element.getStyle(property); + return { + style: property.camelize(), + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), + targetValue: unit=='color' ? parseColor(value) : value, + unit: unit + }; + }.bind(this)).reject(function(transform){ + return ( + (transform.originalValue == transform.targetValue) || + ( + transform.unit != 'color' && + (isNaN(transform.originalValue) || isNaN(transform.targetValue)) + ) + ) + }); + }, + update: function(position) { + var style = {}, transform, i = this.transforms.length; + while(i--) + style[(transform = this.transforms[i]).style] = + transform.unit=='color' ? '#'+ + (Math.round(transform.originalValue[0]+ + (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + + (Math.round(transform.originalValue[1]+ + (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + + (Math.round(transform.originalValue[2]+ + (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : + transform.originalValue + Math.round( + ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit; + this.element.setStyle(style, true); + } +}); + +Effect.Transform = Class.create(); +Object.extend(Effect.Transform.prototype, { + initialize: function(tracks){ + this.tracks = []; + this.options = arguments[1] || {}; + this.addTracks(tracks); + }, + addTracks: function(tracks){ + tracks.each(function(track){ + var data = $H(track).values().first(); + this.tracks.push($H({ + ids: $H(track).keys().first(), + effect: Effect.Morph, + options: { style: data } + })); + }.bind(this)); + return this; + }, + play: function(){ + return new Effect.Parallel( + this.tracks.map(function(track){ + var elements = [$(track.ids) || $$(track.ids)].flatten(); + return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) }); + }).flatten(), + this.options + ); + } +}); + +Element.CSS_PROPERTIES = $w( + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + + 'fontSize fontWeight height left letterSpacing lineHeight ' + + 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ + 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + + 'right textIndent top width wordSpacing zIndex'); + +Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; + +String.prototype.parseStyle = function(){ + var element = document.createElement('div'); + element.innerHTML = '
    '; + var style = element.childNodes[0].style, styleRules = $H(); + + Element.CSS_PROPERTIES.each(function(property){ + if(style[property]) styleRules[property] = style[property]; + }); + if(Prototype.Browser.IE && this.indexOf('opacity') > -1) { + styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]; + } + return styleRules; +}; + +Element.morph = function(element, style) { + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {})); + return element; +}; + +['getInlineOpacity','forceRerendering','setContentZoom', + 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( + function(f) { Element.Methods[f] = Element[f]; } +); + +Element.Methods.visualEffect = function(element, effect, options) { + s = effect.dasherize().camelize(); + effect_class = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[effect_class](element, options); + return $(element); +}; + +Element.addMethods(); \ No newline at end of file diff --git a/xCAT-web/javascripts/extended_debug.js b/xCAT-web/javascripts/extended_debug.js new file mode 100644 index 000000000..2b935b1b7 --- /dev/null +++ b/xCAT-web/javascripts/extended_debug.js @@ -0,0 +1,113 @@ +var commandHistory; +var historyIndex; + +function showExtendedDebug() { + if (debugWindow != null) { + hideDebug(); + } + + if (debugWindow == null) { + commandHistory = new Array(); + historyIndex = 0; + + debugWindow = new Window('debug_window', {className: 'dialog',width:250, height:100, right:4, minWidth:250, bottom:42, zIndex:1000, opacity:1, showEffect: Element.show, resizable: true, title: "Debug"}) + debugWindow.getContent().innerHTML = "
    "; + + //create hourglass icon and attach events to it. + var cont = "
    "; + + new Insertion.After('debug_window_maximize', cont); + Event.observe('debug_window_inspect', 'click', enterInspectionMode, false); + + //create command text box + cont = "Eval:" + debugWindow.setStatusBar(cont); + + Event.observe('debug_window_command', 'mousedown', donothing); + Event.observe('debug_window_command', 'keypress', evalJS, false); + } + debugWindow.show(); +} + +function donothing(evt){ + Field.activate('debug_window_command'); + return false; +} + +function evalJS(evt){ + if(evt.keyCode == Event.KEY_RETURN){ + var js = $F('debug_window_command'); + try{ + var ret = eval(js); + if(ret != null) + debug(ret); + }catch(e){ + debug(e); + } + $('debug_window_command').value = ''; + + Field.activate('debug_window_command'); + commandHistory.push(js); + historyIndex = 0; + } + + if(evt.keyCode == Event.KEY_UP){ + if(commandHistory.length > historyIndex){ + historyIndex++; + var js = commandHistory[commandHistory.length-historyIndex]; + $('debug_window_command').value = js; + Event.stop(evt); + Field.activate('debug_window_command'); + } + } + + if(evt.keyCode == Event.KEY_DOWN){ + if(commandHistory.length >= historyIndex && historyIndex > 1){ + historyIndex--; + var js = commandHistory[commandHistory.length-historyIndex]; + $('debug_window_command').value = js; + Event.stop(evt); + Field.activate('debug_window_command'); + } + } +} + +function enterInspectionMode(evt){ + //stop observing magnifying glass + Event.stopObserving('debug_window_inspect', 'click', enterInspectionMode, false); + //change pointer + document.body.style.cursor='help'; + //start observing mouse clicks + Event.observe(window, 'click', inspectItem, false); +} + +function inspectItem(evt){ + // the element that triggered the event + var element = Event.element(evt); + if(element.id!="debug_window_inspect"){ + clearDebug() + //change pointer + document.body.style.cursor='default'; + debug(element.id); + inspect(element); + //stop observing mouse clicks + Event.stopObserving(window, 'click', inspectItem, false); + //alert('doing something'); + //start observing mag + Event.observe('debug_window_inspect', 'click', enterInspectionMode, false); + } +} + +function clearDebug() { + var win = $('debug'); + if (win == null) + return; + + win.innerHTML=" "; + //clear inspections too + var divs = document.getElementsByClassName('inspector'); + divs.each(function(div){ + Element.remove(div); + }); +} + diff --git a/xCAT-web/javascripts/prototype.js b/xCAT-web/javascripts/prototype.js new file mode 100644 index 000000000..826de9fa3 --- /dev/null +++ b/xCAT-web/javascripts/prototype.js @@ -0,0 +1,3269 @@ +/* Prototype JavaScript framework, version 1.5.1_rc3 + * (c) 2005-2007 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.5.1_rc3', + + Browser: { + IE: !!(window.attachEvent && !window.opera), + Opera: !!window.opera, + WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, + Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1 + }, + + BrowserFeatures: { + XPath: !!document.evaluate, + ElementExtensions: !!window.HTMLElement, + SpecificElementExtensions: + (document.createElement('div').__proto__ !== + document.createElement('form').__proto__) + }, + + ScriptFragment: ']*>([\u0001-\uFFFF]*?)', + JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/, + + emptyFunction: function() { }, + K: function(x) { return x } +} + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +} + +var Abstract = new Object(); + +Object.extend = function(destination, source) { + for (var property in source) { + destination[property] = source[property]; + } + return destination; +} + +Object.extend(Object, { + inspect: function(object) { + try { + if (object === undefined) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : object.toString(); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + }, + + toJSON: function(object) { + var type = typeof object; + switch(type) { + case 'undefined': + case 'function': + case 'unknown': return; + case 'boolean': return object.toString(); + } + if (object === null) return 'null'; + if (object.toJSON) return object.toJSON(); + if (object.ownerDocument === document) return; + var results = []; + for (var property in object) { + var value = Object.toJSON(object[property]); + if (value !== undefined) + results.push(property.toJSON() + ': ' + value); + } + return '{' + results.join(', ') + '}'; + }, + + keys: function(object) { + var keys = []; + for (var property in object) + keys.push(property); + return keys; + }, + + values: function(object) { + var values = []; + for (var property in object) + values.push(object[property]); + return values; + }, + + clone: function(object) { + return Object.extend({}, object); + } +}); + +Function.prototype.bind = function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } +} + +Function.prototype.bindAsEventListener = function(object) { + var __method = this, args = $A(arguments), object = args.shift(); + return function(event) { + return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); + } +} + +Object.extend(Number.prototype, { + toColorPart: function() { + return this.toPaddedString(2, 16); + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + }, + + toPaddedString: function(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + }, + + toJSON: function() { + return isFinite(this) ? this.toString() : 'null'; + } +}); + +Date.prototype.toJSON = function() { + return '"' + this.getFullYear() + '-' + + (this.getMonth() + 1).toPaddedString(2) + '-' + + this.getDate().toPaddedString(2) + 'T' + + this.getHours().toPaddedString(2) + ':' + + this.getMinutes().toPaddedString(2) + ':' + + this.getSeconds().toPaddedString(2) + '"'; +}; + +var Try = { + these: function() { + var returnValue; + + for (var i = 0, length = arguments.length; i < length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) {} + } + + return returnValue; + } +} + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create(); +PeriodicalExecuter.prototype = { + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.callback(this); + } finally { + this.currentlyExecuting = false; + } + } + } +} +Object.extend(String, { + interpret: function(value) { + return value == null ? '' : String(value); + }, + specialChar: { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\\': '\\\\' + } +}); + +Object.extend(String.prototype, { + gsub: function(pattern, replacement) { + var result = '', source = this, match; + replacement = arguments.callee.prepareReplacement(replacement); + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + }, + + sub: function(pattern, replacement, count) { + replacement = this.gsub.prepareReplacement(replacement); + count = count === undefined ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + }, + + scan: function(pattern, iterator) { + this.gsub(pattern, iterator); + return this; + }, + + truncate: function(length, truncation) { + length = length || 30; + truncation = truncation === undefined ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : this; + }, + + strip: function() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + }, + + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(function(script) { return eval(script) }); + }, + + escapeHTML: function() { + var self = arguments.callee; + self.text.data = this; + return self.div.innerHTML; + }, + + unescapeHTML: function() { + var div = document.createElement('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? (div.childNodes.length > 1 ? + $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : + div.childNodes[0].nodeValue) : ''; + }, + + toQueryParams: function(separator) { + var match = this.strip().match(/([^?#]*)(#.*)?$/); + if (!match) return {}; + + return match[1].split(separator || '&').inject({}, function(hash, pair) { + if ((pair = pair.split('='))[0]) { + var key = decodeURIComponent(pair.shift()); + var value = pair.length > 1 ? pair.join('=') : pair[0]; + if (value != undefined) value = decodeURIComponent(value); + + if (key in hash) { + if (hash[key].constructor != Array) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + }, + + toArray: function() { + return this.split(''); + }, + + succ: function() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + }, + + times: function(count) { + var result = ''; + for (var i = 0; i < count; i++) result += this; + return result; + }, + + camelize: function() { + var parts = this.split('-'), len = parts.length; + if (len == 1) return parts[0]; + + var camelized = this.charAt(0) == '-' + ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) + : parts[0]; + + for (var i = 1; i < len; i++) + camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); + + return camelized; + }, + + capitalize: function() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + }, + + underscore: function() { + return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); + }, + + dasherize: function() { + return this.gsub(/_/,'-'); + }, + + inspect: function(useDoubleQuotes) { + var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { + var character = String.specialChar[match[0]]; + return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + }, + + toJSON: function() { + return this.inspect(true); + }, + + unfilterJSON: function(filter) { + return this.sub(filter || Prototype.JSONFilter, '#{1}'); + }, + + evalJSON: function(sanitize) { + var json = this.unfilterJSON(); + try { + if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json))) + return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + }, + + include: function(pattern) { + return this.indexOf(pattern) > -1; + }, + + startsWith: function(pattern) { + return this.indexOf(pattern) === 0; + }, + + endsWith: function(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.lastIndexOf(pattern) === d; + }, + + empty: function() { + return this == ''; + }, + + blank: function() { + return /^\s*$/.test(this); + } +}); + +if (Prototype.BrowserFeatures.WebKit || Prototype.BrowserFeatures.IE) Object.extend(String.prototype, { + escapeHTML: function() { + return this.replace(/&/g,'&').replace(//g,'>'); + }, + unescapeHTML: function() { + return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + } +}); + +String.prototype.gsub.prepareReplacement = function(replacement) { + if (typeof replacement == 'function') return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; +} + +String.prototype.parseQuery = String.prototype.toQueryParams; + +Object.extend(String.prototype.escapeHTML, { + div: document.createElement('div'), + text: document.createTextNode('') +}); + +with (String.prototype.escapeHTML) div.appendChild(text); + +var Template = Class.create(); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; +Template.prototype = { + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + return this.template.gsub(this.pattern, function(match) { + var before = match[1]; + if (before == '\\') return match[2]; + return before + String.interpret(object[match[3]]); + }); + } +} + +var $break = new Object(); +var $continue = new Object(); + +var Enumerable = { + each: function(iterator) { + var index = 0; + try { + this._each(function(value) { + iterator(value, index++); + }); + } catch (e) { + if (e != $break) throw e; + } + return this; + }, + + eachSlice: function(number, iterator) { + var index = -number, slices = [], array = this.toArray(); + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.map(iterator); + }, + + all: function(iterator) { + var result = true; + this.each(function(value, index) { + result = result && !!(iterator || Prototype.K)(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator) { + var result = false; + this.each(function(value, index) { + if (result = !!(iterator || Prototype.K)(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator) { + var results = []; + this.each(function(value, index) { + results.push((iterator || Prototype.K)(value, index)); + }); + return results; + }, + + detect: function(iterator) { + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator) { + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(pattern, iterator) { + var results = []; + this.each(function(value, index) { + var stringValue = value.toString(); + if (stringValue.match(pattern)) + results.push((iterator || Prototype.K)(value, index)); + }) + return results; + }, + + include: function(object) { + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inGroupsOf: function(number, fillWith) { + fillWith = fillWith === undefined ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + }, + + inject: function(memo, iterator) { + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (result == undefined || value >= result) + result = value; + }); + return result; + }, + + min: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (result == undefined || value < result) + result = value; + }); + return result; + }, + + partition: function(iterator) { + var trues = [], falses = []; + this.each(function(value, index) { + ((iterator || Prototype.K)(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value, index) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator) { + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator) { + return this.map(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.map(); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (typeof args.last() == 'function') + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + }, + + size: function() { + return this.toArray().length; + }, + + inspect: function() { + return '#'; + } +} + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray +}); +var $A = Array.from = function(iterable) { + if (!iterable) return []; + if (iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0, length = iterable.length; i < length; i++) + results.push(iterable[i]); + return results; + } +} + +if (Prototype.BrowserFeatures.WebKit) { + $A = Array.from = function(iterable) { + if (!iterable) return []; + if (!(typeof iterable == 'function' && iterable == '[object NodeList]') && + iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0, length = iterable.length; i < length; i++) + results.push(iterable[i]); + return results; + } + } +} + +Object.extend(Array.prototype, Enumerable); + +if (!Array.prototype._reverse) + Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0, length = this.length; i < length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(value && value.constructor == Array ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + indexOf: function(object) { + for (var i = 0, length = this.length; i < length; i++) + if (this[i] == object) return i; + return -1; + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + reduce: function() { + return this.length > 1 ? this : this[0]; + }, + + uniq: function(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + }, + + clone: function() { + return [].concat(this); + }, + + size: function() { + return this.length; + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + }, + + toJSON: function() { + var results = []; + this.each(function(object) { + var value = Object.toJSON(object); + if (value !== undefined) results.push(value); + }); + return '[' + results.join(', ') + ']'; + } +}); + +Array.prototype.toArray = Array.prototype.clone; + +function $w(string) { + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +if (Prototype.BrowserFeatures.Opera){ + Array.prototype.concat = function() { + var array = []; + for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); + for (var i = 0, length = arguments.length; i < length; i++) { + if (arguments[i].constructor == Array) { + for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) + array.push(arguments[i][j]); + } else { + array.push(arguments[i]); + } + } + return array; + } +} +var Hash = function(object) { + if (object instanceof Hash) this.merge(object); + else Object.extend(this, object || {}); +}; + +Object.extend(Hash, { + toQueryString: function(obj) { + var parts = []; + parts.add = arguments.callee.addPair; + + this.prototype._each.call(obj, function(pair) { + if (!pair.key) return; + var value = pair.value; + + if (value && typeof value == 'object') { + if (value.constructor == Array) value.each(function(value) { + parts.add(pair.key, value); + }); + return; + } + parts.add(pair.key, value); + }); + + return parts.join('&'); + }, + + toJSON: function(object) { + var results = []; + this.prototype._each.call(object, function(pair) { + var value = Object.toJSON(pair.value); + if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value); + }); + return '{' + results.join(', ') + '}'; + } +}); + +Hash.toQueryString.addPair = function(key, value, prefix) { + key = encodeURIComponent(key); + if (value === undefined) this.push(key); + else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value))); +} + +Object.extend(Hash.prototype, Enumerable); +Object.extend(Hash.prototype, { + _each: function(iterator) { + for (var key in this) { + var value = this[key]; + if (value && value == Hash.prototype[key]) continue; + + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + merge: function(hash) { + return $H(hash).inject(this, function(mergedHash, pair) { + mergedHash[pair.key] = pair.value; + return mergedHash; + }); + }, + + remove: function() { + var result; + for(var i = 0, length = arguments.length; i < length; i++) { + var value = this[arguments[i]]; + if (value !== undefined){ + if (result === undefined) result = value; + else { + if (result.constructor != Array) result = [result]; + result.push(value) + } + } + delete this[arguments[i]]; + } + return result; + }, + + toQueryString: function() { + return Hash.toQueryString(this); + }, + + inspect: function() { + return '#'; + }, + + toJSON: function() { + return Hash.toJSON(this); + } +}); + +function $H(object) { + if (object instanceof Hash) return object; + return new Hash(object); +}; + +// Safari iterates over shadowed properties +if (function() { + var i = 0, Test = function(value) { this.key = value }; + Test.prototype.key = 'foo'; + for (var property in new Test('bar')) i++; + return i > 1; +}()) Hash.prototype._each = function(iterator) { + var cache = []; + for (var key in this) { + var value = this[key]; + if ((value && value == Hash.prototype[key]) || cache.include(key)) continue; + cache.push(key); + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } +}; +ObjectRange = Class.create(); +Object.extend(ObjectRange.prototype, Enumerable); +Object.extend(ObjectRange.prototype, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + while (this.include(value)) { + iterator(value); + value = value.succ(); + } + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +} + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responder) { + if (!this.include(responder)) + this.responders.push(responder); + }, + + unregister: function(responder) { + this.responders = this.responders.without(responder); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (typeof responder[callback] == 'function') { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) {} + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { + Ajax.activeRequestCount++; + }, + onComplete: function() { + Ajax.activeRequestCount--; + } +}); + +Ajax.Base = function() {}; +Ajax.Base.prototype = { + setOptions: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '' + } + Object.extend(this.options, options || {}); + + this.options.method = this.options.method.toLowerCase(); + if (typeof this.options.parameters == 'string') + this.options.parameters = this.options.parameters.toQueryParams(); + } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { + _complete: false, + + initialize: function(url, options) { + this.transport = Ajax.getTransport(); + this.setOptions(options); + this.request(url); + }, + + request: function(url) { + this.url = url; + this.method = this.options.method; + var params = Object.clone(this.options.parameters); + + if (!['get', 'post'].include(this.method)) { + // simulate other verbs over post + params['_method'] = this.method; + this.method = 'post'; + } + + this.parameters = params; + + if (params = Hash.toQueryString(params)) { + // when GET, append parameters to URL + if (this.method == 'get') + this.url += (this.url.include('?') ? '&' : '?') + params; + else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + params += '&_='; + } + + try { + if (this.options.onCreate) this.options.onCreate(this.transport); + Ajax.Responders.dispatch('onCreate', this, this.transport); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) + setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); + + this.transport.onreadystatechange = this.onStateChange.bind(this); + this.setRequestHeaders(); + + this.body = this.method == 'post' ? (this.options.postBody || params) : null; + this.transport.send(this.body); + + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + + } + catch (e) { + this.dispatchException(e); + } + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState > 1 && !((readyState == 4) && this._complete)) + this.respondToReadyState(this.transport.readyState); + }, + + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'X-Prototype-Version': Prototype.Version, + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) + headers['Connection'] = 'close'; + } + + // user-defined headers + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (typeof extras.push == 'function') + for (var i = 0, length = extras.length; i < length; i += 2) + headers[extras[i]] = extras[i+1]; + else + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); + } + + for (var name in headers) + this.transport.setRequestHeader(name, headers[name]); + }, + + success: function() { + return !this.transport.status + || (this.transport.status >= 200 && this.transport.status < 300); + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.evalJSON(); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + this.transport.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(transport, json); + } catch (e) { + this.dispatchException(e); + } + + var contentType = this.getHeader('Content-type'); + if (contentType && contentType.strip(). + match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(transport, json); + Ajax.Responders.dispatch('on' + state, this, transport, json); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + // avoid memory leak in MSIE: clean up + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name); + } catch (e) { return null } + }, + + evalJSON: function() { + try { + var json = this.getHeader('X-JSON'); + return json ? json.evalJSON() : null; + } catch (e) { return null } + }, + + evalResponse: function() { + try { + return eval((this.transport.responseText || '').unfilterJSON()); + } catch (e) { + this.dispatchException(e); + } + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Updater = Class.create(); + +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { + initialize: function(container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + } + + this.transport = Ajax.getTransport(); + this.setOptions(options); + + var onComplete = this.options.onComplete || Prototype.emptyFunction; + this.options.onComplete = (function(transport, param) { + this.updateContent(); + onComplete(transport, param); + }).bind(this); + + this.request(url); + }, + + updateContent: function() { + var receiver = this.container[this.success() ? 'success' : 'failure']; + var response = this.transport.responseText; + + if (!this.options.evalScripts) response = response.stripScripts(); + + if (receiver = $(receiver)) { + if (this.options.insertion) + new this.options.insertion(receiver, response); + else + receiver.update(response); + } + + if (this.success()) { + if (this.onComplete) + setTimeout(this.onComplete.bind(this), 10); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { + initialize: function(container, url, options) { + this.setOptions(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = {}; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.options.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(request) { + if (this.options.decay) { + this.decay = (request.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = request.responseText; + } + this.timer = setTimeout(this.onTimerEvent.bind(this), + this.decay * this.frequency * 1000); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +function $(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push($(arguments[i])); + return elements; + } + if (typeof element == 'string') + element = document.getElementById(element); + return Element.extend(element); +} + +if (Prototype.BrowserFeatures.XPath) { + document._getElementsByXPath = function(expression, parentElement) { + var results = []; + var query = document.evaluate(expression, $(parentElement) || document, + null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + for (var i = 0, length = query.snapshotLength; i < length; i++) + results.push(query.snapshotItem(i)); + return results; + }; + + document.getElementsByClassName = function(className, parentElement) { + var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]"; + return document._getElementsByXPath(q, parentElement); + } + +} else document.getElementsByClassName = function(className, parentElement) { + var children = ($(parentElement) || document.body).getElementsByTagName('*'); + var elements = [], child; + for (var i = 0, length = children.length; i < length; i++) { + child = children[i]; + if (Element.hasClassName(child, className)) + elements.push(Element.extend(child)); + } + return elements; +}; + +/*--------------------------------------------------------------------------*/ + +if (!window.Element) var Element = {}; + +Element.extend = function(element) { + var F = Prototype.BrowserFeatures; + if (!element || !element.tagName || element.nodeType == 3 || + element._extended || F.SpecificElementExtensions || element == window) + return element; + + var methods = {}, tagName = element.tagName, cache = Element.extend.cache, + T = Element.Methods.ByTag; + + // extend methods for all tags (Safari doesn't need this) + if (!F.ElementExtensions) { + Object.extend(methods, Element.Methods), + Object.extend(methods, Element.Methods.Simulated); + } + + // extend methods for specific tags + if (T[tagName]) Object.extend(methods, T[tagName]); + + for (var property in methods) { + var value = methods[property]; + if (typeof value == 'function' && !(property in element)) + element[property] = cache.findOrStore(value); + } + + element._extended = Prototype.emptyFunction; + return element; +}; + +Element.extend.cache = { + findOrStore: function(value) { + return this[value] = this[value] || function() { + return value.apply(null, [this].concat($A(arguments))); + } + } +}; + +Element.Methods = { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function(element) { + element = $(element); + Element[Element.visible(element) ? 'hide' : 'show'](element); + return element; + }, + + hide: function(element) { + $(element).style.display = 'none'; + return element; + }, + + show: function(element) { + $(element).style.display = ''; + return element; + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + }, + + update: function(element, html) { + html = typeof html == 'undefined' ? '' : html.toString(); + $(element).innerHTML = html.stripScripts(); + setTimeout(function() {html.evalScripts()}, 10); + return element; + }, + + replace: function(element, html) { + element = $(element); + html = typeof html == 'undefined' ? '' : html.toString(); + if (element.outerHTML) { + element.outerHTML = html.stripScripts(); + } else { + var range = element.ownerDocument.createRange(); + range.selectNodeContents(element); + element.parentNode.replaceChild( + range.createContextualFragment(html.stripScripts()), element); + } + setTimeout(function() {html.evalScripts()}, 10); + return element; + }, + + inspect: function(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + $H({'id': 'id', 'className': 'class'}).each(function(pair) { + var property = pair.first(), attribute = pair.last(); + var value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + }); + return result + '>'; + }, + + recursivelyCollect: function(element, property) { + element = $(element); + var elements = []; + while (element = element[property]) + if (element.nodeType == 1) + elements.push(Element.extend(element)); + return elements; + }, + + ancestors: function(element) { + return $(element).recursivelyCollect('parentNode'); + }, + + descendants: function(element) { + return $A($(element).getElementsByTagName('*')).each(Element.extend); + }, + + firstDescendant: function(element) { + element = $(element).firstChild; + while (element && element.nodeType != 1) element = element.nextSibling; + return $(element); + }, + + immediateDescendants: function(element) { + if (!(element = $(element).firstChild)) return []; + while (element && element.nodeType != 1) element = element.nextSibling; + if (element) return [element].concat($(element).nextSiblings()); + return []; + }, + + previousSiblings: function(element) { + return $(element).recursivelyCollect('previousSibling'); + }, + + nextSiblings: function(element) { + return $(element).recursivelyCollect('nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return element.previousSiblings().reverse().concat(element.nextSiblings()); + }, + + match: function(element, selector) { + if (typeof selector == 'string') + selector = new Selector(selector); + return selector.match($(element)); + }, + + up: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(element.parentNode); + var ancestors = element.ancestors(); + return expression ? Selector.findElement(ancestors, expression, index) : + ancestors[index || 0]; + }, + + down: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return element.firstDescendant(); + var descendants = element.descendants(); + return expression ? Selector.findElement(descendants, expression, index) : + descendants[index || 0]; + }, + + previous: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); + var previousSiblings = element.previousSiblings(); + return expression ? Selector.findElement(previousSiblings, expression, index) : + previousSiblings[index || 0]; + }, + + next: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); + var nextSiblings = element.nextSiblings(); + return expression ? Selector.findElement(nextSiblings, expression, index) : + nextSiblings[index || 0]; + }, + + getElementsBySelector: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element, args); + }, + + getElementsByClassName: function(element, className) { + return document.getElementsByClassName(className, element); + }, + + readAttribute: function(element, name) { + element = $(element); + if (Prototype.BrowserFeatures.IE) { + if (!element.attributes) return null; + var t = Element._attributeTranslations; + if (t.values[name]) return t.values[name](element, name); + if (t.names[name]) name = t.names[name]; + var attribute = element.attributes[name]; + return attribute ? attribute.nodeValue : null; + } + return element.getAttribute(name); + }, + + getHeight: function(element) { + return $(element).getDimensions().height; + }, + + getWidth: function(element) { + return $(element).getDimensions().width; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + var elementClassName = element.className; + if (elementClassName.length == 0) return false; + if (elementClassName == className || + elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) + return true; + return false; + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + Element.classNames(element).add(className); + return element; + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + Element.classNames(element).remove(className); + return element; + }, + + toggleClassName: function(element, className) { + if (!(element = $(element))) return; + Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className); + return element; + }, + + observe: function() { + Event.observe.apply(Event, arguments); + return $A(arguments).first(); + }, + + stopObserving: function() { + Event.stopObserving.apply(Event, arguments); + return $A(arguments).first(); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + var node = element.firstChild; + while (node) { + var nextNode = node.nextSibling; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + element.removeChild(node); + node = nextNode; + } + return element; + }, + + empty: function(element) { + return $(element).innerHTML.blank(); + }, + + descendantOf: function(element, ancestor) { + element = $(element), ancestor = $(ancestor); + while (element = element.parentNode) + if (element == ancestor) return true; + return false; + }, + + scrollTo: function(element) { + element = $(element); + var pos = Position.cumulativeOffset(element); + window.scrollTo(pos[0], pos[1]); + return element; + }, + + getStyle: function(element, style) { + element = $(element); + style = style == 'float' ? 'cssFloat' : style.camelize(); + var value = element.style[style]; + if (!value) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + if (style == 'opacity') return value ? parseFloat(value) : 1.0; + return value == 'auto' ? null : value; + }, + + getOpacity: function(element) { + return $(element).getStyle('opacity'); + }, + + setStyle: function(element, styles, camelized) { + element = $(element); + var elementStyle = element.style; + + for (var property in styles) + if (property == 'opacity') element.setOpacity(styles[property]) + else + elementStyle[(property == 'float' || property == 'cssFloat') ? + (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') : + (camelized ? property : property.camelize())] = styles[property]; + + return element; + }, + + setOpacity: function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + return element; + }, + + getDimensions: function(element) { + element = $(element); + var display = $(element).getStyle('display'); + if (display != 'none' && display != null) // Safari bug + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + var originalDisplay = els.display; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = 'block'; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = originalDisplay; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + return element; + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + return element; + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return element; + element._overflow = element.style.overflow || 'auto'; + if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') + element.style.overflow = 'hidden'; + return element; + }, + + undoClipping: function(element) { + element = $(element); + if (!element._overflow) return element; + element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; + element._overflow = null; + return element; + } +}; + +Object.extend(Element.Methods, { + childOf: Element.Methods.descendantOf, + childElements: Element.Methods.immediateDescendants +}); + +if (Prototype.BrowserFeatures.Opera) { + Element.Methods._getStyle = Element.Methods.getStyle; + Element.Methods.getStyle = function(element, style) { + switch(style) { + case 'left': + case 'top': + case 'right': + case 'bottom': + if (Element._getStyle(element, 'position') == 'static') return null; + default: return Element._getStyle(element, style); + } + }; +} +else if (Prototype.BrowserFeatures.IE) { + Element.Methods.getStyle = function(element, style) { + element = $(element); + style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); + var value = element.style[style]; + if (!value && element.currentStyle) value = element.currentStyle[style]; + + if (style == 'opacity') { + if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) + if (value[1]) return parseFloat(value[1]) / 100; + return 1.0; + } + + if (value == 'auto') { + if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) + return element['offset'+style.capitalize()] + 'px'; + return null; + } + return value; + }; + + Element.Methods.setOpacity = function(element, value) { + element = $(element); + var filter = element.getStyle('filter'), style = element.style; + if (value == 1 || value === '') { + style.filter = filter.replace(/alpha\([^\)]*\)/gi,''); + return element; + } else if (value < 0.00001) value = 0; + style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') + + 'alpha(opacity=' + (value * 100) + ')'; + return element; + }; + + // IE is missing .innerHTML support for TABLE-related elements + Element.Methods.update = function(element, html) { + element = $(element); + html = typeof html == 'undefined' ? '' : html.toString(); + var tagName = element.tagName.toUpperCase(); + if (['THEAD','TBODY','TR','TD'].include(tagName)) { + var div = document.createElement('div'); + switch (tagName) { + case 'THEAD': + case 'TBODY': + div.innerHTML = '' + html.stripScripts() + '
    '; + depth = 2; + break; + case 'TR': + div.innerHTML = '' + html.stripScripts() + '
    '; + depth = 3; + break; + case 'TD': + div.innerHTML = '
    ' + html.stripScripts() + '
    '; + depth = 4; + } + $A(element.childNodes).each(function(node) { element.removeChild(node) }); + depth.times(function() { div = div.firstChild }); + $A(div.childNodes).each(function(node) { element.appendChild(node) }); + } else { + element.innerHTML = html.stripScripts(); + } + setTimeout(function() { html.evalScripts() }, 10); + return element; + } +} +else if (Prototype.BrowserFeatures.Gecko) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1) ? 0.999999 : + (value === '') ? '' : (value < 0.00001) ? 0 : value; + return element; + }; +} + +Element._attributeTranslations = { + names: { + colspan: "colSpan", + rowspan: "rowSpan", + valign: "vAlign", + datetime: "dateTime", + accesskey: "accessKey", + tabindex: "tabIndex", + enctype: "encType", + maxlength: "maxLength", + readonly: "readOnly", + longdesc: "longDesc" + }, + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _flag: function(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + }, + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + var node = element.getAttributeNode('title'); + return node.specified ? node.nodeValue : null; + } + } +}; + +(function() { + Object.extend(this, { + href: this._getAttr, + src: this._getAttr, + disabled: this._flag, + checked: this._flag, + readonly: this._flag, + multiple: this._flag + }); +}).call(Element._attributeTranslations.values); + +Element.Methods.Simulated = { + hasAttribute: function(element, attribute) { + var t = Element._attributeTranslations, node; + attribute = t.names[attribute] || attribute; + node = $(element).getAttributeNode(attribute); + return node && node.specified; + } +}; + +Element.Methods.ByTag = {}; + +Object.extend(Element, Element.Methods); + +if (!Prototype.BrowserFeatures.ElementExtensions && + document.createElement('div').__proto__) { + window.HTMLElement = {}; + window.HTMLElement.prototype = document.createElement('div').__proto__; + Prototype.BrowserFeatures.ElementExtensions = true; +} + +Element.hasAttribute = function(element, attribute) { + if (element.hasAttribute) return element.hasAttribute(attribute); + return Element.Methods.Simulated.hasAttribute(element, attribute); +}; + +Element.addMethods = function(methods) { + var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; + if (arguments.length == 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) Object.extend(Element.Methods, methods || {}); + else { + if (tagName.constructor == Array) tagName.each(extend); + else extend(tagName); + } + + function extend(tagName) { + tagName = tagName.toUpperCase(); + if (!Element.Methods.ByTag[tagName]) + Element.Methods.ByTag[tagName] = {}; + Object.extend(Element.Methods.ByTag[tagName], methods); + } + + function copy(methods, destination, onlyIfAbsent) { + onlyIfAbsent = onlyIfAbsent || false; + var cache = Element.extend.cache; + for (var property in methods) { + var value = methods[property]; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = cache.findOrStore(value); + } + } + + function findDOMClass(tagName) { + var klass; + var trans = { + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": + "FrameSet", "IFRAME": "IFrame" + }; + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName.capitalize() + 'Element'; + if (window[klass]) return window[klass]; + + window[klass] = {}; + window[klass].prototype = document.createElement(tagName).__proto__; + return window[klass]; + } + + if (F.ElementExtensions) { + copy(Element.Methods, HTMLElement.prototype); + copy(Element.Methods.Simulated, HTMLElement.prototype, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (typeof klass == "undefined") continue; + copy(T[tag], klass.prototype); + } + } + + Object.extend(Element, Element.Methods); + delete Element.ByTag; +}; + +var Toggle = { display: Element.toggle }; + +/*--------------------------------------------------------------------------*/ + +Abstract.Insertion = function(adjacency) { + this.adjacency = adjacency; +} + +Abstract.Insertion.prototype = { + initialize: function(element, content) { + this.element = $(element); + this.content = content.stripScripts(); + + if (this.adjacency && this.element.insertAdjacentHTML) { + try { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } catch (e) { + var tagName = this.element.tagName.toUpperCase(); + if (['TBODY', 'TR'].include(tagName)) { + this.insertContent(this.contentFromAnonymousTable()); + } else { + throw e; + } + } + } else { + this.range = this.element.ownerDocument.createRange(); + if (this.initializeRange) this.initializeRange(); + this.insertContent([this.range.createContextualFragment(this.content)]); + } + + setTimeout(function() {content.evalScripts()}, 10); + }, + + contentFromAnonymousTable: function() { + var div = document.createElement('div'); + div.innerHTML = '' + this.content + '
    '; + return $A(div.childNodes[0].childNodes[0].childNodes); + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, this.element); + }).bind(this)); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function(fragments) { + fragments.reverse(false).each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.appendChild(fragment); + }).bind(this)); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, + this.element.nextSibling); + }).bind(this)); + } +}); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set($A(this).concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set($A(this).without(classNameToRemove).join(' ')); + }, + + toString: function() { + return $A(this).join(' '); + } +}; + +Object.extend(Element.ClassNames.prototype, Enumerable); +/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, + * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style + * license. Please see http://www.yui-ext.com/ for more information. */ + +var Selector = Class.create(); + +Selector.prototype = { + initialize: function(expression) { + this.expression = expression.strip(); + this.compileMatcher(); + }, + + compileMatcher: function() { + // Selectors with namespaced attributes can't use the XPath version + if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression)) + return this.compileXPathMatcher(); + + var e = this.expression, ps = Selector.patterns, h = Selector.handlers, + c = Selector.criteria, le, p, m; + + if (Selector._cache[e]) { + this.matcher = Selector._cache[e]; return; + } + this.matcher = ["this.matcher = function(root) {", + "var r = root, h = Selector.handlers, c = false, n;"]; + + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + this.matcher.push(typeof c[i] == 'function' ? c[i](m) : + new Template(c[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.matcher.push("return h.unique(n);\n}"); + eval(this.matcher.join('\n')); + Selector._cache[this.expression] = this.matcher; + }, + + compileXPathMatcher: function() { + var e = this.expression, ps = Selector.patterns, + x = Selector.xpath, le, m; + + if (Selector._cache[e]) { + this.xpath = Selector._cache[e]; return; + } + + this.matcher = ['.//*']; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + if (m = e.match(ps[i])) { + this.matcher.push(typeof x[i] == 'function' ? x[i](m) : + new Template(x[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.xpath = this.matcher.join(''); + Selector._cache[this.expression] = this.xpath; + }, + + findElements: function(root) { + root = root || document; + if (this.xpath) return document._getElementsByXPath(this.xpath, root); + return this.matcher(root); + }, + + match: function(element) { + return this.findElements(document).include(element); + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#"; + } +}; + +Object.extend(Selector, { + _cache: {}, + + xpath: { + descendant: "//*", + child: "/*", + adjacent: "/following-sibling::*[1]", + laterSibling: '/following-sibling::*', + tagName: function(m) { + if (m[1] == '*') return ''; + return "[local-name()='" + m[1].toLowerCase() + + "' or local-name()='" + m[1].toUpperCase() + "']"; + }, + className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", + id: "[@id='#{1}']", + attrPresence: "[@#{1}]", + attr: function(m) { + m[3] = m[5] || m[6]; + return new Template(Selector.xpath.operators[m[2]]).evaluate(m); + }, + pseudo: function(m) { + var h = Selector.xpath.pseudos[m[1]]; + if (!h) return ''; + if (typeof h === 'function') return h(m); + return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); + }, + operators: { + '=': "[@#{1}='#{3}']", + '!=': "[@#{1}!='#{3}']", + '^=': "[starts-with(@#{1}, '#{3}')]", + '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", + '*=': "[contains(@#{1}, '#{3}')]", + '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", + '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" + }, + pseudos: { + 'first-child': '[not(preceding-sibling::*)]', + 'last-child': '[not(following-sibling::*)]', + 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', + 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", + 'checked': "[@checked]", + 'disabled': "[@disabled]", + 'enabled': "[not(@disabled)]", + 'not': function(m) { + var e = m[6], p = Selector.patterns, + x = Selector.xpath, le, m, v; + + var exclusion = []; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in p) { + if (m = e.match(p[i])) { + v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m); + exclusion.push("(" + v.substring(1, v.length - 1) + ")"); + e = e.replace(m[0], ''); + break; + } + } + } + return "[not(" + exclusion.join(" and ") + ")]"; + }, + 'nth-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); + }, + 'nth-last-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); + }, + 'nth-of-type': function(m) { + return Selector.xpath.pseudos.nth("position() ", m); + }, + 'nth-last-of-type': function(m) { + return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); + }, + 'first-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); + }, + 'last-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); + }, + 'only-of-type': function(m) { + var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); + }, + nth: function(fragment, m) { + var mm, formula = m[6], predicate; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + if (mm = formula.match(/^(\d+)$/)) // digit only + return '[' + fragment + "= " + mm[1] + ']'; + if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (mm[1] == "-") mm[1] = -1; + var a = mm[1] ? Number(mm[1]) : 1; + var b = mm[2] ? Number(mm[2]) : 0; + predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + + "((#{fragment} - #{b}) div #{a} >= 0)]"; + return new Template(predicate).evaluate({ + fragment: fragment, a: a, b: b }); + } + } + } + }, + + criteria: { + tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', + className: 'n = h.className(n, r, "#{1}", c); c = false;', + id: 'n = h.id(n, r, "#{1}", c); c = false;', + attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;', + attr: function(m) { + m[3] = (m[5] || m[6]); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m); + }, + pseudo: function(m) { + if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); + return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); + }, + descendant: 'c = "descendant";', + child: 'c = "child";', + adjacent: 'c = "adjacent";', + laterSibling: 'c = "laterSibling";' + }, + + patterns: { + // combinators must be listed first + // (and descendant needs to be last combinator) + laterSibling: /^\s*~\s*/, + child: /^\s*>\s*/, + adjacent: /^\s*\+\s*/, + descendant: /^\s/, + + // selectors follow + tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, + id: /^#([\w\-\*]+)(\b|$)/, + className: /^\.([\w\-\*]+)(\b|$)/, + pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s)/, + attrPresence: /^\[([\w]+)\]/, + attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/ + }, + + handlers: { + // UTILITY FUNCTIONS + // joins two collections + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + a.push(node); + return a; + }, + + // marks an array of nodes for counting + mark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._counted = true; + return nodes; + }, + + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._counted = undefined; + return nodes; + }, + + // mark each child node with its position (for nth calls) + // "ofType" flag indicates whether we're indexing for nth-of-type + // rather than nth-child + index: function(parentNode, reverse, ofType) { + parentNode._counted = true; + if (reverse) { + for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { + node = nodes[i]; + if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; + } + } else { + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) + if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; + } + }, + + // filters out duplicates and extends all nodes + unique: function(nodes) { + if (nodes.length == 0) return nodes; + var results = [], n; + for (var i = 0, l = nodes.length; i < l; i++) + if (!(n = nodes[i])._counted) { + n._counted = true; + results.push(Element.extend(n)); + } + return Selector.handlers.unmark(results); + }, + + // COMBINATOR FUNCTIONS + descendant: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName('*')); + return results; + }, + + child: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) { + for (var j = 0, children = [], child; child = node.childNodes[j]; j++) + if (child.nodeType == 1 && child.tagName != '!') results.push(child); + } + return results; + }, + + adjacent: function(nodes) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + var next = this.nextElementSibling(node); + if (next) results.push(next); + } + return results; + }, + + laterSibling: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, Element.nextSiblings(node)); + return results; + }, + + nextElementSibling: function(node) { + while (node = node.nextSibling) + if (node.nodeType == 1) return node; + return null; + }, + + previousElementSibling: function(node) { + while (node = node.previousSibling) + if (node.nodeType == 1) return node; + return null; + }, + + // TOKEN FUNCTIONS + tagName: function(nodes, root, tagName, combinator) { + tagName = tagName.toUpperCase(); + var results = [], h = Selector.handlers; + if (nodes) { + if (combinator) { + // fastlane for ordinary descendant combinators + if (combinator == "descendant") { + for (var i = 0, node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName(tagName)); + return results; + } else nodes = this[combinator](nodes); + if (tagName == "*") return nodes; + } + for (var i = 0, node; node = nodes[i]; i++) + if (node.tagName.toUpperCase() == tagName) results.push(node); + return results; + } else return root.getElementsByTagName(tagName); + }, + + id: function(nodes, root, id, combinator) { + var targetNode = $(id), h = Selector.handlers; + if (!nodes && root == document) return targetNode ? [targetNode] : []; + if (nodes) { + if (combinator) { + if (combinator == 'child') { + for (var i = 0, node; node = nodes[i]; i++) + if (targetNode.parentNode == node) return [targetNode]; + } else if (combinator == 'descendant') { + for (var i = 0, node; node = nodes[i]; i++) + if (Element.descendantOf(targetNode, node)) return [targetNode]; + } else if (combinator == 'adjacent') { + for (var i = 0, node; node = nodes[i]; i++) + if (Selector.handlers.previousElementSibling(targetNode) == node) + return [targetNode]; + } else nodes = h[combinator](nodes); + } + for (var i = 0, node; node = nodes[i]; i++) + if (node == targetNode) return [targetNode]; + return []; + } + return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; + }, + + className: function(nodes, root, className, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + return Selector.handlers.byClassName(nodes, root, className); + }, + + byClassName: function(nodes, root, className) { + if (!nodes) nodes = Selector.handlers.descendant([root]); + var needle = ' ' + className + ' '; + for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { + nodeClassName = node.className; + if (nodeClassName.length == 0) continue; + if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) + results.push(node); + } + return results; + }, + + attrPresence: function(nodes, root, attr) { + var results = []; + for (var i = 0, node; node = nodes[i]; i++) + if (Element.hasAttribute(node, attr)) results.push(node); + return results; + }, + + attr: function(nodes, root, attr, value, operator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + var handler = Selector.operators[operator], results = []; + for (var i = 0, node; node = nodes[i]; i++) { + var nodeValue = Element.readAttribute(node, attr); + if (nodeValue === null) continue; + if (handler(nodeValue, value)) results.push(node); + } + return results; + }, + + pseudo: function(nodes, name, value, root, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + if (!nodes) nodes = root.getElementsByTagName("*"); + return Selector.pseudos[name](nodes, value, root); + } + }, + + pseudos: { + 'first-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.previousElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'last-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.nextElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'only-child': function(nodes, value, root) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) + results.push(node); + return results; + }, + 'nth-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root); + }, + 'nth-last-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true); + }, + 'nth-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, false, true); + }, + 'nth-last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true, true); + }, + 'first-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, false, true); + }, + 'last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, true, true); + }, + 'only-of-type': function(nodes, formula, root) { + var p = Selector.pseudos; + return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); + }, + + // handles the an+b logic + getIndices: function(a, b, total) { + if (a == 0) return b > 0 ? [b] : []; + return $R(1, total).inject([], function(memo, i) { + if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); + return memo; + }); + }, + + // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type + nth: function(nodes, formula, root, reverse, ofType) { + if (nodes.length == 0) return []; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + var h = Selector.handlers, results = [], indexed = [], m; + h.mark(nodes); + for (var i = 0, node; node = nodes[i]; i++) { + if (!node.parentNode._counted) { + h.index(node.parentNode, reverse, ofType); + indexed.push(node.parentNode); + } + } + if (formula.match(/^\d+$/)) { // just a number + formula = Number(formula); + for (var i = 0, node; node = nodes[i]; i++) + if (node.nodeIndex == formula) results.push(node); + } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (m[1] == "-") m[1] = -1; + var a = m[1] ? Number(m[1]) : 1; + var b = m[2] ? Number(m[2]) : 0; + var indices = Selector.pseudos.getIndices(a, b, nodes.length); + for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { + for (var j = 0; j < l; j++) + if (node.nodeIndex == indices[j]) results.push(node); + } + } + h.unmark(nodes); + h.unmark(indexed); + return results; + }, + + 'empty': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + // IE treats comments as element nodes + if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; + results.push(node); + } + return results; + }, + + 'not': function(nodes, selector, root) { + var h = Selector.handlers, selectorType, m; + var exclusions = new Selector(selector).findElements(root); + h.mark(exclusions); + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node._counted) results.push(node); + h.unmark(exclusions); + return results; + }, + + 'enabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node.disabled) results.push(node); + return results; + }, + + 'disabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.disabled) results.push(node); + return results; + }, + + 'checked': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.checked) results.push(node); + return results; + } + }, + + operators: { + '=': function(nv, v) { return nv == v; }, + '!=': function(nv, v) { return nv != v; }, + '^=': function(nv, v) { return nv.startsWith(v); }, + '$=': function(nv, v) { return nv.endsWith(v); }, + '*=': function(nv, v) { return nv.include(v); }, + '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, + '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } + }, + + matchElements: function(elements, expression) { + var matches = new Selector(expression).findElements(), h = Selector.handlers; + h.mark(matches); + for (var i = 0, results = [], element; element = elements[i]; i++) + if (element._counted) results.push(element); + h.unmark(matches); + return results; + }, + + findElement: function(elements, expression, index) { + if (typeof expression == 'number') { + index = expression; expression = false; + } + return Selector.matchElements(elements, expression || '*')[index || 0]; + }, + + findChildElements: function(element, expressions) { + var exprs = expressions.join(','), expressions = []; + exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + var results = [], h = Selector.handlers; + for (var i = 0, l = expressions.length, selector; i < l; i++) { + selector = new Selector(expressions[i].strip()); + h.concat(results, selector.findElements(element)); + } + return (l > 1) ? h.unique(results) : results; + } +}); + +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} +var Form = { + reset: function(form) { + $(form).reset(); + return form; + }, + + serializeElements: function(elements, getHash) { + var data = elements.inject({}, function(result, element) { + if (!element.disabled && element.name) { + var key = element.name, value = $(element).getValue(); + if (value != null) { + if (key in result) { + if (result[key].constructor != Array) result[key] = [result[key]]; + result[key].push(value); + } + else result[key] = value; + } + } + return result; + }); + + return getHash ? data : Hash.toQueryString(data); + } +}; + +Form.Methods = { + serialize: function(form, getHash) { + return Form.serializeElements(Form.getElements(form), getHash); + }, + + getElements: function(form) { + return $A($(form).getElementsByTagName('*')).inject([], + function(elements, child) { + if (Form.Element.Serializers[child.tagName.toLowerCase()]) + elements.push(Element.extend(child)); + return elements; + } + ); + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) return $A(inputs).map(Element.extend); + + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || (name && input.name != name)) + continue; + matchingInputs.push(Element.extend(input)); + } + + return matchingInputs; + }, + + disable: function(form) { + form = $(form); + Form.getElements(form).invoke('disable'); + return form; + }, + + enable: function(form) { + form = $(form); + Form.getElements(form).invoke('enable'); + return form; + }, + + findFirstElement: function(form) { + return $(form).getElements().find(function(element) { + return element.type != 'hidden' && !element.disabled && + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + form.findFirstElement().activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || {}); + + var params = options.parameters; + options.parameters = form.serialize(true); + + if (params) { + if (typeof params == 'string') params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(form.readAttribute('action'), options); + } +} + +Object.extend(Form, Form.Methods); + +/*--------------------------------------------------------------------------*/ + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; + }, + + select: function(element) { + $(element).select(); + return element; + } +} + +Form.Element.Methods = { + serialize: function(element) { + element = $(element); + if (!element.disabled && element.name) { + var value = element.getValue(); + if (value != undefined) { + var pair = {}; + pair[element.name] = value; + return Hash.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + try { + element.focus(); + if (element.select && (element.tagName.toLowerCase() != 'input' || + !['button', 'reset', 'submit'].include(element.type))) + element.select(); + } catch (e) {} + return element; + }, + + disable: function(element) { + element = $(element); + element.blur(); + element.disabled = true; + return element; + }, + + enable: function(element) { + element = $(element); + element.disabled = false; + return element; + } +} + +Object.extend(Form.Element, Form.Element.Methods); +Object.extend(Element.Methods.ByTag, { + "FORM": Object.clone(Form.Methods), + "INPUT": Object.clone(Form.Element.Methods), + "SELECT": Object.clone(Form.Element.Methods), + "TEXTAREA": Object.clone(Form.Element.Methods) +}); + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; +var $F = Form.Element.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + default: + return Form.Element.Serializers.textarea(element); + } + }, + + inputSelector: function(element) { + return element.checked ? element.value : null; + }, + + textarea: function(element) { + return element.value; + }, + + select: function(element) { + return this[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + }, + + selectOne: function(element) { + var index = element.selectedIndex; + return index >= 0 ? this.optionValue(element.options[index]) : null; + }, + + selectMany: function(element) { + var values, length = element.length; + if (!length) return null; + + for (var i = 0, values = []; i < length; i++) { + var opt = element.options[i]; + if (opt.selected) values.push(this.optionValue(opt)); + } + return values; + }, + + optionValue: function(opt) { + // extend element because hasAttribute may not be native + return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; + } +} + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + var changed = ('string' == typeof this.lastValue && 'string' == typeof value + ? this.lastValue != value : String(this.lastValue) != String(value)); + if (changed) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + Form.getElements(this.element).each(this.registerCallback.bind(this)); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + default: + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, + + element: function(event) { + return $(event.target || event.srcElement); + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + event.cancelBubble = true; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0, length = Event.observers.length; i < length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (Prototype.BrowserFeatures.WebKit || element.attachEvent)) + name = 'keydown'; + + Event._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (Prototype.BrowserFeatures.WebKit || element.attachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + try { + element.detachEvent('on' + name, observer); + } catch (e) {} + } + } +}); + +/* prevent memory leaks in IE */ +if (Prototype.BrowserFeatures.IE) + Event.observe(window, 'unload', Event.unloadCache, false); +var Position = { + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if(element.tagName=='BODY') break; + var p = Element.getStyle(element, 'position'); + if (p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; + }, + + offsetParent: function(element) { + if (element.offsetParent) return element.offsetParent; + if (element == document.body) return element; + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return element; + + return document.body; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + page: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent == document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + if (!window.opera || element.tagName=='BODY') { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + + return [valueL, valueT]; + }, + + clone: function(source, target) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || {}) + + // find page position of source + source = $(source); + var p = Position.page(source); + + // find coordinate system to use + target = $(target); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(target,'position') == 'absolute') { + parent = Position.offsetParent(target); + delta = Position.page(parent); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if(options.setWidth) target.style.width = source.offsetWidth + 'px'; + if(options.setHeight) target.style.height = source.offsetHeight + 'px'; + }, + + absolutize: function(element) { + element = $(element); + if (element.style.position == 'absolute') return; + Position.prepare(); + + var offsets = Position.positionedOffset(element); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.width = width + 'px'; + element.style.height = height + 'px'; + }, + + relativize: function(element) { + element = $(element); + if (element.style.position == 'relative') return; + Position.prepare(); + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + } +} + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned. For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (Prototype.BrowserFeatures.WebKit) { + Position.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return [valueL, valueT]; + } +} + +Element.addMethods(); \ No newline at end of file diff --git a/xCAT-web/javascripts/tooltip.js b/xCAT-web/javascripts/tooltip.js new file mode 100644 index 000000000..65e610603 --- /dev/null +++ b/xCAT-web/javascripts/tooltip.js @@ -0,0 +1,241 @@ +// Singleton class TooltipWindow +// This class works with special className. The tooltip content could be in your HTML page as an hidden element or +// can be retreive by an AJAX call. +// +// To work, You just need to set two class name on elements that should show tooltips +// - One to say to TooltipManager that this element must have a tooltip ('tooltip' by default) +// - Another to indicate how to find the tooltip content +// It could be html_XXXX if tootltip content is somewhere hidden in your page, XXX must be DOM ID of this hidden element +// It could be ajax_XXXX if tootltip content must be find by an ajax request, XXX will be the string send as id parameter to your server. +// Check samples/tooltips/tooltip.html to see how it works +// +TooltipManager = { + options: {cssClassName: 'tooltip', delayOver: 200, delayOut: 1000, shiftX: 10, shiftY: 10, + className: 'alphacube', width: 200, height: null, + draggable: false, minimizable: false, maximizable: false, showEffect: Element.show, hideEffect: Element.hide}, + ajaxInfo: null, + elements: null, + showTimer: null, + hideTimer: null, + + // Init tooltip manager + // parameters: + // - cssClassName (string) : CSS class name where tooltip should be shown. + // - ajaxOptions (hash) : Ajax options for ajax tooltip. + // For examples {url: "/tooltip/get.php", options: {method: 'get'}} + // see Ajax.Request documentation for details + //- tooltipOptions (hash) : available keys + // - delayOver: int in ms (default 10) delay before showing tooltip + // - delayOut: int in ms (default 1000) delay before hidding tooltip + // - shiftX: int in pixels (default 10) left shift of the tooltip window + // - shiftY: int in pixels (default 10) top shift of the tooltip window + // and All window options like showEffect: Element.show, hideEffect: Element.hide to remove animation + // default: {className: 'alphacube', width: 200, height: null, draggable: false, minimizable: false, maximizable: false} + + init: function(cssClassName, ajaxInfo, tooltipOptions) { + TooltipManager.options = Object.extend(TooltipManager.options, tooltipOptions || {}); + + cssClassName = TooltipManager.options.cssClassName || "tooltip"; + TooltipManager.ajaxInfo = ajaxInfo; + TooltipManager.elements = $$("." + cssClassName); + TooltipManager.elements.each(function(element) { + element = $(element) + var info = TooltipManager._getInfo(element); + if (info.ajax) { + element.ajaxId = info.id; + element.ajaxInfo = ajaxInfo; + } + else { + element.tooltipElement = $(info.id); + } + element.observe("mouseover", TooltipManager._mouseOver); + element.observe("mouseout", TooltipManager._mouseOut); + }); + Windows.addObserver(this); + }, + + addHTML: function(element, tooltipElement) { + element = $(element); + tooltipElement = $(tooltipElement); + element.tooltipElement = tooltipElement; + + element.observe("mouseover", TooltipManager._mouseOver); + element.observe("mouseout", TooltipManager._mouseOut); + }, + + addAjax: function(element, ajaxInfo) { + element = $(element); + element.ajaxInfo = ajaxInfo; + element.observe("mouseover", TooltipManager._mouseOver); + element.observe("mouseout", TooltipManager._mouseOut); + }, + + addURL: function(element, url, width, height) { + element = $(element); + element.url = url; + element.frameWidth = width; + element.frameHeight = height; + element.observe("mouseover", TooltipManager._mouseOver); + element.observe("mouseout", TooltipManager._mouseOut); + }, + + close: function() { + if (TooltipManager.tooltipWindow) + TooltipManager.tooltipWindow.hide(); + }, + + preloadImages: function(path, images, extension) { + if (!extension) + extension = ".gif"; + + //preload images + $A(images).each(function(i) { + var image = new Image(); + image.src= path + "/" + i + extension; + }); + }, + + _showTooltip: function(element) { + if (this.element == element) + return; + // Get original element + while (element && (!element.tooltipElement && !element.ajaxInfo && !element.url)) + element = element.parentNode; + this.element = element; + + TooltipManager.showTimer = null; + if (TooltipManager.hideTimer) + clearTimeout(TooltipManager.hideTimer); + + var position = Position.cumulativeOffset(element); + var dimension = element.getDimensions(); + + if (! this.tooltipWindow) + this.tooltipWindow = new Window("__tooltip__", TooltipManager.options); + + this.tooltipWindow.hide(); + this.tooltipWindow.setLocation(position[1] + dimension.height + TooltipManager.options.shiftY, position[0] + TooltipManager.options.shiftX); + + Event.observe(this.tooltipWindow.element, "mouseover", function(event) {TooltipManager._tooltipOver(event, element)}); + Event.observe(this.tooltipWindow.element, "mouseout", function(event) {TooltipManager._tooltipOut(event, element)}); + + // Reset width/height for computation + this.tooltipWindow.height = TooltipManager.options.height; + this.tooltipWindow.width = TooltipManager.options.width; + + // Ajax content + if (element.ajaxInfo) { + var p = element.ajaxInfo.options.parameters; + var saveParam = p; + + // Set by CSS + if (element.ajaxId) { + if (p) + p += "&id=" + element.ajaxId; + else + p = "id=" + element.ajaxId; + } + element.ajaxInfo.options.parameters = p || ""; + this.tooltipWindow.setHTMLContent(""); + this.tooltipWindow.setAjaxContent(element.ajaxInfo.url, element.ajaxInfo.options); + element.ajaxInfo.options.parameters = saveParam; + } + // URL content + else if (element.url) { + this.tooltipWindow.setURL(element.url); + this.tooltipWindow.setSize(element.frameWidth, element.frameHeight); + + // Set tooltip size + this.tooltipWindow.height = element.frameHeight; + this.tooltipWindow.width = element.frameWidth; + } + // HTML content + else + this.tooltipWindow.setHTMLContent(element.tooltipElement.innerHTML); + + if (!element.ajaxInfo) { + this.tooltipWindow.show(); + this.tooltipWindow.toFront(); + } + }, + + _hideTooltip: function(element) { + if (this.tooltipWindow) { + this.tooltipWindow.hide(); + this.element = null; + } + }, + + _mouseOver: function (event) { + var element = Event.element(event); + if (TooltipManager.showTimer) + clearTimeout(TooltipManager.showTimer); + + TooltipManager.showTimer = setTimeout(function() {TooltipManager._showTooltip(element)}, TooltipManager.options.delayOver) + }, + + _mouseOut: function(event) { + var element = Event.element(event); + if (TooltipManager.showTimer) { + clearTimeout(TooltipManager.showTimer); + TooltipManager.showTimer = null; + return; + } + if (TooltipManager.tooltipWindow) + TooltipManager.hideTimer = setTimeout(function() {TooltipManager._hideTooltip(element)}, TooltipManager.options.delayOut) + }, + + _tooltipOver: function(event, element) { + if (TooltipManager.hideTimer) { + clearTimeout(TooltipManager.hideTimer); + TooltipManager.hideTimer = null; + } + }, + + _tooltipOut: function(event, element) { + if (TooltipManager.hideTimer == null) + TooltipManager.hideTimer = setTimeout(function() {TooltipManager._hideTooltip(element)}, TooltipManager.options.delayOut) + }, + + _getInfo: function(element) { + // Find html_ for static content + var id = element.className.split(' ').detect(function(name) {return name.indexOf("html_") == 0}); + var ajax = true; + if (id) + ajax = false; + else + // Find ajax_ for ajax content + id = element.className.split(' ').detect(function(name) {return name.indexOf("ajax_") == 0}); + + id = id.substr(id.indexOf('_')+1, id.length) + return id ? {ajax: ajax, id: id} : null; + }, + + onBeforeShow: function(eventName, win) { + var top = parseFloat(win.getLocation().top); + var dim = win.element.getDimensions(); + + if (top + dim.height > TooltipManager._getScrollTop() + TooltipManager._getPageHeight()) { + var position = Position.cumulativeOffset(this.element); + + var top = position[1] - TooltipManager.options.shiftY - dim.height; + win.setLocation(top, position[0] + TooltipManager.options.shiftX) + } + }, + + _getPageWidth: function(){ + return window.innerWidth || document.documentElement.clientWidth || 0; + }, + + _getPageHeight: function(){ + return window.innerHeight || document.documentElement.clientHeight || 0; + }, + + _getScrollTop: function(){ + return document.documentElement.scrollTop || window.pageYOffset || 0; + }, + + _getScrollLeft: function(){ + return document.documentElement.scrollLeft || window.pageXOffset || 0; + } +}; diff --git a/xCAT-web/javascripts/window.js b/xCAT-web/javascripts/window.js new file mode 100644 index 000000000..76d37933c --- /dev/null +++ b/xCAT-web/javascripts/window.js @@ -0,0 +1,1843 @@ +// Copyright (c) 2006 Sébastien Gruhier (http://xilinus.com, http://itseb.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// VERSION 1.3 + +var Window = Class.create(); + +Window.keepMultiModalWindow = false; +Window.hasEffectLib = (typeof Effect != 'undefined'); +Window.resizeEffectDuration = 0.4; + +Window.prototype = { + // Constructor + // Available parameters : className, blurClassName, title, minWidth, minHeight, maxWidth, maxHeight, width, height, top, left, bottom, right, resizable, zIndex, opacity, recenterAuto, wiredDrag + // hideEffect, showEffect, showEffectOptions, hideEffectOptions, effectOptions, url, draggable, closable, minimizable, maximizable, parent, onload + // add all callbacks (if you do not use an observer) + // onDestroy onStartResize onStartMove onResize onMove onEndResize onEndMove onFocus onBlur onBeforeShow onShow onHide onMinimize onMaximize onClose + + initialize: function() { + var id; + var optionIndex = 0; + // For backward compatibility like win= new Window("id", {...}) instead of win = new Window({id: "id", ...}) + if (arguments.length > 0) { + if (typeof arguments[0] == "string" ) { + id = arguments[0]; + optionIndex = 1; + } + else + id = arguments[0] ? arguments[0].id : null; + } + + // Generate unique ID if not specified + if (!id) + id = "window_" + new Date().getTime(); + + if ($(id)) + alert("Window " + id + " is already registered in the DOM! Make sure you use setDestroyOnClose() or destroyOnClose: true in the constructor"); + + this.options = Object.extend({ + className: "dialog", + blurClassName: null, + minWidth: 100, + minHeight: 20, + resizable: true, + closable: true, + minimizable: true, + maximizable: true, + draggable: true, + userData: null, + showEffect: (Window.hasEffectLib ? Effect.Appear : Element.show), + hideEffect: (Window.hasEffectLib ? Effect.Fade : Element.hide), + showEffectOptions: {}, + hideEffectOptions: {}, + effectOptions: null, + parent: document.body, + title: " ", + url: null, + onload: Prototype.emptyFunction, + width: 200, + height: 300, + opacity: 1, + recenterAuto: true, + wiredDrag: false, + closeCallback: null, + destroyOnClose: false, + gridX: 1, + gridY: 1 + }, arguments[optionIndex] || {}); + if (this.options.blurClassName) + this.options.focusClassName = this.options.className; + + if (typeof this.options.top == "undefined" && typeof this.options.bottom == "undefined") + this.options.top = this._round(Math.random()*500, this.options.gridY); + if (typeof this.options.left == "undefined" && typeof this.options.right == "undefined") + this.options.left = this._round(Math.random()*500, this.options.gridX); + + if (this.options.effectOptions) { + Object.extend(this.options.hideEffectOptions, this.options.effectOptions); + Object.extend(this.options.showEffectOptions, this.options.effectOptions); + if (this.options.showEffect == Element.Appear) + this.options.showEffectOptions.to = this.options.opacity; + } + if (Window.hasEffectLib) { + if (this.options.showEffect == Effect.Appear) + this.options.showEffectOptions.to = this.options.opacity; + + if (this.options.hideEffect == Effect.Fade) + this.options.hideEffectOptions.from = this.options.opacity; + } + if (this.options.hideEffect == Element.hide) + this.options.hideEffect = function(){ Element.hide(this.element); if (this.options.destroyOnClose) this.destroy(); }.bind(this) + + if (this.options.parent != document.body) + this.options.parent = $(this.options.parent); + + this.element = this._createWindow(id); + this.element.win = this; + + // Bind event listener + this.eventMouseDown = this._initDrag.bindAsEventListener(this); + this.eventMouseUp = this._endDrag.bindAsEventListener(this); + this.eventMouseMove = this._updateDrag.bindAsEventListener(this); + this.eventOnLoad = this._getWindowBorderSize.bindAsEventListener(this); + this.eventMouseDownContent = this.toFront.bindAsEventListener(this); + this.eventResize = this._recenter.bindAsEventListener(this); + + this.topbar = $(this.element.id + "_top"); + this.bottombar = $(this.element.id + "_bottom"); + this.content = $(this.element.id + "_content"); + + Event.observe(this.topbar, "mousedown", this.eventMouseDown); + Event.observe(this.bottombar, "mousedown", this.eventMouseDown); + Event.observe(this.content, "mousedown", this.eventMouseDownContent); + Event.observe(window, "load", this.eventOnLoad); + Event.observe(window, "resize", this.eventResize); + Event.observe(window, "scroll", this.eventResize); + Event.observe(this.options.parent, "scroll", this.eventResize); + + if (this.options.draggable) { + var that = this; + [this.topbar, this.topbar.up().previous(), this.topbar.up().next()].each(function(element) { + element.observe("mousedown", that.eventMouseDown); + element.addClassName("top_draggable"); + }); + [this.bottombar.up(), this.bottombar.up().previous(), this.bottombar.up().next()].each(function(element) { + element.observe("mousedown", that.eventMouseDown); + element.addClassName("bottom_draggable"); + }); + + } + + if (this.options.resizable) { + this.sizer = $(this.element.id + "_sizer"); + Event.observe(this.sizer, "mousedown", this.eventMouseDown); + } + + this.useLeft = null; + this.useTop = null; + if (typeof this.options.left != "undefined") { + this.element.setStyle({left: parseFloat(this.options.left) + 'px'}); + this.useLeft = true; + } + else { + this.element.setStyle({right: parseFloat(this.options.right) + 'px'}); + this.useLeft = false; + } + + if (typeof this.options.top != "undefined") { + this.element.setStyle({top: parseFloat(this.options.top) + 'px'}); + this.useTop = true; + } + else { + this.element.setStyle({bottom: parseFloat(this.options.bottom) + 'px'}); + this.useTop = false; + } + + this.storedLocation = null; + + this.setOpacity(this.options.opacity); + if (this.options.zIndex) + this.setZIndex(this.options.zIndex) + + if (this.options.destroyOnClose) + this.setDestroyOnClose(true); + + this._getWindowBorderSize(); + this.width = this.options.width; + this.height = this.options.height; + this.visible = false; + + this.constraint = false; + this.constraintPad = {top: 0, left:0, bottom:0, right:0}; + + if (this.width && this.height) + this.setSize(this.options.width, this.options.height); + this.setTitle(this.options.title) + Windows.register(this); + }, + + // Destructor + destroy: function() { + this._notify("onDestroy"); + Event.stopObserving(this.topbar, "mousedown", this.eventMouseDown); + Event.stopObserving(this.bottombar, "mousedown", this.eventMouseDown); + Event.stopObserving(this.content, "mousedown", this.eventMouseDownContent); + + Event.stopObserving(window, "load", this.eventOnLoad); + Event.stopObserving(window, "resize", this.eventResize); + Event.stopObserving(window, "scroll", this.eventResize); + + Event.stopObserving(this.content, "load", this.options.onload); + + if (this._oldParent) { + var content = this.getContent(); + var originalContent = null; + for(var i = 0; i < content.childNodes.length; i++) { + originalContent = content.childNodes[i]; + if (originalContent.nodeType == 1) + break; + originalContent = null; + } + if (originalContent) + this._oldParent.appendChild(originalContent); + this._oldParent = null; + } + + if (this.sizer) + Event.stopObserving(this.sizer, "mousedown", this.eventMouseDown); + + if (this.options.url) + this.content.src = null + + if(this.iefix) + Element.remove(this.iefix); + + Element.remove(this.element); + Windows.unregister(this); + }, + + // Sets close callback, if it sets, it should return true to be able to close the window. + setCloseCallback: function(callback) { + this.options.closeCallback = callback; + }, + + // Gets window content + getContent: function () { + return this.content; + }, + + // Sets the content with an element id + setContent: function(id, autoresize, autoposition) { + var element = $(id); + if (null == element) throw "Unable to find element '" + id + "' in DOM"; + this._oldParent = element.parentNode; + + var d = null; + var p = null; + + if (autoresize) + d = Element.getDimensions(element); + if (autoposition) + p = Position.cumulativeOffset(element); + + var content = this.getContent(); + // Clear HTML (and even iframe) + this.setHTMLContent(""); + content = this.getContent(); + + content.appendChild(element); + element.show(); + if (autoresize) + this.setSize(d.width, d.height); + if (autoposition) + this.setLocation(p[1] - this.heightN, p[0] - this.widthW); + }, + + setHTMLContent: function(html) { + // It was an url (iframe), recreate a div content instead of iframe content + if (this.options.url) { + this.content.src = null; + this.options.url = null; + + var content ="
    "; + $(this.getId() +"_table_content").innerHTML = content; + + this.content = $(this.element.id + "_content"); + } + + this.getContent().innerHTML = html; + }, + + setAjaxContent: function(url, options, showCentered, showModal) { + this.showFunction = showCentered ? "showCenter" : "show"; + this.showModal = showModal || false; + + options = options || {}; + + // Clear HTML (and even iframe) + this.setHTMLContent(""); + + this.onComplete = options.onComplete; + if (! this._onCompleteHandler) + this._onCompleteHandler = this._setAjaxContent.bind(this); + options.onComplete = this._onCompleteHandler; + + new Ajax.Request(url, options); + options.onComplete = this.onComplete; + }, + + _setAjaxContent: function(originalRequest) { + Element.update(this.getContent(), originalRequest.responseText); + if (this.onComplete) + this.onComplete(originalRequest); + this.onComplete = null; + this[this.showFunction](this.showModal) + }, + + setURL: function(url) { + // Not an url content, change div to iframe + if (this.options.url) + this.content.src = null; + this.options.url = url; + var content= ""; + $(this.getId() +"_table_content").innerHTML = content; + + this.content = $(this.element.id + "_content"); + }, + + getURL: function() { + return this.options.url ? this.options.url : null; + }, + + refresh: function() { + if (this.options.url) + $(this.element.getAttribute('id') + '_content').src = this.options.url; + }, + + // Stores position/size in a cookie, by default named with window id + setCookie: function(name, expires, path, domain, secure) { + name = name || this.element.id; + this.cookie = [name, expires, path, domain, secure]; + + // Get cookie + var value = WindowUtilities.getCookie(name) + // If exists + if (value) { + var values = value.split(','); + var x = values[0].split(':'); + var y = values[1].split(':'); + + var w = parseFloat(values[2]), h = parseFloat(values[3]); + var mini = values[4]; + var maxi = values[5]; + + this.setSize(w, h); + if (mini == "true") + this.doMinimize = true; // Minimize will be done at onload window event + else if (maxi == "true") + this.doMaximize = true; // Maximize will be done at onload window event + + this.useLeft = x[0] == "l"; + this.useTop = y[0] == "t"; + + this.element.setStyle(this.useLeft ? {left: x[1]} : {right: x[1]}); + this.element.setStyle(this.useTop ? {top: y[1]} : {bottom: y[1]}); + } + }, + + // Gets window ID + getId: function() { + return this.element.id; + }, + + // Detroys itself when closing + setDestroyOnClose: function() { + this.options.destroyOnClose = true; + }, + + setConstraint: function(bool, padding) { + this.constraint = bool; + this.constraintPad = Object.extend(this.constraintPad, padding || {}); + // Reset location to apply constraint + if (this.useTop && this.useLeft) + this.setLocation(parseFloat(this.element.style.top), parseFloat(this.element.style.left)); + }, + + // initDrag event + + _initDrag: function(event) { + // No resize on minimized window + if (Event.element(event) == this.sizer && this.isMinimized()) + return; + + // No move on maximzed window + if (Event.element(event) != this.sizer && this.isMaximized()) + return; + + if (Prototype.BrowserFeatures.IE && this.heightN == 0) + this._getWindowBorderSize(); + + // Get pointer X,Y + this.pointer = [this._round(Event.pointerX(event), this.options.gridX), this._round(Event.pointerY(event), this.options.gridY)]; + if (this.options.wiredDrag) + this.currentDrag = this._createWiredElement(); + else + this.currentDrag = this.element; + + // Resize + if (Event.element(event) == this.sizer) { + this.doResize = true; + this.widthOrg = this.width; + this.heightOrg = this.height; + this.bottomOrg = parseFloat(this.element.getStyle('bottom')); + this.rightOrg = parseFloat(this.element.getStyle('right')); + this._notify("onStartResize"); + } + else { + this.doResize = false; + + // Check if click on close button, + var closeButton = $(this.getId() + '_close'); + if (closeButton && Position.within(closeButton, this.pointer[0], this.pointer[1])) { + this.currentDrag = null; + return; + } + + this.toFront(); + + if (! this.options.draggable) + return; + this._notify("onStartMove"); + } + // Register global event to capture mouseUp and mouseMove + Event.observe(document, "mouseup", this.eventMouseUp, false); + Event.observe(document, "mousemove", this.eventMouseMove, false); + + // Add an invisible div to keep catching mouse event over iframes + WindowUtilities.disableScreen('__invisible__', '__invisible__', this.overlayOpacity); + + // Stop selection while dragging + document.body.ondrag = function () { return false; }; + document.body.onselectstart = function () { return false; }; + + this.currentDrag.show(); + Event.stop(event); + }, + + _round: function(val, round) { + return round == 1 ? val : val = Math.floor(val / round) * round; + }, + + // updateDrag event + _updateDrag: function(event) { + var pointer = [this._round(Event.pointerX(event), this.options.gridX), this._round(Event.pointerY(event), this.options.gridY)]; + var dx = pointer[0] - this.pointer[0]; + var dy = pointer[1] - this.pointer[1]; + + // Resize case, update width/height + if (this.doResize) { + var w = this.widthOrg + dx; + var h = this.heightOrg + dy; + + dx = this.width - this.widthOrg + dy = this.height - this.heightOrg + + // Check if it's a right position, update it to keep upper-left corner at the same position + if (this.useLeft) + w = this._updateWidthConstraint(w) + else + this.currentDrag.setStyle({right: (this.rightOrg -dx) + 'px'}); + // Check if it's a bottom position, update it to keep upper-left corner at the same position + if (this.useTop) + h = this._updateHeightConstraint(h) + else + this.currentDrag.setStyle({bottom: (this.bottomOrg -dy) + 'px'}); + + this.setSize(w , h); + this._notify("onResize"); + } + // Move case, update top/left + else { + this.pointer = pointer; + + if (this.useLeft) { + var left = parseFloat(this.currentDrag.getStyle('left')) + dx; + var newLeft = this._updateLeftConstraint(left); + // Keep mouse pointer correct + this.pointer[0] += newLeft-left; + this.currentDrag.setStyle({left: newLeft + 'px'}); + } + else + this.currentDrag.setStyle({right: parseFloat(this.currentDrag.getStyle('right')) - dx + 'px'}); + + if (this.useTop) { + var top = parseFloat(this.currentDrag.getStyle('top')) + dy; + var newTop = this._updateTopConstraint(top); + // Keep mouse pointer correct + this.pointer[1] += newTop - top; + this.currentDrag.setStyle({top: newTop + 'px'}); + } + else + this.currentDrag.setStyle({bottom: parseFloat(this.currentDrag.getStyle('bottom')) - dy + 'px'}); + + this._notify("onMove"); + } + if (this.iefix) + this._fixIEOverlapping(); + + this._removeStoreLocation(); + Event.stop(event); + }, + + // endDrag callback + _endDrag: function(event) { + // Remove temporary div over iframes + WindowUtilities.enableScreen('__invisible__'); + + if (this.doResize) + this._notify("onEndResize"); + else + this._notify("onEndMove"); + + // Release event observing + Event.stopObserving(document, "mouseup", this.eventMouseUp,false); + Event.stopObserving(document, "mousemove", this.eventMouseMove, false); + + Event.stop(event); + + this._hideWiredElement(); + + // Store new location/size if need be + this._saveCookie() + + // Restore selection + document.body.ondrag = null; + document.body.onselectstart = null; + }, + + _updateLeftConstraint: function(left) { + if (this.constraint && this.useLeft && this.useTop) { + var width = this.options.parent == document.body ? WindowUtilities.getPageSize().windowWidth : this.options.parent.getDimensions().width; + + if (left < this.constraintPad.left) + left = this.constraintPad.left; + if (left + this.width + this.widthE + this.widthW > width - this.constraintPad.right) + left = width - this.constraintPad.right - this.width - this.widthE - this.widthW; + } + return left; + }, + + _updateTopConstraint: function(top) { + if (this.constraint && this.useLeft && this.useTop) { + var height = this.options.parent == document.body ? WindowUtilities.getPageSize().windowHeight : this.options.parent.getDimensions().height; + + var h = this.height + this.heightN + this.heightS; + + if (top < this.constraintPad.top) + top = this.constraintPad.top; + if (top + h > height - this.constraintPad.bottom) + top = height - this.constraintPad.bottom - h; + } + return top; + }, + + _updateWidthConstraint: function(w) { + if (this.constraint && this.useLeft && this.useTop) { + var width = this.options.parent == document.body ? WindowUtilities.getPageSize().windowWidth : this.options.parent.getDimensions().width; + var left = parseFloat(this.element.getStyle("left")); + + if (left + w + this.widthE + this.widthW > width - this.constraintPad.right) + w = width - this.constraintPad.right - left - this.widthE - this.widthW; + } + return w; + }, + + _updateHeightConstraint: function(h) { + if (this.constraint && this.useLeft && this.useTop) { + var height = this.options.parent == document.body ? WindowUtilities.getPageSize().windowHeight : this.options.parent.getDimensions().height; + var top = parseFloat(this.element.getStyle("top")); + + if (top + h + this.heightN + this.heightS > height - this.constraintPad.bottom) + h = height - this.constraintPad.bottom - top - this.heightN - this.heightS; + } + return h; + }, + + + // Creates HTML window code + _createWindow: function(id) { + var className = this.options.className; + var win = document.createElement("div"); + win.setAttribute('id', id); + win.className = "dialog"; + + var content; + if (this.options.url) + content= ""; + else + content ="
    "; + + var closeDiv = this.options.closable ? "
    " : ""; + var minDiv = this.options.minimizable ? "
    " : ""; + var maxDiv = this.options.maximizable ? "
    " : ""; + var seAttributes = this.options.resizable ? "class='" + className + "_sizer' id='" + id + "_sizer'" : "class='" + className + "_se'"; + var blank = "../themes/default/blank.gif"; + + win.innerHTML = closeDiv + minDiv + maxDiv + "\ + \ + \ + \ + \ + \ + \ +
    "+ this.options.title +"
    \ + \ + \ + \ + \ + \ + \ +
    " + content + "
    \ + \ + \ + \ + \ + \ + \ +
    \ + "; + Element.hide(win); + this.options.parent.insertBefore(win, this.options.parent.firstChild); + Event.observe($(id + "_content"), "load", this.options.onload); + return win; + }, + + + changeClassName: function(newClassName) { + var className = this.options.className; + var id = this.getId(); + $A(["_close", "_minimize", "_maximize", "_sizer", "_content"]).each(function(value) { this._toggleClassName($(id + value), className + value, newClassName + value) }.bind(this)); + this._toggleClassName($(id + "_top"), className + "_title", newClassName + "_title"); + $$("#" + id + " td").each(function(td) {td.className = td.className.sub(className,newClassName); }); + this.options.className = newClassName; + }, + + _toggleClassName: function(element, oldClassName, newClassName) { + if (element) { + element.removeClassName(oldClassName); + element.addClassName(newClassName); + } + }, + + // Sets window location + setLocation: function(top, left) { + top = this._updateTopConstraint(top); + left = this._updateLeftConstraint(left); + + var e = this.currentDrag || this.element; + e.setStyle({top: top + 'px'}); + e.setStyle({left: left + 'px'}); + + this.useLeft = true; + this.useTop = true; + }, + + getLocation: function() { + var location = {}; + if (this.useTop) + location = Object.extend(location, {top: this.element.getStyle("top")}); + else + location = Object.extend(location, {bottom: this.element.getStyle("bottom")}); + if (this.useLeft) + location = Object.extend(location, {left: this.element.getStyle("left")}); + else + location = Object.extend(location, {right: this.element.getStyle("right")}); + + return location; + }, + + // Gets window size + getSize: function() { + return {width: this.width, height: this.height}; + }, + + // Sets window size + setSize: function(width, height, useEffect) { + width = parseFloat(width); + height = parseFloat(height); + + // Check min and max size + if (!this.minimized && width < this.options.minWidth) + width = this.options.minWidth; + + if (!this.minimized && height < this.options.minHeight) + height = this.options.minHeight; + + if (this.options. maxHeight && height > this.options. maxHeight) + height = this.options. maxHeight; + + if (this.options. maxWidth && width > this.options. maxWidth) + width = this.options. maxWidth; + + + if (this.useTop && this.useLeft && Window.hasEffectLib && Effect.ResizeWindow && useEffect) { + new Effect.ResizeWindow(this, null, null, width, height, {duration: Window.resizeEffectDuration}); + } else { + this.width = width; + this.height = height; + var e = this.currentDrag ? this.currentDrag : this.element; + + e.setStyle({width: width + this.widthW + this.widthE + "px"}) + e.setStyle({height: height + this.heightN + this.heightS + "px"}) + + // Update content size + if (!this.currentDrag || this.currentDrag == this.element) { + var content = $(this.element.id + '_content'); + content.setStyle({height: height + 'px'}); + content.setStyle({width: width + 'px'}); + } + } + }, + + updateHeight: function() { + this.setSize(this.width, this.content.scrollHeight, true); + }, + + updateWidth: function() { + this.setSize(this.content.scrollWidth, this.height, true); + }, + + // Brings window to front + toFront: function() { + if (this.element.style.zIndex < Windows.maxZIndex) + this.setZIndex(Windows.maxZIndex + 1); + if (this.iefix) + this._fixIEOverlapping(); + }, + + getBounds: function(insideOnly) { + if (! this.width || !this.height || !this.visible) + this.computeBounds(); + var w = this.width; + var h = this.height; + + if (!insideOnly) { + w += this.widthW + this.widthE; + h += this.heightN + this.heightS; + } + var bounds = Object.extend(this.getLocation(), {width: w + "px", height: h + "px"}); + return bounds; + }, + + computeBounds: function() { + if (! this.width || !this.height) { + var size = WindowUtilities._computeSize(this.content.innerHTML, this.content.id, this.width, this.height, 0, this.options.className) + if (this.height) + this.width = size + 5 + else + this.height = size + 5 + } + + this.setSize(this.width, this.height); + if (this.centered) + this._center(this.centerTop, this.centerLeft); + }, + + // Displays window modal state or not + show: function(modal) { + this.visible = true; + if (modal) { + // Hack for Safari !! + if (typeof this.overlayOpacity == "undefined") { + var that = this; + setTimeout(function() {that.show(modal)}, 10); + return; + } + Windows.addModalWindow(this); + + this.modal = true; + this.setZIndex(Windows.maxZIndex + 1); + Windows.unsetOverflow(this); + } + else + if (!this.element.style.zIndex) + this.setZIndex(Windows.maxZIndex + 1); + + // To restore overflow if need be + if (this.oldStyle) + this.getContent().setStyle({overflow: this.oldStyle}); + + this.computeBounds(); + + this._notify("onBeforeShow"); + if (this.options.showEffect != Element.show && this.options.showEffectOptions) + this.options.showEffect(this.element, this.options.showEffectOptions); + else + this.options.showEffect(this.element); + + this._checkIEOverlapping(); + WindowUtilities.focusedWindow = this + this._notify("onShow"); + }, + + // Displays window modal state or not at the center of the page + showCenter: function(modal, top, left) { + this.centered = true; + this.centerTop = top; + this.centerLeft = left; + + this.show(modal); + }, + + isVisible: function() { + return this.visible; + }, + + _center: function(top, left) { + var windowScroll = WindowUtilities.getWindowScroll(this.options.parent); + var pageSize = WindowUtilities.getPageSize(this.options.parent); + if (typeof top == "undefined") + top = (pageSize.windowHeight - (this.height + this.heightN + this.heightS))/2; + top += windowScroll.top + + if (typeof left == "undefined") + left = (pageSize.windowWidth - (this.width + this.widthW + this.widthE))/2; + left += windowScroll.left + this.setLocation(top, left); + this.toFront(); + }, + + _recenter: function(event) { + if (this.centered) { + var pageSize = WindowUtilities.getPageSize(this.options.parent); + var windowScroll = WindowUtilities.getWindowScroll(this.options.parent); + + // Check for this stupid IE that sends dumb events + if (this.pageSize && this.pageSize.windowWidth == pageSize.windowWidth && this.pageSize.windowHeight == pageSize.windowHeight && + this.windowScroll.left == windowScroll.left && this.windowScroll.top == windowScroll.top) + return; + this.pageSize = pageSize; + this.windowScroll = windowScroll; + // set height of Overlay to take up whole page and show + if ($('overlay_modal')) + $('overlay_modal').setStyle({height: (pageSize.pageHeight + 'px')}); + + if (this.options.recenterAuto) + this._center(this.centerTop, this.centerLeft); + } + }, + + // Hides window + hide: function() { + this.visible = false; + if (this.modal) { + Windows.removeModalWindow(this); + Windows.resetOverflow(); + } + // To avoid bug on scrolling bar + this.oldStyle = this.getContent().getStyle('overflow') || "auto" + this.getContent().setStyle({overflow: "hidden"}); + + this.options.hideEffect(this.element, this.options.hideEffectOptions); + + if(this.iefix) + this.iefix.hide(); + + if (!this.doNotNotifyHide) + this._notify("onHide"); + }, + + close: function() { + // Asks closeCallback if exists + if (this.visible) { + if (this.options.closeCallback && ! this.options.closeCallback(this)) + return; + + if (this.options.destroyOnClose) { + var destroyFunc = this.destroy.bind(this); + if (this.options.hideEffectOptions.afterFinish) { + var func = this.options.hideEffectOptions.afterFinish; + this.options.hideEffectOptions.afterFinish = function() {func();destroyFunc() } + } + else + this.options.hideEffectOptions.afterFinish = function() {destroyFunc() } + } + Windows.updateFocusedWindow(); + + this.doNotNotifyHide = true; + this.hide(); + this.doNotNotifyHide = false; + this._notify("onClose"); + } + }, + + minimize: function() { + if (this.resizing) + return; + + var r2 = $(this.getId() + "_row2"); + + if (!this.minimized) { + this.minimized = true; + + var dh = r2.getDimensions().height; + this.r2Height = dh; + var h = this.element.getHeight() - dh; + + if (this.useLeft && this.useTop && Window.hasEffectLib && Effect.ResizeWindow) { + new Effect.ResizeWindow(this, null, null, null, this.height -dh, {duration: Window.resizeEffectDuration}); + } else { + this.height -= dh; + this.element.setStyle({height: h + "px"}); + r2.hide(); + } + + if (! this.useTop) { + var bottom = parseFloat(this.element.getStyle('bottom')); + this.element.setStyle({bottom: (bottom + dh) + 'px'}); + } + } + else { + this.minimized = false; + + var dh = this.r2Height; + this.r2Height = null; + if (this.useLeft && this.useTop && Window.hasEffectLib && Effect.ResizeWindow) { + new Effect.ResizeWindow(this, null, null, null, this.height + dh, {duration: Window.resizeEffectDuration}); + } + else { + var h = this.element.getHeight() + dh; + this.height += dh; + this.element.setStyle({height: h + "px"}) + r2.show(); + } + if (! this.useTop) { + var bottom = parseFloat(this.element.getStyle('bottom')); + this.element.setStyle({bottom: (bottom - dh) + 'px'}); + } + this.toFront(); + } + this._notify("onMinimize"); + + // Store new location/size if need be + this._saveCookie() + }, + + maximize: function() { + if (this.isMinimized() || this.resizing) + return; + + if (Prototype.BrowserFeatures.IE && this.heightN == 0) + this._getWindowBorderSize(); + + if (this.storedLocation != null) { + this._restoreLocation(); + if(this.iefix) + this.iefix.hide(); + } + else { + this._storeLocation(); + Windows.unsetOverflow(this); + + var windowScroll = WindowUtilities.getWindowScroll(this.options.parent); + var pageSize = WindowUtilities.getPageSize(this.options.parent); + var left = windowScroll.left; + var top = windowScroll.top; + + if (this.options.parent != document.body) { + windowScroll = {top:0, left:0, bottom:0, right:0}; + var dim = this.options.parent.getDimensions(); + pageSize.windowWidth = dim.width; + pageSize.windowHeight = dim.height; + top = 0; + left = 0; + } + + if (this.constraint) { + pageSize.windowWidth -= Math.max(0, this.constraintPad.left) + Math.max(0, this.constraintPad.right); + pageSize.windowHeight -= Math.max(0, this.constraintPad.top) + Math.max(0, this.constraintPad.bottom); + left += Math.max(0, this.constraintPad.left); + top += Math.max(0, this.constraintPad.top); + } + + var width = pageSize.windowWidth - this.widthW - this.widthE; + var height= pageSize.windowHeight - this.heightN - this.heightS; + + if (this.useLeft && this.useTop && Window.hasEffectLib && Effect.ResizeWindow) { + new Effect.ResizeWindow(this, top, left, width, height, {duration: Window.resizeEffectDuration}); + } + else { + this.setSize(width, height); + this.element.setStyle(this.useLeft ? {left: left} : {right: left}); + this.element.setStyle(this.useTop ? {top: top} : {bottom: top}); + } + + this.toFront(); + if (this.iefix) + this._fixIEOverlapping(); + } + this._notify("onMaximize"); + + // Store new location/size if need be + this._saveCookie() + }, + + isMinimized: function() { + return this.minimized; + }, + + isMaximized: function() { + return (this.storedLocation != null); + }, + + setOpacity: function(opacity) { + if (Element.setOpacity) + Element.setOpacity(this.element, opacity); + }, + + setZIndex: function(zindex) { + this.element.setStyle({zIndex: zindex}); + Windows.updateZindex(zindex, this); + }, + + setTitle: function(newTitle) { + if (!newTitle || newTitle == "") + newTitle = " "; + + Element.update(this.element.id + '_top', newTitle); + }, + + getTitle: function() { + return $(this.element.id + '_top').innerHTML; + }, + + setStatusBar: function(element) { + var statusBar = $(this.getId() + "_bottom"); + + if (typeof(element) == "object") { + if (this.bottombar.firstChild) + this.bottombar.replaceChild(element, this.bottombar.firstChild); + else + this.bottombar.appendChild(element); + } + else + this.bottombar.innerHTML = element; + }, + + _checkIEOverlapping: function() { + if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (navigator.userAgent.indexOf('Opera')<0) && (this.element.getStyle('position')=='absolute')) { + new Insertion.After(this.element.id, ''); + this.iefix = $(this.element.id+'_iefix'); + } + if(this.iefix) + setTimeout(this._fixIEOverlapping.bind(this), 50); + }, + + _fixIEOverlapping: function() { + Position.clone(this.element, this.iefix); + this.iefix.style.zIndex = this.element.style.zIndex - 1; + this.iefix.show(); + }, + + _getWindowBorderSize: function(event) { + // Hack to get real window border size!! + var div = this._createHiddenDiv(this.options.className + "_n") + this.heightN = Element.getDimensions(div).height; + div.parentNode.removeChild(div) + + var div = this._createHiddenDiv(this.options.className + "_s") + this.heightS = Element.getDimensions(div).height; + div.parentNode.removeChild(div) + + var div = this._createHiddenDiv(this.options.className + "_e") + this.widthE = Element.getDimensions(div).width; + div.parentNode.removeChild(div) + + var div = this._createHiddenDiv(this.options.className + "_w") + this.widthW = Element.getDimensions(div).width; + div.parentNode.removeChild(div); + + var div = document.createElement("div"); + div.className = "overlay_" + this.options.className ; + document.body.appendChild(div); + //alert("no timeout:\nopacity: " + div.getStyle("opacity") + "\nwidth: " + document.defaultView.getComputedStyle(div, null).width); + var that = this; + + // Workaround for Safari!! + setTimeout(function() {that.overlayOpacity = ($(div).getStyle("opacity")); div.parentNode.removeChild(div);}, 10); + + // Workaround for IE!! + if (Prototype.BrowserFeatures.IE) { + this.heightS = $(this.getId() +"_row3").getDimensions().height; + this.heightN = $(this.getId() +"_row1").getDimensions().height; + } + + // Safari size fix + if (Prototype.BrowserFeatures.WebKit && Prototype.BrowserFeatures.WebKitVersion < 420) + this.setSize(this.width, this.height); + if (this.doMaximize) + this.maximize(); + if (this.doMinimize) + this.minimize(); + }, + + _createHiddenDiv: function(className) { + var objBody = document.body; + var win = document.createElement("div"); + win.setAttribute('id', this.element.id+ "_tmp"); + win.className = className; + win.style.display = 'none'; + win.innerHTML = ''; + objBody.insertBefore(win, objBody.firstChild); + return win; + }, + + _storeLocation: function() { + if (this.storedLocation == null) { + this.storedLocation = {useTop: this.useTop, useLeft: this.useLeft, + top: this.element.getStyle('top'), bottom: this.element.getStyle('bottom'), + left: this.element.getStyle('left'), right: this.element.getStyle('right'), + width: this.width, height: this.height }; + } + }, + + _restoreLocation: function() { + if (this.storedLocation != null) { + this.useLeft = this.storedLocation.useLeft; + this.useTop = this.storedLocation.useTop; + + if (this.useLeft && this.useTop && Window.hasEffectLib && Effect.ResizeWindow) + new Effect.ResizeWindow(this, this.storedLocation.top, this.storedLocation.left, this.storedLocation.width, this.storedLocation.height, {duration: Window.resizeEffectDuration}); + else { + this.element.setStyle(this.useLeft ? {left: this.storedLocation.left} : {right: this.storedLocation.right}); + this.element.setStyle(this.useTop ? {top: this.storedLocation.top} : {bottom: this.storedLocation.bottom}); + this.setSize(this.storedLocation.width, this.storedLocation.height); + } + + Windows.resetOverflow(); + this._removeStoreLocation(); + } + }, + + _removeStoreLocation: function() { + this.storedLocation = null; + }, + + _saveCookie: function() { + if (this.cookie) { + var value = ""; + if (this.useLeft) + value += "l:" + (this.storedLocation ? this.storedLocation.left : this.element.getStyle('left')) + else + value += "r:" + (this.storedLocation ? this.storedLocation.right : this.element.getStyle('right')) + if (this.useTop) + value += ",t:" + (this.storedLocation ? this.storedLocation.top : this.element.getStyle('top')) + else + value += ",b:" + (this.storedLocation ? this.storedLocation.bottom :this.element.getStyle('bottom')) + + value += "," + (this.storedLocation ? this.storedLocation.width : this.width); + value += "," + (this.storedLocation ? this.storedLocation.height : this.height); + value += "," + this.isMinimized(); + value += "," + this.isMaximized(); + WindowUtilities.setCookie(value, this.cookie) + } + }, + + _createWiredElement: function() { + if (! this.wiredElement) { + if (Prototype.BrowserFeatures.IE) + this._getWindowBorderSize(); + var div = document.createElement("div"); + div.className = "wired_frame " + this.options.className + "_wired_frame"; + + div.style.position = 'absolute'; + this.options.parent.insertBefore(div, this.options.parent.firstChild); + this.wiredElement = $(div); + } + if (this.useLeft) + this.wiredElement.setStyle({left: this.element.getStyle('left')}); + else + this.wiredElement.setStyle({right: this.element.getStyle('right')}); + + if (this.useTop) + this.wiredElement.setStyle({top: this.element.getStyle('top')}); + else + this.wiredElement.setStyle({bottom: this.element.getStyle('bottom')}); + + var dim = this.element.getDimensions(); + this.wiredElement.setStyle({width: dim.width + "px", height: dim.height +"px"}); + + this.wiredElement.setStyle({zIndex: Windows.maxZIndex+30}); + return this.wiredElement; + }, + + _hideWiredElement: function() { + if (! this.wiredElement || ! this.currentDrag) + return; + if (this.currentDrag == this.element) + this.currentDrag = null; + else { + if (this.useLeft) + this.element.setStyle({left: this.currentDrag.getStyle('left')}); + else + this.element.setStyle({right: this.currentDrag.getStyle('right')}); + + if (this.useTop) + this.element.setStyle({top: this.currentDrag.getStyle('top')}); + else + this.element.setStyle({bottom: this.currentDrag.getStyle('bottom')}); + + this.currentDrag.hide(); + this.currentDrag = null; + if (this.doResize) + this.setSize(this.width, this.height); + } + }, + + _notify: function(eventName) { + if (this.options[eventName]) + this.options[eventName](this); + else + Windows.notify(eventName, this); + } +}; + +// Windows containers, register all page windows +var Windows = { + windows: [], + modalWindows: [], + observers: [], + focusedWindow: null, + maxZIndex: 0, + overlayShowEffectOptions: {duration: 0.5}, + overlayHideEffectOptions: {duration: 0.5}, + + addObserver: function(observer) { + this.removeObserver(observer); + this.observers.push(observer); + }, + + removeObserver: function(observer) { + this.observers = this.observers.reject( function(o) { return o==observer }); + }, + + // onDestroy onStartResize onStartMove onResize onMove onEndResize onEndMove onFocus onBlur onBeforeShow onShow onHide onMinimize onMaximize onClose + notify: function(eventName, win) { + this.observers.each( function(o) {if(o[eventName]) o[eventName](eventName, win);}); + }, + + // Gets window from its id + getWindow: function(id) { + return this.windows.detect(function(d) { return d.getId() ==id }); + }, + + // Gets the last focused window + getFocusedWindow: function() { + return this.focusedWindow; + }, + + updateFocusedWindow: function() { + this.focusedWindow = this.windows.length >=2 ? this.windows[this.windows.length-2] : null; + }, + + // Registers a new window (called by Windows constructor) + register: function(win) { + this.windows.push(win); + }, + + // Add a modal window in the stack + addModalWindow: function(win) { + // Disable screen if first modal window + if (this.modalWindows.length == 0) { + WindowUtilities.disableScreen(win.options.className, 'overlay_modal', win.overlayOpacity, win.getId(), win.options.parent); + } + else { + // Move overlay over all windows + if (Window.keepMultiModalWindow) { + $('overlay_modal').style.zIndex = Windows.maxZIndex + 1; + Windows.maxZIndex += 1; + WindowUtilities._hideSelect(this.modalWindows.last().getId()); + } + // Hide current modal window + else + this.modalWindows.last().element.hide(); + // Fucking IE select issue + WindowUtilities._showSelect(win.getId()); + } + this.modalWindows.push(win); + }, + + removeModalWindow: function(win) { + this.modalWindows.pop(); + + // No more modal windows + if (this.modalWindows.length == 0) + WindowUtilities.enableScreen(); + else { + if (Window.keepMultiModalWindow) { + this.modalWindows.last().toFront(); + WindowUtilities._showSelect(this.modalWindows.last().getId()); + } + else + this.modalWindows.last().element.show(); + } + }, + + // Registers a new window (called by Windows constructor) + register: function(win) { + this.windows.push(win); + }, + + // Unregisters a window (called by Windows destructor) + unregister: function(win) { + this.windows = this.windows.reject(function(d) { return d==win }); + }, + + // Closes all windows + closeAll: function() { + this.windows.each( function(w) {Windows.close(w.getId())} ); + }, + + closeAllModalWindows: function() { + WindowUtilities.enableScreen(); + this.modalWindows.each( function(win) {if (win) win.close()}); + }, + + // Minimizes a window with its id + minimize: function(id, event) { + var win = this.getWindow(id) + if (win && win.visible) + win.minimize(); + Event.stop(event); + }, + + // Maximizes a window with its id + maximize: function(id, event) { + var win = this.getWindow(id) + if (win && win.visible) + win.maximize(); + Event.stop(event); + }, + + // Closes a window with its id + close: function(id, event) { + var win = this.getWindow(id); + if (win) + win.close(); + if (event) + Event.stop(event); + }, + + blur: function(id) { + var win = this.getWindow(id); + if (!win) + return; + if (win.options.blurClassName) + win.changeClassName(win.options.blurClassName); + if (this.focusedWindow == win) + this.focusedWindow = null; + win._notify("onBlur"); + }, + + focus: function(id) { + var win = this.getWindow(id); + if (!win) + return; + if (this.focusedWindow) + this.blur(this.focusedWindow.getId()) + + if (win.options.focusClassName) + win.changeClassName(win.options.focusClassName); + this.focusedWindow = win; + win._notify("onFocus"); + }, + + unsetOverflow: function(except) { + this.windows.each(function(d) { d.oldOverflow = d.getContent().getStyle("overflow") || "auto" ; d.getContent().setStyle({overflow: "hidden"}) }); + if (except && except.oldOverflow) + except.getContent().setStyle({overflow: except.oldOverflow}); + }, + + resetOverflow: function() { + this.windows.each(function(d) { if (d.oldOverflow) d.getContent().setStyle({overflow: d.oldOverflow}) }); + }, + + updateZindex: function(zindex, win) { + if (zindex > this.maxZIndex) { + this.maxZIndex = zindex; + if (this.focusedWindow) + this.blur(this.focusedWindow.getId()) + } + this.focusedWindow = win; + if (this.focusedWindow) + this.focus(this.focusedWindow.getId()) + } +}; + +var Dialog = { + dialogId: null, + onCompleteFunc: null, + callFunc: null, + parameters: null, + + confirm: function(content, parameters) { + // Get Ajax return before + if (content && typeof content != "string") { + Dialog._runAjaxRequest(content, parameters, Dialog.confirm); + return + } + content = content || ""; + + parameters = parameters || {}; + var okLabel = parameters.okLabel ? parameters.okLabel : "Ok"; + var cancelLabel = parameters.cancelLabel ? parameters.cancelLabel : "Cancel"; + + // Backward compatibility + parameters = Object.extend(parameters, parameters.windowParameters || {}); + parameters.windowParameters = parameters.windowParameters || {}; + + parameters.className = parameters.className || "alert"; + + var okButtonClass = "class ='" + (parameters.buttonClass ? parameters.buttonClass + " " : "") + " ok_button'" + var cancelButtonClass = "class ='" + (parameters.buttonClass ? parameters.buttonClass + " " : "") + " cancel_button'" + var content = "\ +
    " + content + "
    \ +
    \ + \ + \ +
    \ + "; + return this._openDialog(content, parameters) + }, + + alert: function(content, parameters) { + // Get Ajax return before + if (content && typeof content != "string") { + Dialog._runAjaxRequest(content, parameters, Dialog.alert); + return + } + content = content || ""; + + parameters = parameters || {}; + var okLabel = parameters.okLabel ? parameters.okLabel : "Ok"; + + // Backward compatibility + parameters = Object.extend(parameters, parameters.windowParameters || {}); + parameters.windowParameters = parameters.windowParameters || {}; + + parameters.className = parameters.className || "alert"; + + var okButtonClass = "class ='" + (parameters.buttonClass ? parameters.buttonClass + " " : "") + " ok_button'" + var content = "\ +
    " + content + "
    \ +
    \ + \ +
    "; + return this._openDialog(content, parameters) + }, + + info: function(content, parameters) { + // Get Ajax return before + if (content && typeof content != "string") { + Dialog._runAjaxRequest(content, parameters, Dialog.info); + return + } + content = content || ""; + + // Backward compatibility + parameters = parameters || {}; + parameters = Object.extend(parameters, parameters.windowParameters || {}); + parameters.windowParameters = parameters.windowParameters || {}; + + parameters.className = parameters.className || "alert"; + + var content = ""; + if (parameters.showProgress) + content += ""; + + parameters.ok = null; + parameters.cancel = null; + + return this._openDialog(content, parameters) + }, + + setInfoMessage: function(message) { + $('modal_dialog_message').update(message); + }, + + closeInfo: function() { + Windows.close(this.dialogId); + }, + + _openDialog: function(content, parameters) { + var className = parameters.className; + + if (! parameters.height && ! parameters.width) { + parameters.width = WindowUtilities.getPageSize(parameters.options.parent || document.body).pageWidth / 2; + } + if (parameters.id) + this.dialogId = parameters.id; + else { + var t = new Date(); + this.dialogId = 'modal_dialog_' + t.getTime(); + parameters.id = this.dialogId; + } + + // compute height or width if need be + if (! parameters.height || ! parameters.width) { + var size = WindowUtilities._computeSize(content, this.dialogId, parameters.width, parameters.height, 5, className) + if (parameters.height) + parameters.width = size + 5 + else + parameters.height = size + 5 + } + parameters.effectOptions = parameters.effectOptions ; + parameters.resizable = parameters.resizable || false; + parameters.minimizable = parameters.minimizable || false; + parameters.maximizable = parameters.maximizable || false; + parameters.draggable = parameters.draggable || false; + parameters.closable = parameters.closable || false; + + var win = new Window(parameters); + win.getContent().innerHTML = content; + + win.showCenter(true, parameters.top, parameters.left); + win.setDestroyOnClose(); + + win.cancelCallback = parameters.onCancel || parameters.cancel; + win.okCallback = parameters.onOk || parameters.ok; + + return win; + }, + + _getAjaxContent: function(originalRequest) { + Dialog.callFunc(originalRequest.responseText, Dialog.parameters) + }, + + _runAjaxRequest: function(message, parameters, callFunc) { + if (message.options == null) + message.options = {} + Dialog.onCompleteFunc = message.options.onComplete; + Dialog.parameters = parameters; + Dialog.callFunc = callFunc; + + message.options.onComplete = Dialog._getAjaxContent; + new Ajax.Request(message.url, message.options); + }, + + okCallback: function() { + var win = Windows.focusedWindow; + if (!win.okCallback || win.okCallback(win)) { + // Remove onclick on button + $$("#" + win.getId()+" input").each(function(element) {element.onclick=null;}) + win.close(); + } + }, + + cancelCallback: function() { + var win = Windows.focusedWindow; + // Remove onclick on button + $$("#" + win.getId()+" input").each(function(element) {element.onclick=null}) + win.close(); + if (win.cancelCallback) + win.cancelCallback(win); + } +} +/* + Based on Lightbox JS: Fullsize Image Overlays + by Lokesh Dhakar - http://www.huddletogether.com + + For more information on this script, visit: + http://huddletogether.com/projects/lightbox/ + + Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/ + (basically, do anything you want, just leave my name and link) +*/ + +if (Prototype.BrowserFeatures.WebKit) { + var array = navigator.userAgent.match(new RegExp(/AppleWebKit\/([\d\.\+]*)/)); + Prototype.BrowserFeatures.WebKitVersion = parseFloat(array[1]); +} + +var WindowUtilities = { + // From dragdrop.js + getWindowScroll: function(parent) { + var T, L, W, H; + parent = parent || document.body; + if (parent != document.body) { + T = parent.scrollTop; + L = parent.scrollLeft; + W = parent.scrollWidth; + H = parent.scrollHeight; + } + else { + var w = window; + with (w.document) { + if (w.document.documentElement && documentElement.scrollTop) { + T = documentElement.scrollTop; + L = documentElement.scrollLeft; + } else if (w.document.body) { + T = body.scrollTop; + L = body.scrollLeft; + } + if (w.innerWidth) { + W = w.innerWidth; + H = w.innerHeight; + } else if (w.document.documentElement && documentElement.clientWidth) { + W = documentElement.clientWidth; + H = documentElement.clientHeight; + } else { + W = body.offsetWidth; + H = body.offsetHeight + } + } + } + return { top: T, left: L, width: W, height: H }; + }, + // + // getPageSize() + // Returns array with page width, height and window width, height + // Core code from - quirksmode.org + // Edit for Firefox by pHaez + // + getPageSize: function(parent){ + parent = parent || document.body; + var windowWidth, windowHeight; + var pageHeight, pageWidth; + if (parent != document.body) { + windowWidth = parent.getWidth(); + windowHeight = parent.getHeight(); + pageWidth = parent.scrollWidth; + pageHeight = parent.scrollHeight; + } + else { + var xScroll, yScroll; + + if (window.innerHeight && window.scrollMaxY) { + xScroll = document.body.scrollWidth; + yScroll = window.innerHeight + window.scrollMaxY; + } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac + xScroll = document.body.scrollWidth; + yScroll = document.body.scrollHeight; + } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari + xScroll = document.body.offsetWidth; + yScroll = document.body.offsetHeight; + } + + + if (self.innerHeight) { // all except Explorer + windowWidth = self.innerWidth; + windowHeight = self.innerHeight; + } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode + windowWidth = document.documentElement.clientWidth; + windowHeight = document.documentElement.clientHeight; + } else if (document.body) { // other Explorers + windowWidth = document.body.clientWidth; + windowHeight = document.body.clientHeight; + } + + // for small pages with total height less then height of the viewport + if(yScroll < windowHeight){ + pageHeight = windowHeight; + } else { + pageHeight = yScroll; + } + + // for small pages with total width less then width of the viewport + if(xScroll < windowWidth){ + pageWidth = windowWidth; + } else { + pageWidth = xScroll; + } + } + return {pageWidth: pageWidth ,pageHeight: pageHeight , windowWidth: windowWidth, windowHeight: windowHeight}; + }, + + disableScreen: function(className, overlayId, overlayOpacity, contentId, parent) { + WindowUtilities.initLightbox(overlayId, className, function() {this._disableScreen(className, overlayId, overlayOpacity, contentId)}.bind(this), parent || document.body); + }, + + _disableScreen: function(className, overlayId, overlayOpacity, contentId) { + // prep objects + var objOverlay = $(overlayId); + + var pageSize = WindowUtilities.getPageSize(objOverlay.parentNode); + + // Hide select boxes as they will 'peek' through the image in IE, store old value + if (contentId && Prototype.BrowserFeatures.IE) { + WindowUtilities._hideSelect(); + WindowUtilities._showSelect(contentId); + } + + // set height of Overlay to take up whole page and show + objOverlay.style.height = (pageSize.pageHeight + 'px'); + objOverlay.style.display = 'none'; + if (overlayId == "overlay_modal" && Window.hasEffectLib && Windows.overlayShowEffectOptions) { + objOverlay.overlayOpacity = overlayOpacity; + new Effect.Appear(objOverlay, Object.extend({from: 0, to: overlayOpacity}, Windows.overlayShowEffectOptions)); + } + else + objOverlay.style.display = "block"; + }, + + enableScreen: function(id) { + id = id || 'overlay_modal'; + var objOverlay = $(id); + if (objOverlay) { + // hide lightbox and overlay + if (id == "overlay_modal" && Window.hasEffectLib && Windows.overlayHideEffectOptions) + new Effect.Fade(objOverlay, Object.extend({from: objOverlay.overlayOpacity, to:0}, Windows.overlayHideEffectOptions)); + else { + objOverlay.style.display = 'none'; + objOverlay.parentNode.removeChild(objOverlay); + } + + // make select boxes visible using old value + if (id != "__invisible__") + WindowUtilities._showSelect(); + } + }, + + _hideSelect: function(id) { + if (Prototype.BrowserFeatures.IE) { + id = id == null ? "" : "#" + id + " "; + $$(id + 'select').each(function(element) { + if (! WindowUtilities.isDefined(element.oldVisibility)) { + element.oldVisibility = element.style.visibility ? element.style.visibility : "visible"; + element.style.visibility = "hidden"; + } + }); + } + }, + + _showSelect: function(id) { + if (Prototype.BrowserFeatures.IE) { + id = id == null ? "" : "#" + id + " "; + $$(id + 'select').each(function(element) { + if (WindowUtilities.isDefined(element.oldVisibility)) { + // Why?? Ask IE + try { + element.style.visibility = element.oldVisibility; + } catch(e) { + element.style.visibility = "visible"; + } + element.oldVisibility = null; + } + else { + if (element.style.visibility) + element.style.visibility = "visible"; + } + }); + } + }, + + isDefined: function(object) { + return typeof(object) != "undefined" && object != null; + }, + + // initLightbox() + // Function runs on window load, going through link tags looking for rel="lightbox". + // These links receive onclick events that enable the lightbox display for their targets. + // The function also inserts html markup at the top of the page which will be used as a + // container for the overlay pattern and the inline image. + initLightbox: function(id, className, doneHandler, parent) { + // Already done, just update zIndex + if ($(id)) { + Element.setStyle(id, {zIndex: Windows.maxZIndex + 1}); + Windows.maxZIndex++; + doneHandler(); + } + // create overlay div and hardcode some functional styles (aesthetic styles are in CSS file) + else { + var objOverlay = document.createElement("div"); + objOverlay.setAttribute('id', id); + objOverlay.className = "overlay_" + className + objOverlay.style.display = 'none'; + objOverlay.style.position = 'absolute'; + objOverlay.style.top = '0'; + objOverlay.style.left = '0'; + objOverlay.style.zIndex = Windows.maxZIndex + 1; + Windows.maxZIndex++; + objOverlay.style.width = '100%'; + parent.insertBefore(objOverlay, parent.firstChild); + if (Prototype.BrowserFeatures.WebKit && id == "overlay_modal") { + setTimeout(function() {doneHandler()}, 10); + } + else + doneHandler(); + } + }, + + setCookie: function(value, parameters) { + document.cookie= parameters[0] + "=" + escape(value) + + ((parameters[1]) ? "; expires=" + parameters[1].toGMTString() : "") + + ((parameters[2]) ? "; path=" + parameters[2] : "") + + ((parameters[3]) ? "; domain=" + parameters[3] : "") + + ((parameters[4]) ? "; secure" : ""); + }, + + getCookie: function(name) { + var dc = document.cookie; + var prefix = name + "="; + var begin = dc.indexOf("; " + prefix); + if (begin == -1) { + begin = dc.indexOf(prefix); + if (begin != 0) return null; + } else { + begin += 2; + } + var end = document.cookie.indexOf(";", begin); + if (end == -1) { + end = dc.length; + } + return unescape(dc.substring(begin + prefix.length, end)); + }, + + _computeSize: function(content, id, width, height, margin, className) { + var objBody = document.body; + var tmpObj = document.createElement("div"); + tmpObj.setAttribute('id', id); + tmpObj.className = className + "_content"; + + if (height) + tmpObj.style.height = height + "px" + else + tmpObj.style.width = width + "px" + + tmpObj.style.position = 'absolute'; + tmpObj.style.top = '0'; + tmpObj.style.left = '0'; + tmpObj.style.display = 'none'; + + tmpObj.innerHTML = content; + objBody.insertBefore(tmpObj, objBody.firstChild); + + var size; + if (height) + size = $(tmpObj).getDimensions().width + margin; + else + size = $(tmpObj).getDimensions().height + margin; + objBody.removeChild(tmpObj); + return size; + } +} + diff --git a/xCAT-web/javascripts/window_effects.js b/xCAT-web/javascripts/window_effects.js new file mode 100644 index 000000000..6ad73cc05 --- /dev/null +++ b/xCAT-web/javascripts/window_effects.js @@ -0,0 +1,157 @@ +Effect.ResizeWindow = Class.create(); +Object.extend(Object.extend(Effect.ResizeWindow.prototype, Effect.Base.prototype), { + initialize: function(win, top, left, width, height) { + this.window = win; + this.window.resizing = true; + + var size = win.getSize(); + this.initWidth = parseFloat(size.width); + this.initHeight = parseFloat(size.height); + + var location = win.getLocation(); + this.initTop = parseFloat(location.top); + this.initLeft = parseFloat(location.left); + + this.width = width != null ? parseFloat(width) : this.initWidth; + this.height = height != null ? parseFloat(height) : this.initHeight; + this.top = top != null ? parseFloat(top) : this.initTop; + this.left = left != null ? parseFloat(left) : this.initLeft; + + this.dx = this.left - this.initLeft; + this.dy = this.top - this.initTop; + this.dw = this.width - this.initWidth; + this.dh = this.height - this.initHeight; + + this.r2 = $(this.window.getId() + "_row2"); + this.content = $(this.window.getId() + "_content"); + + this.contentOverflow = this.content.getStyle("overflow") || "auto"; + this.content.setStyle({overflow: "hidden"}); + + // Wired mode + if (this.window.options.wiredDrag) { + this.window.currentDrag = win._createWiredElement(); + this.window.currentDrag.show(); + this.window.element.hide(); + } + + this.start(arguments[5]); + }, + + update: function(position) { + var width = Math.floor(this.initWidth + this.dw * position); + var height = Math.floor(this.initHeight + this.dh * position); + var top = Math.floor(this.initTop + this.dy * position); + var left = Math.floor(this.initLeft + this.dx * position); + + if (window.ie) { + if (Math.floor(height) == 0) + this.r2.hide(); + else if (Math.floor(height) >1) + this.r2.show(); + } + this.r2.setStyle({height: height}); + this.window.setSize(width, height); + this.window.setLocation(top, left); + }, + + finish: function(position) { + // Wired mode + if (this.window.options.wiredDrag) { + this.window._hideWiredElement(); + this.window.element.show(); + } + + this.window.setSize(this.width, this.height); + this.window.setLocation(this.top, this.left); + this.r2.setStyle({height: null}); + + this.content.setStyle({overflow: this.contentOverflow}); + + this.window.resizing = false; + } +}); + +Effect.ModalSlideDown = function(element) { + var windowScroll = WindowUtilities.getWindowScroll(); + var height = element.getStyle("height"); + element.setStyle({top: - (parseFloat(height) - windowScroll.top) + "px"}); + + element.show(); + return new Effect.Move(element, Object.extend({ x: 0, y: parseFloat(height) }, arguments[1] || {})); +}; + + +Effect.ModalSlideUp = function(element) { + var height = element.getStyle("height"); + return new Effect.Move(element, Object.extend({ x: 0, y: -parseFloat(height) }, arguments[1] || {})); +}; + +PopupEffect = Class.create(); +PopupEffect.prototype = { + initialize: function(htmlElement) { + this.html = $(htmlElement); + this.options = Object.extend({className: "popup_effect", duration: 0.4}, arguments[1] || {}); + + }, + show: function(element, options) { + var position = Position.cumulativeOffset(this.html); + var size = this.html.getDimensions(); + var bounds = element.win.getBounds(); + this.window = element.win; + // Create a div + if (!this.div) { + this.div = document.createElement("div"); + this.div.className = this.options.className; + this.div.style.height = size.height + "px"; + this.div.style.width = size.width + "px"; + this.div.style.top = position[1] + "px"; + this.div.style.left = position[0] + "px"; + this.div.style.position = "absolute" + document.body.appendChild(this.div); + } + if (this.options.fromOpacity) + this.div.setStyle({opacity: this.options.fromOpacity}) + this.div.show(); + var style = "top:" + bounds.top + ";left:" +bounds.left + ";width:" + bounds.width +";height:" + bounds.height; + if (this.options.toOpacity) + style += ";opacity:" + this.options.toOpacity; + + new Effect.Morph(this.div ,{style: style, duration: this.options.duration, afterFinish: this._showWindow.bind(this)}); + }, + + hide: function(element, options) { + var position = Position.cumulativeOffset(this.html); + var size = this.html.getDimensions(); + this.window.visible = true; + var bounds = this.window.getBounds(); + this.window.visible = false; + + this.window.element.hide(); + + this.div.style.height = bounds.height; + this.div.style.width = bounds.width; + this.div.style.top = bounds.top; + this.div.style.left = bounds.left; + + if (this.options.toOpacity) + this.div.setStyle({opacity: this.options.toOpacity}) + + this.div.show(); + var style = "top:" + position[1] + "px;left:" + position[0] + "px;width:" + size.width +"px;height:" + size.height + "px"; + + if (this.options.fromOpacity) + style += ";opacity:" + this.options.fromOpacity; + new Effect.Morph(this.div ,{style: style, duration: this.options.duration, afterFinish: this._hideDiv.bind(this)}); + }, + + _showWindow: function() { + this.div.hide(); + this.window.element.show(); + }, + + _hideDiv: function() { + this.div.hide(); + } +} + diff --git a/xCAT-web/javascripts/window_ext.js b/xCAT-web/javascripts/window_ext.js new file mode 100644 index 000000000..60efac61b --- /dev/null +++ b/xCAT-web/javascripts/window_ext.js @@ -0,0 +1,115 @@ +// Copyright (c) 2006 Sébastien Gruhier (http://xilinus.com, http://itseb.com) +// YOU MUST INCLUDE window.js BEFORE +// +// Object to store hide/show windows status in a cookie +// Just add at the end of your HTML file this javascript line: WindowStore.init() +WindowStore = { + doSetCookie: false, + cookieName: "__window_store__", + expired: null, + + // Init function with two optional parameters + // - cookieName (default = __window_store__) + // - expiration date (default 3 years from now) + init: function(cookieName, expired) { + WindowStore.cookieName = cookieName || WindowStore.cookieName + + if (! expired) { + var today = new Date(); + today.setYear(today.getYear()+1903); + WindowStore.expired = today; + } + else + WindowStore.expired = expired; + + Windows.windows.each(function(win) { + win.setCookie(win.getId(), WindowStore.expired); + }); + + // Create observer on show/hide events + var myObserver = { + onShow: function(eventName, win) { + WindowStore._saveCookie(); + }, + + onClose: function(eventName, win) { + WindowStore._saveCookie(); + }, + + onHide: function(eventName, win) { + WindowStore._saveCookie(); + } + } + Windows.addObserver(myObserver); + + WindowStore._restoreWindows(); + WindowStore._saveCookie(); + }, + + show: function(win) { + eval("var cookie = " + WindowUtilities.getCookie(WindowStore.cookieName)); + if (cookie != null) { + if (cookie[win.getId()]) + win.show(); + } + else + win.show(); + }, + + // Function to store windows show/hide status in a cookie + _saveCookie: function() { + if (!doSetCookie) + return; + + var cookieValue = "{"; + Windows.windows.each(function(win) { + if (cookieValue != "{") + cookieValue += "," + cookieValue += win.getId() + ": " + win.isVisible(); + }); + cookieValue += "}" + + WindowUtilities.setCookie(cookieValue, [WindowStore.cookieName, WindowStore.expired]); + }, + + // Function to restore windows show/hide status from a cookie if exists + _restoreWindows: function() { + eval("var cookie = " + WindowUtilities.getCookie(WindowStore.cookieName)); + if (cookie != null) { + doSetCookie = false; + Windows.windows.each(function(win) { + if (cookie[win.getId()]) + win.show(); + }); + } + doSetCookie = true; + } +} + +// Object to set a close key an all windows +WindowCloseKey = { + keyCode: Event.KEY_ESC, + + init: function(keyCode) { + if (keyCode) + WindowCloseKey.keyCode = keyCode; + + Event.observe(document, 'keydown', this._closeCurrentWindow.bindAsEventListener(this)); + }, + + _closeCurrentWindow: function(event) { + var e = event || window.event + var characterCode = e.which || e.keyCode; + + // Check if there is a top window (it means it's an URL content) + var win = top.Windows.focusedWindow; + if (characterCode == WindowCloseKey.keyCode && win) { + if (win.cancelCallback) + top.Dialog.cancelCallback(); + else if (win.okCallback) + top.Dialog.okCallback(); + else + top.Windows.close(top.Windows.focusedWindow.getId()); + } + } +} \ No newline at end of file diff --git a/xCAT-web/js/builder.js b/xCAT-web/js/builder.js new file mode 100644 index 000000000..5b4ce87d2 --- /dev/null +++ b/xCAT-web/js/builder.js @@ -0,0 +1,136 @@ +// script.aculo.us builder.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +var Builder = { + NODEMAP: { + AREA: 'map', + CAPTION: 'table', + COL: 'table', + COLGROUP: 'table', + LEGEND: 'fieldset', + OPTGROUP: 'select', + OPTION: 'select', + PARAM: 'object', + TBODY: 'table', + TD: 'table', + TFOOT: 'table', + TH: 'table', + THEAD: 'table', + TR: 'table' + }, + // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, + // due to a Firefox bug + node: function(elementName) { + elementName = elementName.toUpperCase(); + + // try innerHTML approach + var parentTag = this.NODEMAP[elementName] || 'div'; + var parentElement = document.createElement(parentTag); + try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 + parentElement.innerHTML = "<" + elementName + ">"; + } catch(e) {} + var element = parentElement.firstChild || null; + + // see if browser added wrapping tags + if(element && (element.tagName.toUpperCase() != elementName)) + element = element.getElementsByTagName(elementName)[0]; + + // fallback to createElement approach + if(!element) element = document.createElement(elementName); + + // abort if nothing could be created + if(!element) return; + + // attributes (or text) + if(arguments[1]) + if(this._isStringOrNumber(arguments[1]) || + (arguments[1] instanceof Array) || + arguments[1].tagName) { + this._children(element, arguments[1]); + } else { + var attrs = this._attributes(arguments[1]); + if(attrs.length) { + try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 + parentElement.innerHTML = "<" +elementName + " " + + attrs + ">"; + } catch(e) {} + element = parentElement.firstChild || null; + // workaround firefox 1.0.X bug + if(!element) { + element = document.createElement(elementName); + for(attr in arguments[1]) + element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; + } + if(element.tagName.toUpperCase() != elementName) + element = parentElement.getElementsByTagName(elementName)[0]; + } + } + + // text, or array of children + if(arguments[2]) + this._children(element, arguments[2]); + + return element; + }, + _text: function(text) { + return document.createTextNode(text); + }, + + ATTR_MAP: { + 'className': 'class', + 'htmlFor': 'for' + }, + + _attributes: function(attributes) { + var attrs = []; + for(attribute in attributes) + attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) + + '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"'); + return attrs.join(" "); + }, + _children: function(element, children) { + if(children.tagName) { + element.appendChild(children); + return; + } + if(typeof children=='object') { // array can hold nodes and text + children.flatten().each( function(e) { + if(typeof e=='object') + element.appendChild(e) + else + if(Builder._isStringOrNumber(e)) + element.appendChild(Builder._text(e)); + }); + } else + if(Builder._isStringOrNumber(children)) + element.appendChild(Builder._text(children)); + }, + _isStringOrNumber: function(param) { + return(typeof param=='string' || typeof param=='number'); + }, + build: function(html) { + var element = this.node('div'); + $(element).update(html.strip()); + return element.down(); + }, + dump: function(scope) { + if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope + + var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " + + "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " + + "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+ + "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+ + "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+ + "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/); + + tags.each( function(tag){ + scope[tag] = function() { + return Builder.node.apply(Builder, [tag].concat($A(arguments))); + } + }); + } +} diff --git a/xCAT-web/js/clickTree.js b/xCAT-web/js/clickTree.js new file mode 100644 index 000000000..122ac26b7 --- /dev/null +++ b/xCAT-web/js/clickTree.js @@ -0,0 +1,129 @@ + + + +processHeader = function(head,ul) { + + + /* make the head icon different */ + for (i=0; i < head.childNodes.length; i++ ) { + if(head.childNodes[i].className = "nodeIcon"){ + head.childNodes[i].className = "nodeGroupIcon"; + break; + } + } + /* add the plus and expandibility */ + myBox = document.createElement('span'); + myBox.className = 'plus'; + myBox.innerHTML = " "; + myBox.onclick = function() { + this.className = (this.className == 'plus') ? 'minus' : 'plus'; + this.parentNode.parentNode.className = (this.parentNode.parentNode.className=='open') ? 'closed' : 'open'; + return false; + } + head.insertBefore(myBox, head.childNodes[0]); + head.parentNode.className = 'closed'; +} + +processItem = function(li, lili){ + myN = document.createElement('span'); + myIcon = document.createElement('span'); + myIcon.innerHTML = " "; + myCheck = document.createElement('span'); + myIcon.className = "nodeIcon"; + myCheck.className = "unchecked"; + myCheck.onclick = function (){ + this.className = (this.className=='checked') ? "unchecked" : "checked"; + //this.parentNode. + return false; + } + + /* create form element */ + myInput = document.createElement('input'); + myInput.type = 'checkbox'; + myInput.id = 'input'; + myInput.name = 'nodes[]'; + myInput.value = lili.nodeValue; + /* end create form element */ + li.insertBefore(myN, lili); + li.removeChild(lili); + // myCheck.appendChild(lili); + // myInput.appendChild(lili); + myN.appendChild(myIcon); + // myN.appendChild(myCheck); + myN.appendChild(myInput); + myN.appendChild(lili); + return myN; +} + + + +processList = function(ul) { + if (!ul.childNodes || ul.childNodes.length == 0) return; + isFirst = ''; + isLast = ''; + tempNode = ''; + for (var i=0; i < ul.childNodes.length; i++ ) { + li = ul.childNodes[i]; + if (li.nodeName == "LI") { + var subUL = ''; + var head = ''; + for(j = 0; j < li.childNodes.length; j++) { + lili = li.childNodes[j]; + switch (lili.nodeName) { + case "#text": + myN = processItem(li, lili); + head = myN; + break; + case "UL": + subUL = lili; + processList(lili); + break; + default: + // other items may be a span. + // alert("exception:" + lili.nodeName); + break; + } + } + if (subUL) { + processHeader(head,subUL); + } else { + //alert('cl: ' + (ul.childNodes.length - 1)); + if(isFirst == ''){ + li.className = 'firstItem'; + isFirst = 1; + } + else { + if(isLast == ''){ + li.className = "lastItem"; + tempNode = li; + isLast = 1; + }else{ + tempNode.className = "middleItem"; + li.className = "lastItem"; + tempNode = li; + } + } + } + } + } +} + +makeTree = function(el) { + // see if we can create an element + if (!document.createElement) return; + ul = document.getElementById(el); + if(!ul){ + return; + } + ul.className = "clickTree"; + processList(ul); +} + +function closeTree(){ + var el = document.getElementsByClassName('minus'); + for(var i = 0; i< el.length; i++){ + el[i].className = 'plus'; + el[i].parentNode.parentNode.className = 'closed'; + } +} + diff --git a/xCAT-web/js/controls.js b/xCAT-web/js/controls.js new file mode 100644 index 000000000..6783bd0a6 --- /dev/null +++ b/xCAT-web/js/controls.js @@ -0,0 +1,875 @@ +// script.aculo.us controls.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com) +// Contributors: +// Richard Livsey +// Rahul Bhargava +// Rob Wills +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +// Autocompleter.Base handles all the autocompletion functionality +// that's independent of the data source for autocompletion. This +// includes drawing the autocompletion menu, observing keyboard +// and mouse events, and similar. +// +// Specific autocompleters need to provide, at the very least, +// a getUpdatedChoices function that will be invoked every time +// the text inside the monitored textbox changes. This method +// should get the text for which to provide autocompletion by +// invoking this.getToken(), NOT by directly accessing +// this.element.value. This is to allow incremental tokenized +// autocompletion. Specific auto-completion logic (AJAX, etc) +// belongs in getUpdatedChoices. +// +// Tokenized incremental autocompletion is enabled automatically +// when an autocompleter is instantiated with the 'tokens' option +// in the options parameter, e.g.: +// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); +// will incrementally autocomplete with a comma as the token. +// Additionally, ',' in the above example can be replaced with +// a token array, e.g. { tokens: [',', '\n'] } which +// enables autocompletion on multiple tokens. This is most +// useful when one of the tokens is \n (a newline), as it +// allows smart autocompletion after linebreaks. + +if(typeof Effect == 'undefined') + throw("controls.js requires including script.aculo.us' effects.js library"); + +var Autocompleter = {} +Autocompleter.Base = function() {}; +Autocompleter.Base.prototype = { + baseInitialize: function(element, update, options) { + element = $(element) + this.element = element; + this.update = $(update); + this.hasFocus = false; + this.changed = false; + this.active = false; + this.index = 0; + this.entryCount = 0; + + if(this.setOptions) + this.setOptions(options); + else + this.options = options || {}; + + this.options.paramName = this.options.paramName || this.element.name; + this.options.tokens = this.options.tokens || []; + this.options.frequency = this.options.frequency || 0.4; + this.options.minChars = this.options.minChars || 1; + this.options.onShow = this.options.onShow || + function(element, update){ + if(!update.style.position || update.style.position=='absolute') { + update.style.position = 'absolute'; + Position.clone(element, update, { + setHeight: false, + offsetTop: element.offsetHeight + }); + } + Effect.Appear(update,{duration:0.15}); + }; + this.options.onHide = this.options.onHide || + function(element, update){ new Effect.Fade(update,{duration:0.15}) }; + + if(typeof(this.options.tokens) == 'string') + this.options.tokens = new Array(this.options.tokens); + + this.observer = null; + + this.element.setAttribute('autocomplete','off'); + + Element.hide(this.update); + + Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this)); + Event.observe(this.element, 'keypress', this.onKeyPress.bindAsEventListener(this)); + + // Turn autocomplete back on when the user leaves the page, so that the + // field's value will be remembered on Mozilla-based browsers. + Event.observe(window, 'beforeunload', function(){ + element.setAttribute('autocomplete', 'on'); + }); + }, + + show: function() { + if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); + if(!this.iefix && + (Prototype.Browser.IE) && + (Element.getStyle(this.update, 'position')=='absolute')) { + new Insertion.After(this.update, + ''); + this.iefix = $(this.update.id+'_iefix'); + } + if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); + }, + + fixIEOverlapping: function() { + Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); + this.iefix.style.zIndex = 1; + this.update.style.zIndex = 2; + Element.show(this.iefix); + }, + + hide: function() { + this.stopIndicator(); + if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); + if(this.iefix) Element.hide(this.iefix); + }, + + startIndicator: function() { + if(this.options.indicator) Element.show(this.options.indicator); + }, + + stopIndicator: function() { + if(this.options.indicator) Element.hide(this.options.indicator); + }, + + onKeyPress: function(event) { + if(this.active) + switch(event.keyCode) { + case Event.KEY_TAB: + case Event.KEY_RETURN: + this.selectEntry(); + Event.stop(event); + case Event.KEY_ESC: + this.hide(); + this.active = false; + Event.stop(event); + return; + case Event.KEY_LEFT: + case Event.KEY_RIGHT: + return; + case Event.KEY_UP: + this.markPrevious(); + this.render(); + if(Prototype.Browser.WebKit) Event.stop(event); + return; + case Event.KEY_DOWN: + this.markNext(); + this.render(); + if(Prototype.Browser.WebKit) Event.stop(event); + return; + } + else + if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || + (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; + + this.changed = true; + this.hasFocus = true; + + if(this.observer) clearTimeout(this.observer); + this.observer = + setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); + }, + + activate: function() { + this.changed = false; + this.hasFocus = true; + this.getUpdatedChoices(); + }, + + onHover: function(event) { + var element = Event.findElement(event, 'LI'); + if(this.index != element.autocompleteIndex) + { + this.index = element.autocompleteIndex; + this.render(); + } + Event.stop(event); + }, + + onClick: function(event) { + var element = Event.findElement(event, 'LI'); + this.index = element.autocompleteIndex; + this.selectEntry(); + this.hide(); + }, + + onBlur: function(event) { + // needed to make click events working + setTimeout(this.hide.bind(this), 250); + this.hasFocus = false; + this.active = false; + }, + + render: function() { + if(this.entryCount > 0) { + for (var i = 0; i < this.entryCount; i++) + this.index==i ? + Element.addClassName(this.getEntry(i),"selected") : + Element.removeClassName(this.getEntry(i),"selected"); + if(this.hasFocus) { + this.show(); + this.active = true; + } + } else { + this.active = false; + this.hide(); + } + }, + + markPrevious: function() { + if(this.index > 0) this.index-- + else this.index = this.entryCount-1; + this.getEntry(this.index).scrollIntoView(true); + }, + + markNext: function() { + if(this.index < this.entryCount-1) this.index++ + else this.index = 0; + this.getEntry(this.index).scrollIntoView(false); + }, + + getEntry: function(index) { + return this.update.firstChild.childNodes[index]; + }, + + getCurrentEntry: function() { + return this.getEntry(this.index); + }, + + selectEntry: function() { + this.active = false; + this.updateElement(this.getCurrentEntry()); + }, + + updateElement: function(selectedElement) { + if (this.options.updateElement) { + this.options.updateElement(selectedElement); + return; + } + var value = ''; + if (this.options.select) { + var nodes = document.getElementsByClassName(this.options.select, selectedElement) || []; + if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); + } else + value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); + + var lastTokenPos = this.findLastToken(); + if (lastTokenPos != -1) { + var newValue = this.element.value.substr(0, lastTokenPos + 1); + var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/); + if (whitespace) + newValue += whitespace[0]; + this.element.value = newValue + value; + } else { + this.element.value = value; + } + this.element.focus(); + + if (this.options.afterUpdateElement) + this.options.afterUpdateElement(this.element, selectedElement); + }, + + updateChoices: function(choices) { + if(!this.changed && this.hasFocus) { + this.update.innerHTML = choices; + Element.cleanWhitespace(this.update); + Element.cleanWhitespace(this.update.down()); + + if(this.update.firstChild && this.update.down().childNodes) { + this.entryCount = + this.update.down().childNodes.length; + for (var i = 0; i < this.entryCount; i++) { + var entry = this.getEntry(i); + entry.autocompleteIndex = i; + this.addObservers(entry); + } + } else { + this.entryCount = 0; + } + + this.stopIndicator(); + this.index = 0; + + if(this.entryCount==1 && this.options.autoSelect) { + this.selectEntry(); + this.hide(); + } else { + this.render(); + } + } + }, + + addObservers: function(element) { + Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); + Event.observe(element, "click", this.onClick.bindAsEventListener(this)); + }, + + onObserverEvent: function() { + this.changed = false; + if(this.getToken().length>=this.options.minChars) { + this.getUpdatedChoices(); + } else { + this.active = false; + this.hide(); + } + }, + + getToken: function() { + var tokenPos = this.findLastToken(); + if (tokenPos != -1) + var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,''); + else + var ret = this.element.value; + + return /\n/.test(ret) ? '' : ret; + }, + + findLastToken: function() { + var lastTokenPos = -1; + + for (var i=0; i lastTokenPos) + lastTokenPos = thisTokenPos; + } + return lastTokenPos; + } +} + +Ajax.Autocompleter = Class.create(); +Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { + initialize: function(element, update, url, options) { + this.baseInitialize(element, update, options); + this.options.asynchronous = true; + this.options.onComplete = this.onComplete.bind(this); + this.options.defaultParams = this.options.parameters || null; + this.url = url; + }, + + getUpdatedChoices: function() { + this.startIndicator(); + + var entry = encodeURIComponent(this.options.paramName) + '=' + + encodeURIComponent(this.getToken()); + + this.options.parameters = this.options.callback ? + this.options.callback(this.element, entry) : entry; + + if(this.options.defaultParams) + this.options.parameters += '&' + this.options.defaultParams; + + new Ajax.Request(this.url, this.options); + }, + + onComplete: function(request) { + this.updateChoices(request.responseText); + } + +}); + +// The local array autocompleter. Used when you'd prefer to +// inject an array of autocompletion options into the page, rather +// than sending out Ajax queries, which can be quite slow sometimes. +// +// The constructor takes four parameters. The first two are, as usual, +// the id of the monitored textbox, and id of the autocompletion menu. +// The third is the array you want to autocomplete from, and the fourth +// is the options block. +// +// Extra local autocompletion options: +// - choices - How many autocompletion choices to offer +// +// - partialSearch - If false, the autocompleter will match entered +// text only at the beginning of strings in the +// autocomplete array. Defaults to true, which will +// match text at the beginning of any *word* in the +// strings in the autocomplete array. If you want to +// search anywhere in the string, additionally set +// the option fullSearch to true (default: off). +// +// - fullSsearch - Search anywhere in autocomplete array strings. +// +// - partialChars - How many characters to enter before triggering +// a partial match (unlike minChars, which defines +// how many characters are required to do any match +// at all). Defaults to 2. +// +// - ignoreCase - Whether to ignore case when autocompleting. +// Defaults to true. +// +// It's possible to pass in a custom function as the 'selector' +// option, if you prefer to write your own autocompletion logic. +// In that case, the other options above will not apply unless +// you support them. + +Autocompleter.Local = Class.create(); +Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { + initialize: function(element, update, array, options) { + this.baseInitialize(element, update, options); + this.options.array = array; + }, + + getUpdatedChoices: function() { + this.updateChoices(this.options.selector(this)); + }, + + setOptions: function(options) { + this.options = Object.extend({ + choices: 10, + partialSearch: true, + partialChars: 2, + ignoreCase: true, + fullSearch: false, + selector: function(instance) { + var ret = []; // Beginning matches + var partial = []; // Inside matches + var entry = instance.getToken(); + var count = 0; + + for (var i = 0; i < instance.options.array.length && + ret.length < instance.options.choices ; i++) { + + var elem = instance.options.array[i]; + var foundPos = instance.options.ignoreCase ? + elem.toLowerCase().indexOf(entry.toLowerCase()) : + elem.indexOf(entry); + + while (foundPos != -1) { + if (foundPos == 0 && elem.length != entry.length) { + ret.push("
  • " + elem.substr(0, entry.length) + "" + + elem.substr(entry.length) + "
  • "); + break; + } else if (entry.length >= instance.options.partialChars && + instance.options.partialSearch && foundPos != -1) { + if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { + partial.push("
  • " + elem.substr(0, foundPos) + "" + + elem.substr(foundPos, entry.length) + "" + elem.substr( + foundPos + entry.length) + "
  • "); + break; + } + } + + foundPos = instance.options.ignoreCase ? + elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : + elem.indexOf(entry, foundPos + 1); + + } + } + if (partial.length) + ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) + return "
      " + ret.join('') + "
    "; + } + }, options || {}); + } +}); + +// AJAX in-place editor +// +// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor + +// Use this if you notice weird scrolling problems on some browsers, +// the DOM might be a bit confused when this gets called so do this +// waits 1 ms (with setTimeout) until it does the activation +Field.scrollFreeActivate = function(field) { + setTimeout(function() { + Field.activate(field); + }, 1); +} + +Ajax.InPlaceEditor = Class.create(); +Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; +Ajax.InPlaceEditor.prototype = { + initialize: function(element, url, options) { + this.url = url; + this.element = $(element); + + this.options = Object.extend({ + paramName: "value", + okButton: true, + okLink: false, + okText: "ok", + cancelButton: false, + cancelLink: true, + cancelText: "cancel", + textBeforeControls: '', + textBetweenControls: '', + textAfterControls: '', + savingText: "Saving...", + clickToEditText: "Click to edit", + okText: "ok", + rows: 1, + onComplete: function(transport, element) { + new Effect.Highlight(element, {startcolor: this.options.highlightcolor}); + }, + onFailure: function(transport) { + alert("Error communicating with the server: " + transport.responseText.stripTags()); + }, + callback: function(form) { + return Form.serialize(form); + }, + handleLineBreaks: true, + loadingText: 'Loading...', + savingClassName: 'inplaceeditor-saving', + loadingClassName: 'inplaceeditor-loading', + formClassName: 'inplaceeditor-form', + highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, + highlightendcolor: "#FFFFFF", + externalControl: null, + submitOnBlur: false, + ajaxOptions: {}, + evalScripts: false + }, options || {}); + + if(!this.options.formId && this.element.id) { + this.options.formId = this.element.id + "-inplaceeditor"; + if ($(this.options.formId)) { + // there's already a form with that name, don't specify an id + this.options.formId = null; + } + } + + if (this.options.externalControl) { + this.options.externalControl = $(this.options.externalControl); + } + + this.originalBackground = Element.getStyle(this.element, 'background-color'); + if (!this.originalBackground) { + this.originalBackground = "transparent"; + } + + this.element.title = this.options.clickToEditText; + + this.onclickListener = this.enterEditMode.bindAsEventListener(this); + this.mouseoverListener = this.enterHover.bindAsEventListener(this); + this.mouseoutListener = this.leaveHover.bindAsEventListener(this); + Event.observe(this.element, 'click', this.onclickListener); + Event.observe(this.element, 'mouseover', this.mouseoverListener); + Event.observe(this.element, 'mouseout', this.mouseoutListener); + if (this.options.externalControl) { + Event.observe(this.options.externalControl, 'click', this.onclickListener); + Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener); + Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); + } + }, + enterEditMode: function(evt) { + if (this.saving) return; + if (this.editing) return; + this.editing = true; + this.onEnterEditMode(); + if (this.options.externalControl) { + Element.hide(this.options.externalControl); + } + Element.hide(this.element); + this.createForm(); + this.element.parentNode.insertBefore(this.form, this.element); + if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField); + // stop the event to avoid a page refresh in Safari + if (evt) { + Event.stop(evt); + } + return false; + }, + createForm: function() { + this.form = document.createElement("form"); + this.form.id = this.options.formId; + Element.addClassName(this.form, this.options.formClassName) + this.form.onsubmit = this.onSubmit.bind(this); + + this.createEditField(); + + if (this.options.textarea) { + var br = document.createElement("br"); + this.form.appendChild(br); + } + + if (this.options.textBeforeControls) + this.form.appendChild(document.createTextNode(this.options.textBeforeControls)); + + if (this.options.okButton) { + var okButton = document.createElement("input"); + okButton.type = "submit"; + okButton.value = this.options.okText; + okButton.className = 'editor_ok_button'; + this.form.appendChild(okButton); + } + + if (this.options.okLink) { + var okLink = document.createElement("a"); + okLink.href = "#"; + okLink.appendChild(document.createTextNode(this.options.okText)); + okLink.onclick = this.onSubmit.bind(this); + okLink.className = 'editor_ok_link'; + this.form.appendChild(okLink); + } + + if (this.options.textBetweenControls && + (this.options.okLink || this.options.okButton) && + (this.options.cancelLink || this.options.cancelButton)) + this.form.appendChild(document.createTextNode(this.options.textBetweenControls)); + + if (this.options.cancelButton) { + var cancelButton = document.createElement("input"); + cancelButton.type = "submit"; + cancelButton.value = this.options.cancelText; + cancelButton.onclick = this.onclickCancel.bind(this); + cancelButton.className = 'editor_cancel_button'; + this.form.appendChild(cancelButton); + } + + if (this.options.cancelLink) { + var cancelLink = document.createElement("a"); + cancelLink.href = "#"; + cancelLink.appendChild(document.createTextNode(this.options.cancelText)); + cancelLink.onclick = this.onclickCancel.bind(this); + cancelLink.className = 'editor_cancel editor_cancel_link'; + this.form.appendChild(cancelLink); + } + + if (this.options.textAfterControls) + this.form.appendChild(document.createTextNode(this.options.textAfterControls)); + }, + hasHTMLLineBreaks: function(string) { + if (!this.options.handleLineBreaks) return false; + return string.match(/
    /i); + }, + convertHTMLLineBreaks: function(string) { + return string.replace(/
    /gi, "\n").replace(//gi, "\n").replace(/<\/p>/gi, "\n").replace(/

    /gi, ""); + }, + createEditField: function() { + var text; + if(this.options.loadTextURL) { + text = this.options.loadingText; + } else { + text = this.getText(); + } + + var obj = this; + + if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { + this.options.textarea = false; + var textField = document.createElement("input"); + textField.obj = this; + textField.type = "text"; + textField.name = this.options.paramName; + textField.value = text; + textField.style.backgroundColor = this.options.highlightcolor; + textField.className = 'editor_field'; + var size = this.options.size || this.options.cols || 0; + if (size != 0) textField.size = size; + if (this.options.submitOnBlur) + textField.onblur = this.onSubmit.bind(this); + this.editField = textField; + } else { + this.options.textarea = true; + var textArea = document.createElement("textarea"); + textArea.obj = this; + textArea.name = this.options.paramName; + textArea.value = this.convertHTMLLineBreaks(text); + textArea.rows = this.options.rows; + textArea.cols = this.options.cols || 40; + textArea.className = 'editor_field'; + if (this.options.submitOnBlur) + textArea.onblur = this.onSubmit.bind(this); + this.editField = textArea; + } + + if(this.options.loadTextURL) { + this.loadExternalText(); + } + this.form.appendChild(this.editField); + }, + getText: function() { + return this.element.innerHTML; + }, + loadExternalText: function() { + Element.addClassName(this.form, this.options.loadingClassName); + this.editField.disabled = true; + new Ajax.Request( + this.options.loadTextURL, + Object.extend({ + asynchronous: true, + onComplete: this.onLoadedExternalText.bind(this) + }, this.options.ajaxOptions) + ); + }, + onLoadedExternalText: function(transport) { + Element.removeClassName(this.form, this.options.loadingClassName); + this.editField.disabled = false; + this.editField.value = transport.responseText.stripTags(); + Field.scrollFreeActivate(this.editField); + }, + onclickCancel: function() { + this.onComplete(); + this.leaveEditMode(); + return false; + }, + onFailure: function(transport) { + this.options.onFailure(transport); + if (this.oldInnerHTML) { + this.element.innerHTML = this.oldInnerHTML; + this.oldInnerHTML = null; + } + return false; + }, + onSubmit: function() { + // onLoading resets these so we need to save them away for the Ajax call + var form = this.form; + var value = this.editField.value; + + // do this first, sometimes the ajax call returns before we get a chance to switch on Saving... + // which means this will actually switch on Saving... *after* we've left edit mode causing Saving... + // to be displayed indefinitely + this.onLoading(); + + if (this.options.evalScripts) { + new Ajax.Request( + this.url, Object.extend({ + parameters: this.options.callback(form, value), + onComplete: this.onComplete.bind(this), + onFailure: this.onFailure.bind(this), + asynchronous:true, + evalScripts:true + }, this.options.ajaxOptions)); + } else { + new Ajax.Updater( + { success: this.element, + // don't update on failure (this could be an option) + failure: null }, + this.url, Object.extend({ + parameters: this.options.callback(form, value), + onComplete: this.onComplete.bind(this), + onFailure: this.onFailure.bind(this) + }, this.options.ajaxOptions)); + } + // stop the event to avoid a page refresh in Safari + if (arguments.length > 1) { + Event.stop(arguments[0]); + } + return false; + }, + onLoading: function() { + this.saving = true; + this.removeForm(); + this.leaveHover(); + this.showSaving(); + }, + showSaving: function() { + this.oldInnerHTML = this.element.innerHTML; + this.element.innerHTML = this.options.savingText; + Element.addClassName(this.element, this.options.savingClassName); + this.element.style.backgroundColor = this.originalBackground; + Element.show(this.element); + }, + removeForm: function() { + if(this.form) { + if (this.form.parentNode) Element.remove(this.form); + this.form = null; + } + }, + enterHover: function() { + if (this.saving) return; + this.element.style.backgroundColor = this.options.highlightcolor; + if (this.effect) { + this.effect.cancel(); + } + Element.addClassName(this.element, this.options.hoverClassName) + }, + leaveHover: function() { + if (this.options.backgroundColor) { + this.element.style.backgroundColor = this.oldBackground; + } + Element.removeClassName(this.element, this.options.hoverClassName) + if (this.saving) return; + this.effect = new Effect.Highlight(this.element, { + startcolor: this.options.highlightcolor, + endcolor: this.options.highlightendcolor, + restorecolor: this.originalBackground + }); + }, + leaveEditMode: function() { + Element.removeClassName(this.element, this.options.savingClassName); + this.removeForm(); + this.leaveHover(); + this.element.style.backgroundColor = this.originalBackground; + Element.show(this.element); + if (this.options.externalControl) { + Element.show(this.options.externalControl); + } + this.editing = false; + this.saving = false; + this.oldInnerHTML = null; + this.onLeaveEditMode(); + }, + onComplete: function(transport) { + this.leaveEditMode(); + this.options.onComplete.bind(this)(transport, this.element); + }, + onEnterEditMode: function() {}, + onLeaveEditMode: function() {}, + dispose: function() { + if (this.oldInnerHTML) { + this.element.innerHTML = this.oldInnerHTML; + } + this.leaveEditMode(); + Event.stopObserving(this.element, 'click', this.onclickListener); + Event.stopObserving(this.element, 'mouseover', this.mouseoverListener); + Event.stopObserving(this.element, 'mouseout', this.mouseoutListener); + if (this.options.externalControl) { + Event.stopObserving(this.options.externalControl, 'click', this.onclickListener); + Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener); + Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener); + } + } +}; + +Ajax.InPlaceCollectionEditor = Class.create(); +Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype); +Object.extend(Ajax.InPlaceCollectionEditor.prototype, { + createEditField: function() { + if (!this.cached_selectTag) { + var selectTag = document.createElement("select"); + var collection = this.options.collection || []; + var optionTag; + collection.each(function(e,i) { + optionTag = document.createElement("option"); + optionTag.value = (e instanceof Array) ? e[0] : e; + if((typeof this.options.value == 'undefined') && + ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true; + if(this.options.value==optionTag.value) optionTag.selected = true; + optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e)); + selectTag.appendChild(optionTag); + }.bind(this)); + this.cached_selectTag = selectTag; + } + + this.editField = this.cached_selectTag; + if(this.options.loadTextURL) this.loadExternalText(); + this.form.appendChild(this.editField); + this.options.callback = function(form, value) { + return "value=" + encodeURIComponent(value); + } + } +}); + +// Delayed observer, like Form.Element.Observer, +// but waits for delay after last key input +// Ideal for live-search fields + +Form.Element.DelayedObserver = Class.create(); +Form.Element.DelayedObserver.prototype = { + initialize: function(element, delay, callback) { + this.delay = delay || 0.5; + this.element = $(element); + this.callback = callback; + this.timer = null; + this.lastValue = $F(this.element); + Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); + }, + delayedListener: function(event) { + if(this.lastValue == $F(this.element)) return; + if(this.timer) clearTimeout(this.timer); + this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); + this.lastValue = $F(this.element); + }, + onTimerEvent: function() { + this.timer = null; + this.callback(this.element, $F(this.element)); + } +}; diff --git a/xCAT-web/js/dragdrop.js b/xCAT-web/js/dragdrop.js new file mode 100644 index 000000000..108dd6681 --- /dev/null +++ b/xCAT-web/js/dragdrop.js @@ -0,0 +1,970 @@ +// script.aculo.us dragdrop.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +if(typeof Effect == 'undefined') + throw("dragdrop.js requires including script.aculo.us' effects.js library"); + +var Droppables = { + drops: [], + + remove: function(element) { + this.drops = this.drops.reject(function(d) { return d.element==$(element) }); + }, + + add: function(element) { + element = $(element); + var options = Object.extend({ + greedy: true, + hoverclass: null, + tree: false + }, arguments[1] || {}); + + // cache containers + if(options.containment) { + options._containers = []; + var containment = options.containment; + if((typeof containment == 'object') && + (containment.constructor == Array)) { + containment.each( function(c) { options._containers.push($(c)) }); + } else { + options._containers.push($(containment)); + } + } + + if(options.accept) options.accept = [options.accept].flatten(); + + Element.makePositioned(element); // fix IE + options.element = element; + + this.drops.push(options); + }, + + findDeepestChild: function(drops) { + deepest = drops[0]; + + for (i = 1; i < drops.length; ++i) + if (Element.isParent(drops[i].element, deepest.element)) + deepest = drops[i]; + + return deepest; + }, + + isContained: function(element, drop) { + var containmentNode; + if(drop.tree) { + containmentNode = element.treeNode; + } else { + containmentNode = element.parentNode; + } + return drop._containers.detect(function(c) { return containmentNode == c }); + }, + + isAffected: function(point, element, drop) { + return ( + (drop.element!=element) && + ((!drop._containers) || + this.isContained(element, drop)) && + ((!drop.accept) || + (Element.classNames(element).detect( + function(v) { return drop.accept.include(v) } ) )) && + Position.within(drop.element, point[0], point[1]) ); + }, + + deactivate: function(drop) { + if(drop.hoverclass) + Element.removeClassName(drop.element, drop.hoverclass); + this.last_active = null; + }, + + activate: function(drop) { + if(drop.hoverclass) + Element.addClassName(drop.element, drop.hoverclass); + this.last_active = drop; + }, + + show: function(point, element) { + if(!this.drops.length) return; + var affected = []; + + if(this.last_active) this.deactivate(this.last_active); + this.drops.each( function(drop) { + if(Droppables.isAffected(point, element, drop)) + affected.push(drop); + }); + + if(affected.length>0) { + drop = Droppables.findDeepestChild(affected); + Position.within(drop.element, point[0], point[1]); + if(drop.onHover) + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); + + Droppables.activate(drop); + } + }, + + fire: function(event, element) { + if(!this.last_active) return; + Position.prepare(); + + if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) + if (this.last_active.onDrop) { + this.last_active.onDrop(element, this.last_active.element, event); + return true; + } + }, + + reset: function() { + if(this.last_active) + this.deactivate(this.last_active); + } +} + +var Draggables = { + drags: [], + observers: [], + + register: function(draggable) { + if(this.drags.length == 0) { + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.updateDrag.bindAsEventListener(this); + this.eventKeypress = this.keyPress.bindAsEventListener(this); + + Event.observe(document, "mouseup", this.eventMouseUp); + Event.observe(document, "mousemove", this.eventMouseMove); + Event.observe(document, "keypress", this.eventKeypress); + } + this.drags.push(draggable); + }, + + unregister: function(draggable) { + this.drags = this.drags.reject(function(d) { return d==draggable }); + if(this.drags.length == 0) { + Event.stopObserving(document, "mouseup", this.eventMouseUp); + Event.stopObserving(document, "mousemove", this.eventMouseMove); + Event.stopObserving(document, "keypress", this.eventKeypress); + } + }, + + activate: function(draggable) { + if(draggable.options.delay) { + this._timeout = setTimeout(function() { + Draggables._timeout = null; + window.focus(); + Draggables.activeDraggable = draggable; + }.bind(this), draggable.options.delay); + } else { + window.focus(); // allows keypress events if window isn't currently focused, fails for Safari + this.activeDraggable = draggable; + } + }, + + deactivate: function() { + this.activeDraggable = null; + }, + + updateDrag: function(event) { + if(!this.activeDraggable) return; + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + // Mozilla-based browsers fire successive mousemove events with + // the same coordinates, prevent needless redrawing (moz bug?) + if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; + this._lastPointer = pointer; + + this.activeDraggable.updateDrag(event, pointer); + }, + + endDrag: function(event) { + if(this._timeout) { + clearTimeout(this._timeout); + this._timeout = null; + } + if(!this.activeDraggable) return; + this._lastPointer = null; + this.activeDraggable.endDrag(event); + this.activeDraggable = null; + }, + + keyPress: function(event) { + if(this.activeDraggable) + this.activeDraggable.keyPress(event); + }, + + addObserver: function(observer) { + this.observers.push(observer); + this._cacheObserverCallbacks(); + }, + + removeObserver: function(element) { // element instead of observer fixes mem leaks + this.observers = this.observers.reject( function(o) { return o.element==element }); + this._cacheObserverCallbacks(); + }, + + notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' + if(this[eventName+'Count'] > 0) + this.observers.each( function(o) { + if(o[eventName]) o[eventName](eventName, draggable, event); + }); + if(draggable.options[eventName]) draggable.options[eventName](draggable, event); + }, + + _cacheObserverCallbacks: function() { + ['onStart','onEnd','onDrag'].each( function(eventName) { + Draggables[eventName+'Count'] = Draggables.observers.select( + function(o) { return o[eventName]; } + ).length; + }); + } +} + +/*--------------------------------------------------------------------------*/ + +var Draggable = Class.create(); +Draggable._dragging = {}; + +Draggable.prototype = { + initialize: function(element) { + var defaults = { + handle: false, + reverteffect: function(element, top_offset, left_offset) { + var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; + new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, + queue: {scope:'_draggable', position:'end'} + }); + }, + endeffect: function(element) { + var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0; + new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, + queue: {scope:'_draggable', position:'end'}, + afterFinish: function(){ + Draggable._dragging[element] = false + } + }); + }, + zindex: 1000, + revert: false, + quiet: false, + scroll: false, + scrollSensitivity: 20, + scrollSpeed: 15, + snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } + delay: 0 + }; + + if(!arguments[1] || typeof arguments[1].endeffect == 'undefined') + Object.extend(defaults, { + starteffect: function(element) { + element._opacity = Element.getOpacity(element); + Draggable._dragging[element] = true; + new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); + } + }); + + var options = Object.extend(defaults, arguments[1] || {}); + + this.element = $(element); + + if(options.handle && (typeof options.handle == 'string')) + this.handle = this.element.down('.'+options.handle, 0); + + if(!this.handle) this.handle = $(options.handle); + if(!this.handle) this.handle = this.element; + + if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { + options.scroll = $(options.scroll); + this._isScrollChild = Element.childOf(this.element, options.scroll); + } + + Element.makePositioned(this.element); // fix IE + + this.delta = this.currentDelta(); + this.options = options; + this.dragging = false; + + this.eventMouseDown = this.initDrag.bindAsEventListener(this); + Event.observe(this.handle, "mousedown", this.eventMouseDown); + + Draggables.register(this); + }, + + destroy: function() { + Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); + Draggables.unregister(this); + }, + + currentDelta: function() { + return([ + parseInt(Element.getStyle(this.element,'left') || '0'), + parseInt(Element.getStyle(this.element,'top') || '0')]); + }, + + initDrag: function(event) { + if(typeof Draggable._dragging[this.element] != 'undefined' && + Draggable._dragging[this.element]) return; + if(Event.isLeftClick(event)) { + // abort on form elements, fixes a Firefox issue + var src = Event.element(event); + if((tag_name = src.tagName.toUpperCase()) && ( + tag_name=='INPUT' || + tag_name=='SELECT' || + tag_name=='OPTION' || + tag_name=='BUTTON' || + tag_name=='TEXTAREA')) return; + + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var pos = Position.cumulativeOffset(this.element); + this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); + + Draggables.activate(this); + Event.stop(event); + } + }, + + startDrag: function(event) { + this.dragging = true; + + if(this.options.zindex) { + this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); + this.element.style.zIndex = this.options.zindex; + } + + if(this.options.ghosting) { + this._clone = this.element.cloneNode(true); + Position.absolutize(this.element); + this.element.parentNode.insertBefore(this._clone, this.element); + } + + if(this.options.scroll) { + if (this.options.scroll == window) { + var where = this._getWindowScroll(this.options.scroll); + this.originalScrollLeft = where.left; + this.originalScrollTop = where.top; + } else { + this.originalScrollLeft = this.options.scroll.scrollLeft; + this.originalScrollTop = this.options.scroll.scrollTop; + } + } + + Draggables.notify('onStart', this, event); + + if(this.options.starteffect) this.options.starteffect(this.element); + }, + + updateDrag: function(event, pointer) { + if(!this.dragging) this.startDrag(event); + + if(!this.options.quiet){ + Position.prepare(); + Droppables.show(pointer, this.element); + } + + Draggables.notify('onDrag', this, event); + + this.draw(pointer); + if(this.options.change) this.options.change(this); + + if(this.options.scroll) { + this.stopScrolling(); + + var p; + if (this.options.scroll == window) { + with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } + } else { + p = Position.page(this.options.scroll); + p[0] += this.options.scroll.scrollLeft + Position.deltaX; + p[1] += this.options.scroll.scrollTop + Position.deltaY; + p.push(p[0]+this.options.scroll.offsetWidth); + p.push(p[1]+this.options.scroll.offsetHeight); + } + var speed = [0,0]; + if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); + if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); + if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); + if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); + this.startScrolling(speed); + } + + // fix AppleWebKit rendering + if(Prototype.Browser.WebKit) window.scrollBy(0,0); + + Event.stop(event); + }, + + finishDrag: function(event, success) { + this.dragging = false; + + if(this.options.quiet){ + Position.prepare(); + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + Droppables.show(pointer, this.element); + } + + if(this.options.ghosting) { + Position.relativize(this.element); + Element.remove(this._clone); + this._clone = null; + } + + var dropped = false; + if(success) { + dropped = Droppables.fire(event, this.element); + if (!dropped) dropped = false; + } + if(dropped && this.options.onDropped) this.options.onDropped(this.element); + Draggables.notify('onEnd', this, event); + + var revert = this.options.revert; + if(revert && typeof revert == 'function') revert = revert(this.element); + + var d = this.currentDelta(); + if(revert && this.options.reverteffect) { + if (dropped == 0 || revert != 'failure') + this.options.reverteffect(this.element, + d[1]-this.delta[1], d[0]-this.delta[0]); + } else { + this.delta = d; + } + + if(this.options.zindex) + this.element.style.zIndex = this.originalZ; + + if(this.options.endeffect) + this.options.endeffect(this.element); + + Draggables.deactivate(this); + Droppables.reset(); + }, + + keyPress: function(event) { + if(event.keyCode!=Event.KEY_ESC) return; + this.finishDrag(event, false); + Event.stop(event); + }, + + endDrag: function(event) { + if(!this.dragging) return; + this.stopScrolling(); + this.finishDrag(event, true); + Event.stop(event); + }, + + draw: function(point) { + var pos = Position.cumulativeOffset(this.element); + if(this.options.ghosting) { + var r = Position.realOffset(this.element); + pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; + } + + var d = this.currentDelta(); + pos[0] -= d[0]; pos[1] -= d[1]; + + if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { + pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; + pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; + } + + var p = [0,1].map(function(i){ + return (point[i]-pos[i]-this.offset[i]) + }.bind(this)); + + if(this.options.snap) { + if(typeof this.options.snap == 'function') { + p = this.options.snap(p[0],p[1],this); + } else { + if(this.options.snap instanceof Array) { + p = p.map( function(v, i) { + return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this)) + } else { + p = p.map( function(v) { + return Math.round(v/this.options.snap)*this.options.snap }.bind(this)) + } + }} + + var style = this.element.style; + if((!this.options.constraint) || (this.options.constraint=='horizontal')) + style.left = p[0] + "px"; + if((!this.options.constraint) || (this.options.constraint=='vertical')) + style.top = p[1] + "px"; + + if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering + }, + + stopScrolling: function() { + if(this.scrollInterval) { + clearInterval(this.scrollInterval); + this.scrollInterval = null; + Draggables._lastScrollPointer = null; + } + }, + + startScrolling: function(speed) { + if(!(speed[0] || speed[1])) return; + this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; + this.lastScrolled = new Date(); + this.scrollInterval = setInterval(this.scroll.bind(this), 10); + }, + + scroll: function() { + var current = new Date(); + var delta = current - this.lastScrolled; + this.lastScrolled = current; + if(this.options.scroll == window) { + with (this._getWindowScroll(this.options.scroll)) { + if (this.scrollSpeed[0] || this.scrollSpeed[1]) { + var d = delta / 1000; + this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); + } + } + } else { + this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; + this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000; + } + + Position.prepare(); + Droppables.show(Draggables._lastPointer, this.element); + Draggables.notify('onDrag', this); + if (this._isScrollChild) { + Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); + Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; + Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; + if (Draggables._lastScrollPointer[0] < 0) + Draggables._lastScrollPointer[0] = 0; + if (Draggables._lastScrollPointer[1] < 0) + Draggables._lastScrollPointer[1] = 0; + this.draw(Draggables._lastScrollPointer); + } + + if(this.options.change) this.options.change(this); + }, + + _getWindowScroll: function(w) { + var T, L, W, H; + with (w.document) { + if (w.document.documentElement && documentElement.scrollTop) { + T = documentElement.scrollTop; + L = documentElement.scrollLeft; + } else if (w.document.body) { + T = body.scrollTop; + L = body.scrollLeft; + } + if (w.innerWidth) { + W = w.innerWidth; + H = w.innerHeight; + } else if (w.document.documentElement && documentElement.clientWidth) { + W = documentElement.clientWidth; + H = documentElement.clientHeight; + } else { + W = body.offsetWidth; + H = body.offsetHeight + } + } + return { top: T, left: L, width: W, height: H }; + } +} + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create(); +SortableObserver.prototype = { + initialize: function(element, observer) { + this.element = $(element); + this.observer = observer; + this.lastValue = Sortable.serialize(this.element); + }, + + onStart: function() { + this.lastValue = Sortable.serialize(this.element); + }, + + onEnd: function() { + Sortable.unmark(); + if(this.lastValue != Sortable.serialize(this.element)) + this.observer(this.element) + } +} + +var Sortable = { + SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, + + sortables: {}, + + _findRootElement: function(element) { + while (element.tagName.toUpperCase() != "BODY") { + if(element.id && Sortable.sortables[element.id]) return element; + element = element.parentNode; + } + }, + + options: function(element) { + element = Sortable._findRootElement($(element)); + if(!element) return; + return Sortable.sortables[element.id]; + }, + + destroy: function(element){ + var s = Sortable.options(element); + + if(s) { + Draggables.removeObserver(s.element); + s.droppables.each(function(d){ Droppables.remove(d) }); + s.draggables.invoke('destroy'); + + delete Sortable.sortables[s.element.id]; + } + }, + + create: function(element) { + element = $(element); + var options = Object.extend({ + element: element, + tag: 'li', // assumes li children, override with tag: 'tagname' + dropOnEmpty: false, + tree: false, + treeTag: 'ul', + overlap: 'vertical', // one of 'vertical', 'horizontal' + constraint: 'vertical', // one of 'vertical', 'horizontal', false + containment: element, // also takes array of elements (or id's); or false + handle: false, // or a CSS class + only: false, + delay: 0, + hoverclass: null, + ghosting: false, + quiet: false, + scroll: false, + scrollSensitivity: 20, + scrollSpeed: 15, + format: this.SERIALIZE_RULE, + + // these take arrays of elements or ids and can be + // used for better initialization performance + elements: false, + handles: false, + + onChange: Prototype.emptyFunction, + onUpdate: Prototype.emptyFunction + }, arguments[1] || {}); + + // clear any old sortable with same element + this.destroy(element); + + // build options for the draggables + var options_for_draggable = { + revert: true, + quiet: options.quiet, + scroll: options.scroll, + scrollSpeed: options.scrollSpeed, + scrollSensitivity: options.scrollSensitivity, + delay: options.delay, + ghosting: options.ghosting, + constraint: options.constraint, + handle: options.handle }; + + if(options.starteffect) + options_for_draggable.starteffect = options.starteffect; + + if(options.reverteffect) + options_for_draggable.reverteffect = options.reverteffect; + else + if(options.ghosting) options_for_draggable.reverteffect = function(element) { + element.style.top = 0; + element.style.left = 0; + }; + + if(options.endeffect) + options_for_draggable.endeffect = options.endeffect; + + if(options.zindex) + options_for_draggable.zindex = options.zindex; + + // build options for the droppables + var options_for_droppable = { + overlap: options.overlap, + containment: options.containment, + tree: options.tree, + hoverclass: options.hoverclass, + onHover: Sortable.onHover + } + + var options_for_tree = { + onHover: Sortable.onEmptyHover, + overlap: options.overlap, + containment: options.containment, + hoverclass: options.hoverclass + } + + // fix for gecko engine + Element.cleanWhitespace(element); + + options.draggables = []; + options.droppables = []; + + // drop on empty handling + if(options.dropOnEmpty || options.tree) { + Droppables.add(element, options_for_tree); + options.droppables.push(element); + } + + (options.elements || this.findElements(element, options) || []).each( function(e,i) { + var handle = options.handles ? $(options.handles[i]) : + (options.handle ? $(e).getElementsByClassName(options.handle)[0] : e); + options.draggables.push( + new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); + Droppables.add(e, options_for_droppable); + if(options.tree) e.treeNode = element; + options.droppables.push(e); + }); + + if(options.tree) { + (Sortable.findTreeElements(element, options) || []).each( function(e) { + Droppables.add(e, options_for_tree); + e.treeNode = element; + options.droppables.push(e); + }); + } + + // keep reference + this.sortables[element.id] = options; + + // for onupdate + Draggables.addObserver(new SortableObserver(element, options.onUpdate)); + + }, + + // return all suitable-for-sortable elements in a guaranteed order + findElements: function(element, options) { + return Element.findChildren( + element, options.only, options.tree ? true : false, options.tag); + }, + + findTreeElements: function(element, options) { + return Element.findChildren( + element, options.only, options.tree ? true : false, options.treeTag); + }, + + onHover: function(element, dropon, overlap) { + if(Element.isParent(dropon, element)) return; + + if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { + return; + } else if(overlap>0.5) { + Sortable.mark(dropon, 'before'); + if(dropon.previousSibling != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, dropon); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } else { + Sortable.mark(dropon, 'after'); + var nextElement = dropon.nextSibling || null; + if(nextElement != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, nextElement); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } + }, + + onEmptyHover: function(element, dropon, overlap) { + var oldParentNode = element.parentNode; + var droponOptions = Sortable.options(dropon); + + if(!Element.isParent(dropon, element)) { + var index; + + var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); + var child = null; + + if(children) { + var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); + + for (index = 0; index < children.length; index += 1) { + if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { + offset -= Element.offsetSize (children[index], droponOptions.overlap); + } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { + child = index + 1 < children.length ? children[index + 1] : null; + break; + } else { + child = children[index]; + break; + } + } + } + + dropon.insertBefore(element, child); + + Sortable.options(oldParentNode).onChange(element); + droponOptions.onChange(element); + } + }, + + unmark: function() { + if(Sortable._marker) Sortable._marker.hide(); + }, + + mark: function(dropon, position) { + // mark on ghosting only + var sortable = Sortable.options(dropon.parentNode); + if(sortable && !sortable.ghosting) return; + + if(!Sortable._marker) { + Sortable._marker = + ($('dropmarker') || Element.extend(document.createElement('DIV'))). + hide().addClassName('dropmarker').setStyle({position:'absolute'}); + document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); + } + var offsets = Position.cumulativeOffset(dropon); + Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); + + if(position=='after') + if(sortable.overlap == 'horizontal') + Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); + else + Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); + + Sortable._marker.show(); + }, + + _tree: function(element, options, parent) { + var children = Sortable.findElements(element, options) || []; + + for (var i = 0; i < children.length; ++i) { + var match = children[i].id.match(options.format); + + if (!match) continue; + + var child = { + id: encodeURIComponent(match ? match[1] : null), + element: element, + parent: parent, + children: [], + position: parent.children.length, + container: $(children[i]).down(options.treeTag) + } + + /* Get the element containing the children and recurse over it */ + if (child.container) + this._tree(child.container, options, child) + + parent.children.push (child); + } + + return parent; + }, + + tree: function(element) { + element = $(element); + var sortableOptions = this.options(element); + var options = Object.extend({ + tag: sortableOptions.tag, + treeTag: sortableOptions.treeTag, + only: sortableOptions.only, + name: element.id, + format: sortableOptions.format + }, arguments[1] || {}); + + var root = { + id: null, + parent: null, + children: [], + container: element, + position: 0 + } + + return Sortable._tree(element, options, root); + }, + + /* Construct a [i] index for a particular node */ + _constructIndex: function(node) { + var index = ''; + do { + if (node.id) index = '[' + node.position + ']' + index; + } while ((node = node.parent) != null); + return index; + }, + + sequence: function(element) { + element = $(element); + var options = Object.extend(this.options(element), arguments[1] || {}); + + return $(this.findElements(element, options) || []).map( function(item) { + return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; + }); + }, + + setSequence: function(element, new_sequence) { + element = $(element); + var options = Object.extend(this.options(element), arguments[2] || {}); + + var nodeMap = {}; + this.findElements(element, options).each( function(n) { + if (n.id.match(options.format)) + nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; + n.parentNode.removeChild(n); + }); + + new_sequence.each(function(ident) { + var n = nodeMap[ident]; + if (n) { + n[1].appendChild(n[0]); + delete nodeMap[ident]; + } + }); + }, + + serialize: function(element) { + element = $(element); + var options = Object.extend(Sortable.options(element), arguments[1] || {}); + var name = encodeURIComponent( + (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); + + if (options.tree) { + return Sortable.tree(element, arguments[1]).children.map( function (item) { + return [name + Sortable._constructIndex(item) + "[id]=" + + encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); + }).flatten().join('&'); + } else { + return Sortable.sequence(element, arguments[1]).map( function(item) { + return name + "[]=" + encodeURIComponent(item); + }).join('&'); + } + } +} + +// Returns true if child is contained within element +Element.isParent = function(child, element) { + if (!child.parentNode || child == element) return false; + if (child.parentNode == element) return true; + return Element.isParent(child.parentNode, element); +} + +Element.findChildren = function(element, only, recursive, tagName) { + if(!element.hasChildNodes()) return null; + tagName = tagName.toUpperCase(); + if(only) only = [only].flatten(); + var elements = []; + $A(element.childNodes).each( function(e) { + if(e.tagName && e.tagName.toUpperCase()==tagName && + (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) + elements.push(e); + if(recursive) { + var grandchildren = Element.findChildren(e, only, recursive, tagName); + if(grandchildren) elements.push(grandchildren); + } + }); + + return (elements.length>0 ? elements.flatten() : []); +} + +Element.offsetSize = function (element, type) { + return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; +} diff --git a/xCAT-web/js/effects.js b/xCAT-web/js/effects.js new file mode 100644 index 000000000..70d07526c --- /dev/null +++ b/xCAT-web/js/effects.js @@ -0,0 +1,1094 @@ +// script.aculo.us effects.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Contributors: +// Justin Palmer (http://encytemedia.com/) +// Mark Pilgrim (http://diveintomark.org/) +// Martin Bialasinki +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { + var color = '#'; + if(this.slice(0,4) == 'rgb(') { + var cols = this.slice(4,this.length-1).split(','); + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); + } else { + if(this.slice(0,1) == '#') { + if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if(this.length==7) color = this.toLowerCase(); + } + } + return(color.length==7 ? color : (arguments[0] || this)); +} + +/*--------------------------------------------------------------------------*/ + +Element.collectTextNodes = function(element) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); + }).flatten().join(''); +} + +Element.collectTextNodesIgnoreClass = function(element, className) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? + Element.collectTextNodesIgnoreClass(node, className) : '')); + }).flatten().join(''); +} + +Element.setContentZoom = function(element, percent) { + element = $(element); + element.setStyle({fontSize: (percent/100) + 'em'}); + if(Prototype.Browser.WebKit) window.scrollBy(0,0); + return element; +} + +Element.getInlineOpacity = function(element){ + return $(element).style.opacity || ''; +} + +Element.forceRerendering = function(element) { + try { + element = $(element); + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch(e) { } +}; + +/*--------------------------------------------------------------------------*/ + +Array.prototype.call = function() { + var args = arguments; + this.each(function(f){ f.apply(this, args) }); +} + +/*--------------------------------------------------------------------------*/ + +var Effect = { + _elementDoesNotExistError: { + name: 'ElementDoesNotExistError', + message: 'The specified DOM element does not exist, but is required for this effect to operate' + }, + tagifyText: function(element) { + if(typeof Builder == 'undefined') + throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); + + var tagifyStyle = 'position:relative'; + if(Prototype.Browser.IE) tagifyStyle += ';zoom:1'; + + element = $(element); + $A(element.childNodes).each( function(child) { + if(child.nodeType==3) { + child.nodeValue.toArray().each( function(character) { + element.insertBefore( + Builder.node('span',{style: tagifyStyle}, + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if(((typeof element == 'object') || + (typeof element == 'function')) && + (element.length)) + elements = element; + else + elements = $(element).childNodes; + + var options = Object.extend({ + speed: 0.1, + delay: 0.0 + }, arguments[2] || {}); + var masterDelay = options.delay; + + $A(elements).each( function(element, index) { + new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); + }); + }, + PAIRS: { + 'slide': ['SlideDown','SlideUp'], + 'blind': ['BlindDown','BlindUp'], + 'appear': ['Appear','Fade'] + }, + toggle: function(element, effect) { + element = $(element); + effect = (effect || 'appear').toLowerCase(); + var options = Object.extend({ + queue: { position:'end', scope:(element.id || 'global'), limit: 1 } + }, arguments[2] || {}); + Effect[element.visible() ? + Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); + } +}; + +var Effect2 = Effect; // deprecated + +/* ------------- transitions ------------- */ + +Effect.Transitions = { + linear: Prototype.K, + sinoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + 0.5; + }, + reverse: function(pos) { + return 1-pos; + }, + flicker: function(pos) { + var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; + return (pos > 1 ? 1 : pos); + }, + wobble: function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; + }, + pulse: function(pos, pulses) { + pulses = pulses || 5; + return ( + Math.round((pos % (1/pulses)) * pulses) == 0 ? + ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : + 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) + ); + }, + none: function(pos) { + return 0; + }, + full: function(pos) { + return 1; + } +}; + +/* ------------- core effects ------------- */ + +Effect.ScopedQueue = Class.create(); +Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { + initialize: function() { + this.effects = []; + this.interval = null; + }, + _each: function(iterator) { + this.effects._each(iterator); + }, + add: function(effect) { + var timestamp = new Date().getTime(); + + var position = (typeof effect.options.queue == 'string') ? + effect.options.queue : effect.options.queue.position; + + switch(position) { + case 'front': + // move unstarted effects after this effect + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { + e.startOn += effect.finishOn; + e.finishOn += effect.finishOn; + }); + break; + case 'with-last': + timestamp = this.effects.pluck('startOn').max() || timestamp; + break; + case 'end': + // start effect after last queued effect has finished + timestamp = this.effects.pluck('finishOn').max() || timestamp; + break; + } + + effect.startOn += timestamp; + effect.finishOn += timestamp; + + if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) + this.effects.push(effect); + + if(!this.interval) + this.interval = setInterval(this.loop.bind(this), 15); + }, + remove: function(effect) { + this.effects = this.effects.reject(function(e) { return e==effect }); + if(this.effects.length == 0) { + clearInterval(this.interval); + this.interval = null; + } + }, + loop: function() { + var timePos = new Date().getTime(); + for(var i=0, len=this.effects.length;i= this.startOn) { + if(timePos >= this.finishOn) { + this.render(1.0); + this.cancel(); + this.event('beforeFinish'); + if(this.finish) this.finish(); + this.event('afterFinish'); + return; + } + var pos = (timePos - this.startOn) / this.totalTime, + frame = Math.round(pos * this.totalFrames); + if(frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + cancel: function() { + if(!this.options.sync) + Effect.Queues.get(typeof this.options.queue == 'string' ? + 'global' : this.options.queue.scope).remove(this); + this.state = 'finished'; + }, + event: function(eventName) { + if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if(this.options[eventName]) this.options[eventName](this); + }, + inspect: function() { + var data = $H(); + for(property in this) + if(typeof this[property] != 'function') data[property] = this[property]; + return '#'; + } +} + +Effect.Parallel = Class.create(); +Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + this.effects.invoke('render', position); + }, + finish: function(position) { + this.effects.each( function(effect) { + effect.render(1.0); + effect.cancel(); + effect.event('beforeFinish'); + if(effect.finish) effect.finish(position); + effect.event('afterFinish'); + }); + } +}); + +Effect.Event = Class.create(); +Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), { + initialize: function() { + var options = Object.extend({ + duration: 0 + }, arguments[0] || {}); + this.start(options); + }, + update: Prototype.emptyFunction +}); + +Effect.Opacity = Class.create(); +Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + // make this work on IE on elements without 'layout' + if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + var options = Object.extend({ + from: this.element.getOpacity() || 0.0, + to: 1.0 + }, arguments[1] || {}); + this.start(options); + }, + update: function(position) { + this.element.setOpacity(position); + } +}); + +Effect.Move = Class.create(); +Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + x: 0, + y: 0, + mode: 'relative' + }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Bug in Opera: Opera returns the "real" position of a static element or + // relative element that does not have top/left explicitly set. + // ==> Always set top and left for position relative elements in your stylesheets + // (to 0 if you do not need them) + this.element.makePositioned(); + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); + if(this.options.mode == 'absolute') { + // absolute movement, so we need to calc deltaX and deltaY + this.options.x = this.options.x - this.originalLeft; + this.options.y = this.options.y - this.originalTop; + } + }, + update: function(position) { + this.element.setStyle({ + left: Math.round(this.options.x * position + this.originalLeft) + 'px', + top: Math.round(this.options.y * position + this.originalTop) + 'px' + }); + } +}); + +// for backwards compatibility +Effect.MoveBy = function(element, toTop, toLeft) { + return new Effect.Move(element, + Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); +}; + +Effect.Scale = Class.create(); +Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { + initialize: function(element, percent) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or {} with provided values + scaleFrom: 100.0, + scaleTo: percent + }, arguments[2] || {}); + this.start(options); + }, + setup: function() { + this.restoreAfterFinish = this.options.restoreAfterFinish || false; + this.elementPositioning = this.element.getStyle('position'); + + this.originalStyle = {}; + ['top','left','width','height','fontSize'].each( function(k) { + this.originalStyle[k] = this.element.style[k]; + }.bind(this)); + + this.originalTop = this.element.offsetTop; + this.originalLeft = this.element.offsetLeft; + + var fontSize = this.element.getStyle('font-size') || '100%'; + ['em','px','%','pt'].each( function(fontSizeType) { + if(fontSize.indexOf(fontSizeType)>0) { + this.fontSize = parseFloat(fontSize); + this.fontSizeType = fontSizeType; + } + }.bind(this)); + + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; + + this.dims = null; + if(this.options.scaleMode=='box') + this.dims = [this.element.offsetHeight, this.element.offsetWidth]; + if(/^content/.test(this.options.scaleMode)) + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; + if(!this.dims) + this.dims = [this.options.scaleMode.originalHeight, + this.options.scaleMode.originalWidth]; + }, + update: function(position) { + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if(this.options.scaleContent && this.fontSize) + this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); + }, + finish: function(position) { + if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle); + }, + setDimensions: function(height, width) { + var d = {}; + if(this.options.scaleX) d.width = Math.round(width) + 'px'; + if(this.options.scaleY) d.height = Math.round(height) + 'px'; + if(this.options.scaleFromCenter) { + var topd = (height - this.dims[0])/2; + var leftd = (width - this.dims[1])/2; + if(this.elementPositioning == 'absolute') { + if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; + if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; + } else { + if(this.options.scaleY) d.top = -topd + 'px'; + if(this.options.scaleX) d.left = -leftd + 'px'; + } + } + this.element.setStyle(d); + } +}); + +Effect.Highlight = Class.create(); +Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Prevent executing on elements not in the layout flow + if(this.element.getStyle('display')=='none') { this.cancel(); return; } + // Disable background image during the effect + this.oldStyle = {}; + if (!this.options.keepBackgroundImage) { + this.oldStyle.backgroundImage = this.element.getStyle('background-image'); + this.element.setStyle({backgroundImage: 'none'}); + } + if(!this.options.endcolor) + this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); + if(!this.options.restorecolor) + this.options.restorecolor = this.element.getStyle('background-color'); + // init color calculations + this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); + this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); + }, + update: function(position) { + this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ + return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); + }, + finish: function() { + this.element.setStyle(Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = Class.create(); +Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + this.start(arguments[1] || {}); + }, + setup: function() { + Position.prepare(); + var offsets = Position.cumulativeOffset(this.element); + if(this.options.offset) offsets[1] += this.options.offset; + var max = window.innerHeight ? + window.height - window.innerHeight : + document.body.scrollHeight - + (document.documentElement.clientHeight ? + document.documentElement.clientHeight : document.body.clientHeight); + this.scrollStart = Position.deltaY; + this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; + }, + update: function(position) { + Position.prepare(); + window.scrollTo(Position.deltaX, + this.scrollStart + (position*this.delta)); + } +}); + +/* ------------- combination effects ------------- */ + +Effect.Fade = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + var options = Object.extend({ + from: element.getOpacity() || 1.0, + to: 0.0, + afterFinishInternal: function(effect) { + if(effect.options.to!=0) return; + effect.element.hide().setStyle({opacity: oldOpacity}); + }}, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Appear = function(element) { + element = $(element); + var options = Object.extend({ + from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), + to: 1.0, + // force Safari to render floated elements properly + afterFinishInternal: function(effect) { + effect.element.forceRerendering(); + }, + beforeSetup: function(effect) { + effect.element.setOpacity(effect.options.from).show(); + }}, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Puff = function(element) { + element = $(element); + var oldStyle = { + opacity: element.getInlineOpacity(), + position: element.getStyle('position'), + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height + }; + return new Effect.Parallel( + [ new Effect.Scale(element, 200, + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], + Object.extend({ duration: 1.0, + beforeSetupInternal: function(effect) { + Position.absolutize(effect.effects[0].element) + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().setStyle(oldStyle); } + }, arguments[1] || {}) + ); +} + +Effect.BlindUp = function(element) { + element = $(element); + element.makeClipping(); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + restoreAfterFinish: true, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }, arguments[1] || {}) + ); +} + +Effect.BlindDown = function(element) { + element = $(element); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + } + }, arguments[1] || {})); +} + +Effect.SwitchOff = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + return new Effect.Appear(element, Object.extend({ + duration: 0.4, + from: 0, + transition: Effect.Transitions.flicker, + afterFinishInternal: function(effect) { + new Effect.Scale(effect.element, 1, { + duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); + } + }) + } + }, arguments[1] || {})); +} + +Effect.DropOut = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left'), + opacity: element.getInlineOpacity() }; + return new Effect.Parallel( + [ new Effect.Move(element, {x: 0, y: 100, sync: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 }) ], + Object.extend( + { duration: 0.5, + beforeSetup: function(effect) { + effect.effects[0].element.makePositioned(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); + } + }, arguments[1] || {})); +} + +Effect.Shake = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left') }; + return new Effect.Move(element, + { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { + effect.element.undoPositioned().setStyle(oldStyle); + }}) }}) }}) }}) }}) }}); +} + +Effect.SlideDown = function(element) { + element = $(element).cleanWhitespace(); + // SlideDown need to have the content of the element wrapped in a container element with fixed height! + var oldInnerBottom = element.down().getStyle('bottom'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: window.opera ? 0 : 1, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if(window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } + }, arguments[1] || {}) + ); +} + +Effect.SlideUp = function(element) { + element = $(element).cleanWhitespace(); + var oldInnerBottom = element.down().getStyle('bottom'); + return new Effect.Scale(element, window.opera ? 0 : 1, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + restoreAfterFinish: true, + beforeStartInternal: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if(window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom}); + effect.element.down().undoPositioned(); + } + }, arguments[1] || {}) + ); +} + +// Bug in opera makes the TD containing this element expand for a instance after finish +Effect.Squish = function(element) { + return new Effect.Scale(element, window.opera ? 1 : 0, { + restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }); +} + +Effect.Grow = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.full + }, arguments[1] || {}); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = dims.width; + initialMoveY = moveY = 0; + moveX = -dims.width; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = dims.height; + moveY = -dims.height; + break; + case 'bottom-right': + initialMoveX = dims.width; + initialMoveY = dims.height; + moveX = -dims.width; + moveY = -dims.height; + break; + case 'center': + initialMoveX = dims.width / 2; + initialMoveY = dims.height / 2; + moveX = -dims.width / 2; + moveY = -dims.height / 2; + break; + } + + return new Effect.Move(element, { + x: initialMoveX, + y: initialMoveY, + duration: 0.01, + beforeSetup: function(effect) { + effect.element.hide().makeClipping().makePositioned(); + }, + afterFinishInternal: function(effect) { + new Effect.Parallel( + [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), + new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), + new Effect.Scale(effect.element, 100, { + scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, + sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) + ], Object.extend({ + beforeSetup: function(effect) { + effect.effects[0].element.setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); + } + }, options) + ) + } + }); +} + +Effect.Shrink = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.none + }, arguments[1] || {}); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = dims.width; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = dims.height; + break; + case 'bottom-right': + moveX = dims.width; + moveY = dims.height; + break; + case 'center': + moveX = dims.width / 2; + moveY = dims.height / 2; + break; + } + + return new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), + new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), + new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) + ], Object.extend({ + beforeStartInternal: function(effect) { + effect.effects[0].element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } + }, options) + ); +} + +Effect.Pulsate = function(element) { + element = $(element); + var options = arguments[1] || {}; + var oldOpacity = element.getInlineOpacity(); + var transition = options.transition || Effect.Transitions.sinoidal; + var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; + reverser.bind(transition); + return new Effect.Opacity(element, + Object.extend(Object.extend({ duration: 2.0, from: 0, + afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } + }, options), {transition: reverser})); +} + +Effect.Fold = function(element) { + element = $(element); + var oldStyle = { + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height }; + element.makeClipping(); + return new Effect.Scale(element, 5, Object.extend({ + scaleContent: false, + scaleX: false, + afterFinishInternal: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleY: false, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().setStyle(oldStyle); + } }); + }}, arguments[1] || {})); +}; + +Effect.Morph = Class.create(); +Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + style: {} + }, arguments[1] || {}); + if (typeof options.style == 'string') { + if(options.style.indexOf(':') == -1) { + var cssText = '', selector = '.' + options.style; + $A(document.styleSheets).reverse().each(function(styleSheet) { + if (styleSheet.cssRules) cssRules = styleSheet.cssRules; + else if (styleSheet.rules) cssRules = styleSheet.rules; + $A(cssRules).reverse().each(function(rule) { + if (selector == rule.selectorText) { + cssText = rule.style.cssText; + throw $break; + } + }); + if (cssText) throw $break; + }); + this.style = cssText.parseStyle(); + options.afterFinishInternal = function(effect){ + effect.element.addClassName(effect.options.style); + effect.transforms.each(function(transform) { + if(transform.style != 'opacity') + effect.element.style[transform.style] = ''; + }); + } + } else this.style = options.style.parseStyle(); + } else this.style = $H(options.style) + this.start(options); + }, + setup: function(){ + function parseColor(color){ + if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; + color = color.parseColor(); + return $R(0,2).map(function(i){ + return parseInt( color.slice(i*2+1,i*2+3), 16 ) + }); + } + this.transforms = this.style.map(function(pair){ + var property = pair[0], value = pair[1], unit = null; + + if(value.parseColor('#zzzzzz') != '#zzzzzz') { + value = value.parseColor(); + unit = 'color'; + } else if(property == 'opacity') { + value = parseFloat(value); + if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + } else if(Element.CSS_LENGTH.test(value)) { + var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); + value = parseFloat(components[1]); + unit = (components.length == 3) ? components[2] : null; + } + + var originalValue = this.element.getStyle(property); + return { + style: property.camelize(), + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), + targetValue: unit=='color' ? parseColor(value) : value, + unit: unit + }; + }.bind(this)).reject(function(transform){ + return ( + (transform.originalValue == transform.targetValue) || + ( + transform.unit != 'color' && + (isNaN(transform.originalValue) || isNaN(transform.targetValue)) + ) + ) + }); + }, + update: function(position) { + var style = {}, transform, i = this.transforms.length; + while(i--) + style[(transform = this.transforms[i]).style] = + transform.unit=='color' ? '#'+ + (Math.round(transform.originalValue[0]+ + (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + + (Math.round(transform.originalValue[1]+ + (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + + (Math.round(transform.originalValue[2]+ + (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : + transform.originalValue + Math.round( + ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit; + this.element.setStyle(style, true); + } +}); + +Effect.Transform = Class.create(); +Object.extend(Effect.Transform.prototype, { + initialize: function(tracks){ + this.tracks = []; + this.options = arguments[1] || {}; + this.addTracks(tracks); + }, + addTracks: function(tracks){ + tracks.each(function(track){ + var data = $H(track).values().first(); + this.tracks.push($H({ + ids: $H(track).keys().first(), + effect: Effect.Morph, + options: { style: data } + })); + }.bind(this)); + return this; + }, + play: function(){ + return new Effect.Parallel( + this.tracks.map(function(track){ + var elements = [$(track.ids) || $$(track.ids)].flatten(); + return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) }); + }).flatten(), + this.options + ); + } +}); + +Element.CSS_PROPERTIES = $w( + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + + 'fontSize fontWeight height left letterSpacing lineHeight ' + + 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ + 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + + 'right textIndent top width wordSpacing zIndex'); + +Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; + +String.prototype.parseStyle = function(){ + var element = document.createElement('div'); + element.innerHTML = '

    '; + var style = element.childNodes[0].style, styleRules = $H(); + + Element.CSS_PROPERTIES.each(function(property){ + if(style[property]) styleRules[property] = style[property]; + }); + if(Prototype.Browser.IE && this.indexOf('opacity') > -1) { + styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]; + } + return styleRules; +}; + +Element.morph = function(element, style) { + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {})); + return element; +}; + +['getInlineOpacity','forceRerendering','setContentZoom', + 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( + function(f) { Element.Methods[f] = Element[f]; } +); + +Element.Methods.visualEffect = function(element, effect, options) { + s = effect.dasherize().camelize(); + effect_class = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[effect_class](element, options); + return $(element); +}; + +Element.addMethods(); \ No newline at end of file diff --git a/xCAT-web/js/index.html b/xCAT-web/js/index.html new file mode 100644 index 000000000..0b9dddb15 --- /dev/null +++ b/xCAT-web/js/index.html @@ -0,0 +1,64 @@ + + + + + dhtmlxTree v.1.5 Standard + + + + +

    dhtmlxTree Standard Samples

    + +

    New samples in version 1.5

    +
  • Simple drag-n-drop into tree from any HTML
  • +
  • Changing iconsets
  • + +

    Other samples*

    +
  • Init from html
  • +
  • dhtmlXTree - Initialize object on page
  • +
  • dhtmlXTree - Add / Delete items
  • +
  • Event handlers
  • +
  • Collapse/Expand
  • +
  • Change text/image
  • +
  • Checkboxes
  • +
  • Easy skinable design
  • +
  • Autoloading from XML
  • +
  • Drag and Drop
  • + +

    Professional Edition features

    +
  • Context Menu
  • +
  • Sorting (including custom type)
  • +
  • OnDrag/OnDrop event handler
  • +
  • Additional Drag-n-drop behavior – drop-as-sibling, complex drag-n-drop
  • +
  • Drag-n-drop between frames
  • +
  • Drag-n-drop to/from dhtmlxGrid
  • +
  • Ability to move items within/between trees with script API
  • +
  • Multiselect, drag-n-drop multiple items
  • +
  • Focus item with script method
  • +
  • Dynamic rendering – advanced xml processing for loading big trees when dynamical loading can't be used
  • +
  • Serialization – possibility to create XML based on updated tree structure.
  • +
  • Search
  • +
  • Multi-line tree items
  • +
  • Saving/Restoring tree state in/from cookies
  • +
  • Change icon size
  • +
  • Editable Items
  • +
  • Ability to automaticaly update server side on tree structure changes
  • +
  • Keyboard navigation
  • +
  • Locked items
  • +
  • Right-to-left languages support
  • +
  • Error handling
  • +
  • Quick Search - navigate through the tree typing node name
  • +
  • Tree lock
  • + + + +

    +*More than 50 samples of various features included in Professional Edition.
    +To obtain and use dhtmlxTree Professional contact us at dhtmlx@scand.com
    + +
    +© Scand LLC + + + + diff --git a/xCAT-web/js/prototype.js b/xCAT-web/js/prototype.js new file mode 100644 index 000000000..5d2100fac --- /dev/null +++ b/xCAT-web/js/prototype.js @@ -0,0 +1,3271 @@ +/* Prototype JavaScript framework, version 1.5.1 + * (c) 2005-2007 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.5.1', + + Browser: { + IE: !!(window.attachEvent && !window.opera), + Opera: !!window.opera, + WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, + Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1 + }, + + BrowserFeatures: { + XPath: !!document.evaluate, + ElementExtensions: !!window.HTMLElement, + SpecificElementExtensions: + (document.createElement('div').__proto__ !== + document.createElement('form').__proto__) + }, + + ScriptFragment: ']*>([\u0001-\uFFFF]*?)', + JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/, + + emptyFunction: function() { }, + K: function(x) { return x } +} + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +} + +var Abstract = new Object(); + +Object.extend = function(destination, source) { + for (var property in source) { + destination[property] = source[property]; + } + return destination; +} + +Object.extend(Object, { + inspect: function(object) { + try { + if (object === undefined) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : object.toString(); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + }, + + toJSON: function(object) { + var type = typeof object; + switch(type) { + case 'undefined': + case 'function': + case 'unknown': return; + case 'boolean': return object.toString(); + } + if (object === null) return 'null'; + if (object.toJSON) return object.toJSON(); + if (object.ownerDocument === document) return; + var results = []; + for (var property in object) { + var value = Object.toJSON(object[property]); + if (value !== undefined) + results.push(property.toJSON() + ': ' + value); + } + return '{' + results.join(', ') + '}'; + }, + + keys: function(object) { + var keys = []; + for (var property in object) + keys.push(property); + return keys; + }, + + values: function(object) { + var values = []; + for (var property in object) + values.push(object[property]); + return values; + }, + + clone: function(object) { + return Object.extend({}, object); + } +}); + +Function.prototype.bind = function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } +} + +Function.prototype.bindAsEventListener = function(object) { + var __method = this, args = $A(arguments), object = args.shift(); + return function(event) { + return __method.apply(object, [event || window.event].concat(args)); + } +} + +Object.extend(Number.prototype, { + toColorPart: function() { + return this.toPaddedString(2, 16); + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + }, + + toPaddedString: function(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + }, + + toJSON: function() { + return isFinite(this) ? this.toString() : 'null'; + } +}); + +Date.prototype.toJSON = function() { + return '"' + this.getFullYear() + '-' + + (this.getMonth() + 1).toPaddedString(2) + '-' + + this.getDate().toPaddedString(2) + 'T' + + this.getHours().toPaddedString(2) + ':' + + this.getMinutes().toPaddedString(2) + ':' + + this.getSeconds().toPaddedString(2) + '"'; +}; + +var Try = { + these: function() { + var returnValue; + + for (var i = 0, length = arguments.length; i < length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) {} + } + + return returnValue; + } +} + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create(); +PeriodicalExecuter.prototype = { + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.callback(this); + } finally { + this.currentlyExecuting = false; + } + } + } +} +Object.extend(String, { + interpret: function(value) { + return value == null ? '' : String(value); + }, + specialChar: { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\\': '\\\\' + } +}); + +Object.extend(String.prototype, { + gsub: function(pattern, replacement) { + var result = '', source = this, match; + replacement = arguments.callee.prepareReplacement(replacement); + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + }, + + sub: function(pattern, replacement, count) { + replacement = this.gsub.prepareReplacement(replacement); + count = count === undefined ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + }, + + scan: function(pattern, iterator) { + this.gsub(pattern, iterator); + return this; + }, + + truncate: function(length, truncation) { + length = length || 30; + truncation = truncation === undefined ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : this; + }, + + strip: function() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + }, + + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(function(script) { return eval(script) }); + }, + + escapeHTML: function() { + var self = arguments.callee; + self.text.data = this; + return self.div.innerHTML; + }, + + unescapeHTML: function() { + var div = document.createElement('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? (div.childNodes.length > 1 ? + $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : + div.childNodes[0].nodeValue) : ''; + }, + + toQueryParams: function(separator) { + var match = this.strip().match(/([^?#]*)(#.*)?$/); + if (!match) return {}; + + return match[1].split(separator || '&').inject({}, function(hash, pair) { + if ((pair = pair.split('='))[0]) { + var key = decodeURIComponent(pair.shift()); + var value = pair.length > 1 ? pair.join('=') : pair[0]; + if (value != undefined) value = decodeURIComponent(value); + + if (key in hash) { + if (hash[key].constructor != Array) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + }, + + toArray: function() { + return this.split(''); + }, + + succ: function() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + }, + + times: function(count) { + var result = ''; + for (var i = 0; i < count; i++) result += this; + return result; + }, + + camelize: function() { + var parts = this.split('-'), len = parts.length; + if (len == 1) return parts[0]; + + var camelized = this.charAt(0) == '-' + ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) + : parts[0]; + + for (var i = 1; i < len; i++) + camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); + + return camelized; + }, + + capitalize: function() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + }, + + underscore: function() { + return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); + }, + + dasherize: function() { + return this.gsub(/_/,'-'); + }, + + inspect: function(useDoubleQuotes) { + var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { + var character = String.specialChar[match[0]]; + return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + }, + + toJSON: function() { + return this.inspect(true); + }, + + unfilterJSON: function(filter) { + return this.sub(filter || Prototype.JSONFilter, '#{1}'); + }, + + evalJSON: function(sanitize) { + var json = this.unfilterJSON(); + try { + if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json))) + return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + }, + + include: function(pattern) { + return this.indexOf(pattern) > -1; + }, + + startsWith: function(pattern) { + return this.indexOf(pattern) === 0; + }, + + endsWith: function(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.lastIndexOf(pattern) === d; + }, + + empty: function() { + return this == ''; + }, + + blank: function() { + return /^\s*$/.test(this); + } +}); + +if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { + escapeHTML: function() { + return this.replace(/&/g,'&').replace(//g,'>'); + }, + unescapeHTML: function() { + return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + } +}); + +String.prototype.gsub.prepareReplacement = function(replacement) { + if (typeof replacement == 'function') return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; +} + +String.prototype.parseQuery = String.prototype.toQueryParams; + +Object.extend(String.prototype.escapeHTML, { + div: document.createElement('div'), + text: document.createTextNode('') +}); + +with (String.prototype.escapeHTML) div.appendChild(text); + +var Template = Class.create(); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; +Template.prototype = { + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + return this.template.gsub(this.pattern, function(match) { + var before = match[1]; + if (before == '\\') return match[2]; + return before + String.interpret(object[match[3]]); + }); + } +} + +var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead'); + +var Enumerable = { + each: function(iterator) { + var index = 0; + try { + this._each(function(value) { + iterator(value, index++); + }); + } catch (e) { + if (e != $break) throw e; + } + return this; + }, + + eachSlice: function(number, iterator) { + var index = -number, slices = [], array = this.toArray(); + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.map(iterator); + }, + + all: function(iterator) { + var result = true; + this.each(function(value, index) { + result = result && !!(iterator || Prototype.K)(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator) { + var result = false; + this.each(function(value, index) { + if (result = !!(iterator || Prototype.K)(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator) { + var results = []; + this.each(function(value, index) { + results.push((iterator || Prototype.K)(value, index)); + }); + return results; + }, + + detect: function(iterator) { + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator) { + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(pattern, iterator) { + var results = []; + this.each(function(value, index) { + var stringValue = value.toString(); + if (stringValue.match(pattern)) + results.push((iterator || Prototype.K)(value, index)); + }) + return results; + }, + + include: function(object) { + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inGroupsOf: function(number, fillWith) { + fillWith = fillWith === undefined ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + }, + + inject: function(memo, iterator) { + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (result == undefined || value >= result) + result = value; + }); + return result; + }, + + min: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (result == undefined || value < result) + result = value; + }); + return result; + }, + + partition: function(iterator) { + var trues = [], falses = []; + this.each(function(value, index) { + ((iterator || Prototype.K)(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value, index) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator) { + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator) { + return this.map(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.map(); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (typeof args.last() == 'function') + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + }, + + size: function() { + return this.toArray().length; + }, + + inspect: function() { + return '#'; + } +} + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray +}); +var $A = Array.from = function(iterable) { + if (!iterable) return []; + if (iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0, length = iterable.length; i < length; i++) + results.push(iterable[i]); + return results; + } +} + +if (Prototype.Browser.WebKit) { + $A = Array.from = function(iterable) { + if (!iterable) return []; + if (!(typeof iterable == 'function' && iterable == '[object NodeList]') && + iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0, length = iterable.length; i < length; i++) + results.push(iterable[i]); + return results; + } + } +} + +Object.extend(Array.prototype, Enumerable); + +if (!Array.prototype._reverse) + Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0, length = this.length; i < length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(value && value.constructor == Array ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + indexOf: function(object) { + for (var i = 0, length = this.length; i < length; i++) + if (this[i] == object) return i; + return -1; + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + reduce: function() { + return this.length > 1 ? this : this[0]; + }, + + uniq: function(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + }, + + clone: function() { + return [].concat(this); + }, + + size: function() { + return this.length; + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + }, + + toJSON: function() { + var results = []; + this.each(function(object) { + var value = Object.toJSON(object); + if (value !== undefined) results.push(value); + }); + return '[' + results.join(', ') + ']'; + } +}); + +Array.prototype.toArray = Array.prototype.clone; + +function $w(string) { + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +if (Prototype.Browser.Opera){ + Array.prototype.concat = function() { + var array = []; + for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); + for (var i = 0, length = arguments.length; i < length; i++) { + if (arguments[i].constructor == Array) { + for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) + array.push(arguments[i][j]); + } else { + array.push(arguments[i]); + } + } + return array; + } +} +var Hash = function(object) { + if (object instanceof Hash) this.merge(object); + else Object.extend(this, object || {}); +}; + +Object.extend(Hash, { + toQueryString: function(obj) { + var parts = []; + parts.add = arguments.callee.addPair; + + this.prototype._each.call(obj, function(pair) { + if (!pair.key) return; + var value = pair.value; + + if (value && typeof value == 'object') { + if (value.constructor == Array) value.each(function(value) { + parts.add(pair.key, value); + }); + return; + } + parts.add(pair.key, value); + }); + + return parts.join('&'); + }, + + toJSON: function(object) { + var results = []; + this.prototype._each.call(object, function(pair) { + var value = Object.toJSON(pair.value); + if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value); + }); + return '{' + results.join(', ') + '}'; + } +}); + +Hash.toQueryString.addPair = function(key, value, prefix) { + key = encodeURIComponent(key); + if (value === undefined) this.push(key); + else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value))); +} + +Object.extend(Hash.prototype, Enumerable); +Object.extend(Hash.prototype, { + _each: function(iterator) { + for (var key in this) { + var value = this[key]; + if (value && value == Hash.prototype[key]) continue; + + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + merge: function(hash) { + return $H(hash).inject(this, function(mergedHash, pair) { + mergedHash[pair.key] = pair.value; + return mergedHash; + }); + }, + + remove: function() { + var result; + for(var i = 0, length = arguments.length; i < length; i++) { + var value = this[arguments[i]]; + if (value !== undefined){ + if (result === undefined) result = value; + else { + if (result.constructor != Array) result = [result]; + result.push(value) + } + } + delete this[arguments[i]]; + } + return result; + }, + + toQueryString: function() { + return Hash.toQueryString(this); + }, + + inspect: function() { + return '#'; + }, + + toJSON: function() { + return Hash.toJSON(this); + } +}); + +function $H(object) { + if (object instanceof Hash) return object; + return new Hash(object); +}; + +// Safari iterates over shadowed properties +if (function() { + var i = 0, Test = function(value) { this.key = value }; + Test.prototype.key = 'foo'; + for (var property in new Test('bar')) i++; + return i > 1; +}()) Hash.prototype._each = function(iterator) { + var cache = []; + for (var key in this) { + var value = this[key]; + if ((value && value == Hash.prototype[key]) || cache.include(key)) continue; + cache.push(key); + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } +}; +ObjectRange = Class.create(); +Object.extend(ObjectRange.prototype, Enumerable); +Object.extend(ObjectRange.prototype, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + while (this.include(value)) { + iterator(value); + value = value.succ(); + } + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +} + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responder) { + if (!this.include(responder)) + this.responders.push(responder); + }, + + unregister: function(responder) { + this.responders = this.responders.without(responder); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (typeof responder[callback] == 'function') { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) {} + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { + Ajax.activeRequestCount++; + }, + onComplete: function() { + Ajax.activeRequestCount--; + } +}); + +Ajax.Base = function() {}; +Ajax.Base.prototype = { + setOptions: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '' + } + Object.extend(this.options, options || {}); + + this.options.method = this.options.method.toLowerCase(); + if (typeof this.options.parameters == 'string') + this.options.parameters = this.options.parameters.toQueryParams(); + } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { + _complete: false, + + initialize: function(url, options) { + this.transport = Ajax.getTransport(); + this.setOptions(options); + this.request(url); + }, + + request: function(url) { + this.url = url; + this.method = this.options.method; + var params = Object.clone(this.options.parameters); + + if (!['get', 'post'].include(this.method)) { + // simulate other verbs over post + params['_method'] = this.method; + this.method = 'post'; + } + + this.parameters = params; + + if (params = Hash.toQueryString(params)) { + // when GET, append parameters to URL + if (this.method == 'get') + this.url += (this.url.include('?') ? '&' : '?') + params; + else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + params += '&_='; + } + + try { + if (this.options.onCreate) this.options.onCreate(this.transport); + Ajax.Responders.dispatch('onCreate', this, this.transport); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) + setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); + + this.transport.onreadystatechange = this.onStateChange.bind(this); + this.setRequestHeaders(); + + this.body = this.method == 'post' ? (this.options.postBody || params) : null; + this.transport.send(this.body); + + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + + } + catch (e) { + this.dispatchException(e); + } + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState > 1 && !((readyState == 4) && this._complete)) + this.respondToReadyState(this.transport.readyState); + }, + + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'X-Prototype-Version': Prototype.Version, + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) + headers['Connection'] = 'close'; + } + + // user-defined headers + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (typeof extras.push == 'function') + for (var i = 0, length = extras.length; i < length; i += 2) + headers[extras[i]] = extras[i+1]; + else + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); + } + + for (var name in headers) + this.transport.setRequestHeader(name, headers[name]); + }, + + success: function() { + return !this.transport.status + || (this.transport.status >= 200 && this.transport.status < 300); + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.evalJSON(); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + this.transport.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(transport, json); + } catch (e) { + this.dispatchException(e); + } + + var contentType = this.getHeader('Content-type'); + if (contentType && contentType.strip(). + match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(transport, json); + Ajax.Responders.dispatch('on' + state, this, transport, json); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + // avoid memory leak in MSIE: clean up + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name); + } catch (e) { return null } + }, + + evalJSON: function() { + try { + var json = this.getHeader('X-JSON'); + return json ? json.evalJSON() : null; + } catch (e) { return null } + }, + + evalResponse: function() { + try { + return eval((this.transport.responseText || '').unfilterJSON()); + } catch (e) { + this.dispatchException(e); + } + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Updater = Class.create(); + +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { + initialize: function(container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + } + + this.transport = Ajax.getTransport(); + this.setOptions(options); + + var onComplete = this.options.onComplete || Prototype.emptyFunction; + this.options.onComplete = (function(transport, param) { + this.updateContent(); + onComplete(transport, param); + }).bind(this); + + this.request(url); + }, + + updateContent: function() { + var receiver = this.container[this.success() ? 'success' : 'failure']; + var response = this.transport.responseText; + + if (!this.options.evalScripts) response = response.stripScripts(); + + if (receiver = $(receiver)) { + if (this.options.insertion) + new this.options.insertion(receiver, response); + else + receiver.update(response); + } + + if (this.success()) { + if (this.onComplete) + setTimeout(this.onComplete.bind(this), 10); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { + initialize: function(container, url, options) { + this.setOptions(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = {}; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.options.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(request) { + if (this.options.decay) { + this.decay = (request.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = request.responseText; + } + this.timer = setTimeout(this.onTimerEvent.bind(this), + this.decay * this.frequency * 1000); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +function $(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push($(arguments[i])); + return elements; + } + if (typeof element == 'string') + element = document.getElementById(element); + return Element.extend(element); +} + +if (Prototype.BrowserFeatures.XPath) { + document._getElementsByXPath = function(expression, parentElement) { + var results = []; + var query = document.evaluate(expression, $(parentElement) || document, + null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + for (var i = 0, length = query.snapshotLength; i < length; i++) + results.push(query.snapshotItem(i)); + return results; + }; + + document.getElementsByClassName = function(className, parentElement) { + var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]"; + return document._getElementsByXPath(q, parentElement); + } + +} else document.getElementsByClassName = function(className, parentElement) { + var children = ($(parentElement) || document.body).getElementsByTagName('*'); + var elements = [], child; + for (var i = 0, length = children.length; i < length; i++) { + child = children[i]; + if (Element.hasClassName(child, className)) + elements.push(Element.extend(child)); + } + return elements; +}; + +/*--------------------------------------------------------------------------*/ + +if (!window.Element) var Element = {}; + +Element.extend = function(element) { + var F = Prototype.BrowserFeatures; + if (!element || !element.tagName || element.nodeType == 3 || + element._extended || F.SpecificElementExtensions || element == window) + return element; + + var methods = {}, tagName = element.tagName, cache = Element.extend.cache, + T = Element.Methods.ByTag; + + // extend methods for all tags (Safari doesn't need this) + if (!F.ElementExtensions) { + Object.extend(methods, Element.Methods), + Object.extend(methods, Element.Methods.Simulated); + } + + // extend methods for specific tags + if (T[tagName]) Object.extend(methods, T[tagName]); + + for (var property in methods) { + var value = methods[property]; + if (typeof value == 'function' && !(property in element)) + element[property] = cache.findOrStore(value); + } + + element._extended = Prototype.emptyFunction; + return element; +}; + +Element.extend.cache = { + findOrStore: function(value) { + return this[value] = this[value] || function() { + return value.apply(null, [this].concat($A(arguments))); + } + } +}; + +Element.Methods = { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function(element) { + element = $(element); + Element[Element.visible(element) ? 'hide' : 'show'](element); + return element; + }, + + hide: function(element) { + $(element).style.display = 'none'; + return element; + }, + + show: function(element) { + $(element).style.display = ''; + return element; + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + }, + + update: function(element, html) { + html = typeof html == 'undefined' ? '' : html.toString(); + $(element).innerHTML = html.stripScripts(); + setTimeout(function() {html.evalScripts()}, 10); + return element; + }, + + replace: function(element, html) { + element = $(element); + html = typeof html == 'undefined' ? '' : html.toString(); + if (element.outerHTML) { + element.outerHTML = html.stripScripts(); + } else { + var range = element.ownerDocument.createRange(); + range.selectNodeContents(element); + element.parentNode.replaceChild( + range.createContextualFragment(html.stripScripts()), element); + } + setTimeout(function() {html.evalScripts()}, 10); + return element; + }, + + inspect: function(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + $H({'id': 'id', 'className': 'class'}).each(function(pair) { + var property = pair.first(), attribute = pair.last(); + var value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + }); + return result + '>'; + }, + + recursivelyCollect: function(element, property) { + element = $(element); + var elements = []; + while (element = element[property]) + if (element.nodeType == 1) + elements.push(Element.extend(element)); + return elements; + }, + + ancestors: function(element) { + return $(element).recursivelyCollect('parentNode'); + }, + + descendants: function(element) { + return $A($(element).getElementsByTagName('*')).each(Element.extend); + }, + + firstDescendant: function(element) { + element = $(element).firstChild; + while (element && element.nodeType != 1) element = element.nextSibling; + return $(element); + }, + + immediateDescendants: function(element) { + if (!(element = $(element).firstChild)) return []; + while (element && element.nodeType != 1) element = element.nextSibling; + if (element) return [element].concat($(element).nextSiblings()); + return []; + }, + + previousSiblings: function(element) { + return $(element).recursivelyCollect('previousSibling'); + }, + + nextSiblings: function(element) { + return $(element).recursivelyCollect('nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return element.previousSiblings().reverse().concat(element.nextSiblings()); + }, + + match: function(element, selector) { + if (typeof selector == 'string') + selector = new Selector(selector); + return selector.match($(element)); + }, + + up: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(element.parentNode); + var ancestors = element.ancestors(); + return expression ? Selector.findElement(ancestors, expression, index) : + ancestors[index || 0]; + }, + + down: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return element.firstDescendant(); + var descendants = element.descendants(); + return expression ? Selector.findElement(descendants, expression, index) : + descendants[index || 0]; + }, + + previous: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); + var previousSiblings = element.previousSiblings(); + return expression ? Selector.findElement(previousSiblings, expression, index) : + previousSiblings[index || 0]; + }, + + next: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); + var nextSiblings = element.nextSiblings(); + return expression ? Selector.findElement(nextSiblings, expression, index) : + nextSiblings[index || 0]; + }, + + getElementsBySelector: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element, args); + }, + + getElementsByClassName: function(element, className) { + return document.getElementsByClassName(className, element); + }, + + readAttribute: function(element, name) { + element = $(element); + if (Prototype.Browser.IE) { + if (!element.attributes) return null; + var t = Element._attributeTranslations; + if (t.values[name]) return t.values[name](element, name); + if (t.names[name]) name = t.names[name]; + var attribute = element.attributes[name]; + return attribute ? attribute.nodeValue : null; + } + return element.getAttribute(name); + }, + + getHeight: function(element) { + return $(element).getDimensions().height; + }, + + getWidth: function(element) { + return $(element).getDimensions().width; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + var elementClassName = element.className; + if (elementClassName.length == 0) return false; + if (elementClassName == className || + elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) + return true; + return false; + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + Element.classNames(element).add(className); + return element; + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + Element.classNames(element).remove(className); + return element; + }, + + toggleClassName: function(element, className) { + if (!(element = $(element))) return; + Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className); + return element; + }, + + observe: function() { + Event.observe.apply(Event, arguments); + return $A(arguments).first(); + }, + + stopObserving: function() { + Event.stopObserving.apply(Event, arguments); + return $A(arguments).first(); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + var node = element.firstChild; + while (node) { + var nextNode = node.nextSibling; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + element.removeChild(node); + node = nextNode; + } + return element; + }, + + empty: function(element) { + return $(element).innerHTML.blank(); + }, + + descendantOf: function(element, ancestor) { + element = $(element), ancestor = $(ancestor); + while (element = element.parentNode) + if (element == ancestor) return true; + return false; + }, + + scrollTo: function(element) { + element = $(element); + var pos = Position.cumulativeOffset(element); + window.scrollTo(pos[0], pos[1]); + return element; + }, + + getStyle: function(element, style) { + element = $(element); + style = style == 'float' ? 'cssFloat' : style.camelize(); + var value = element.style[style]; + if (!value) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + if (style == 'opacity') return value ? parseFloat(value) : 1.0; + return value == 'auto' ? null : value; + }, + + getOpacity: function(element) { + return $(element).getStyle('opacity'); + }, + + setStyle: function(element, styles, camelized) { + element = $(element); + var elementStyle = element.style; + + for (var property in styles) + if (property == 'opacity') element.setOpacity(styles[property]) + else + elementStyle[(property == 'float' || property == 'cssFloat') ? + (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') : + (camelized ? property : property.camelize())] = styles[property]; + + return element; + }, + + setOpacity: function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + return element; + }, + + getDimensions: function(element) { + element = $(element); + var display = $(element).getStyle('display'); + if (display != 'none' && display != null) // Safari bug + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + var originalDisplay = els.display; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = 'block'; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = originalDisplay; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + return element; + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + return element; + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return element; + element._overflow = element.style.overflow || 'auto'; + if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') + element.style.overflow = 'hidden'; + return element; + }, + + undoClipping: function(element) { + element = $(element); + if (!element._overflow) return element; + element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; + element._overflow = null; + return element; + } +}; + +Object.extend(Element.Methods, { + childOf: Element.Methods.descendantOf, + childElements: Element.Methods.immediateDescendants +}); + +if (Prototype.Browser.Opera) { + Element.Methods._getStyle = Element.Methods.getStyle; + Element.Methods.getStyle = function(element, style) { + switch(style) { + case 'left': + case 'top': + case 'right': + case 'bottom': + if (Element._getStyle(element, 'position') == 'static') return null; + default: return Element._getStyle(element, style); + } + }; +} +else if (Prototype.Browser.IE) { + Element.Methods.getStyle = function(element, style) { + element = $(element); + style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); + var value = element.style[style]; + if (!value && element.currentStyle) value = element.currentStyle[style]; + + if (style == 'opacity') { + if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) + if (value[1]) return parseFloat(value[1]) / 100; + return 1.0; + } + + if (value == 'auto') { + if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) + return element['offset'+style.capitalize()] + 'px'; + return null; + } + return value; + }; + + Element.Methods.setOpacity = function(element, value) { + element = $(element); + var filter = element.getStyle('filter'), style = element.style; + if (value == 1 || value === '') { + style.filter = filter.replace(/alpha\([^\)]*\)/gi,''); + return element; + } else if (value < 0.00001) value = 0; + style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') + + 'alpha(opacity=' + (value * 100) + ')'; + return element; + }; + + // IE is missing .innerHTML support for TABLE-related elements + Element.Methods.update = function(element, html) { + element = $(element); + html = typeof html == 'undefined' ? '' : html.toString(); + var tagName = element.tagName.toUpperCase(); + if (['THEAD','TBODY','TR','TD'].include(tagName)) { + var div = document.createElement('div'); + switch (tagName) { + case 'THEAD': + case 'TBODY': + div.innerHTML = '' + html.stripScripts() + '
    '; + depth = 2; + break; + case 'TR': + div.innerHTML = '' + html.stripScripts() + '
    '; + depth = 3; + break; + case 'TD': + div.innerHTML = '
    ' + html.stripScripts() + '
    '; + depth = 4; + } + $A(element.childNodes).each(function(node) { element.removeChild(node) }); + depth.times(function() { div = div.firstChild }); + $A(div.childNodes).each(function(node) { element.appendChild(node) }); + } else { + element.innerHTML = html.stripScripts(); + } + setTimeout(function() { html.evalScripts() }, 10); + return element; + } +} +else if (Prototype.Browser.Gecko) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1) ? 0.999999 : + (value === '') ? '' : (value < 0.00001) ? 0 : value; + return element; + }; +} + +Element._attributeTranslations = { + names: { + colspan: "colSpan", + rowspan: "rowSpan", + valign: "vAlign", + datetime: "dateTime", + accesskey: "accessKey", + tabindex: "tabIndex", + enctype: "encType", + maxlength: "maxLength", + readonly: "readOnly", + longdesc: "longDesc" + }, + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _flag: function(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + }, + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + var node = element.getAttributeNode('title'); + return node.specified ? node.nodeValue : null; + } + } +}; + +(function() { + Object.extend(this, { + href: this._getAttr, + src: this._getAttr, + type: this._getAttr, + disabled: this._flag, + checked: this._flag, + readonly: this._flag, + multiple: this._flag + }); +}).call(Element._attributeTranslations.values); + +Element.Methods.Simulated = { + hasAttribute: function(element, attribute) { + var t = Element._attributeTranslations, node; + attribute = t.names[attribute] || attribute; + node = $(element).getAttributeNode(attribute); + return node && node.specified; + } +}; + +Element.Methods.ByTag = {}; + +Object.extend(Element, Element.Methods); + +if (!Prototype.BrowserFeatures.ElementExtensions && + document.createElement('div').__proto__) { + window.HTMLElement = {}; + window.HTMLElement.prototype = document.createElement('div').__proto__; + Prototype.BrowserFeatures.ElementExtensions = true; +} + +Element.hasAttribute = function(element, attribute) { + if (element.hasAttribute) return element.hasAttribute(attribute); + return Element.Methods.Simulated.hasAttribute(element, attribute); +}; + +Element.addMethods = function(methods) { + var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; + + if (!methods) { + Object.extend(Form, Form.Methods); + Object.extend(Form.Element, Form.Element.Methods); + Object.extend(Element.Methods.ByTag, { + "FORM": Object.clone(Form.Methods), + "INPUT": Object.clone(Form.Element.Methods), + "SELECT": Object.clone(Form.Element.Methods), + "TEXTAREA": Object.clone(Form.Element.Methods) + }); + } + + if (arguments.length == 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) Object.extend(Element.Methods, methods || {}); + else { + if (tagName.constructor == Array) tagName.each(extend); + else extend(tagName); + } + + function extend(tagName) { + tagName = tagName.toUpperCase(); + if (!Element.Methods.ByTag[tagName]) + Element.Methods.ByTag[tagName] = {}; + Object.extend(Element.Methods.ByTag[tagName], methods); + } + + function copy(methods, destination, onlyIfAbsent) { + onlyIfAbsent = onlyIfAbsent || false; + var cache = Element.extend.cache; + for (var property in methods) { + var value = methods[property]; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = cache.findOrStore(value); + } + } + + function findDOMClass(tagName) { + var klass; + var trans = { + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": + "FrameSet", "IFRAME": "IFrame" + }; + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName.capitalize() + 'Element'; + if (window[klass]) return window[klass]; + + window[klass] = {}; + window[klass].prototype = document.createElement(tagName).__proto__; + return window[klass]; + } + + if (F.ElementExtensions) { + copy(Element.Methods, HTMLElement.prototype); + copy(Element.Methods.Simulated, HTMLElement.prototype, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (typeof klass == "undefined") continue; + copy(T[tag], klass.prototype); + } + } + + Object.extend(Element, Element.Methods); + delete Element.ByTag; +}; + +var Toggle = { display: Element.toggle }; + +/*--------------------------------------------------------------------------*/ + +Abstract.Insertion = function(adjacency) { + this.adjacency = adjacency; +} + +Abstract.Insertion.prototype = { + initialize: function(element, content) { + this.element = $(element); + this.content = content.stripScripts(); + + if (this.adjacency && this.element.insertAdjacentHTML) { + try { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } catch (e) { + var tagName = this.element.tagName.toUpperCase(); + if (['TBODY', 'TR'].include(tagName)) { + this.insertContent(this.contentFromAnonymousTable()); + } else { + throw e; + } + } + } else { + this.range = this.element.ownerDocument.createRange(); + if (this.initializeRange) this.initializeRange(); + this.insertContent([this.range.createContextualFragment(this.content)]); + } + + setTimeout(function() {content.evalScripts()}, 10); + }, + + contentFromAnonymousTable: function() { + var div = document.createElement('div'); + div.innerHTML = '' + this.content + '
    '; + return $A(div.childNodes[0].childNodes[0].childNodes); + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, this.element); + }).bind(this)); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function(fragments) { + fragments.reverse(false).each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.appendChild(fragment); + }).bind(this)); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, + this.element.nextSibling); + }).bind(this)); + } +}); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set($A(this).concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set($A(this).without(classNameToRemove).join(' ')); + }, + + toString: function() { + return $A(this).join(' '); + } +}; + +Object.extend(Element.ClassNames.prototype, Enumerable); +/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, + * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style + * license. Please see http://www.yui-ext.com/ for more information. */ + +var Selector = Class.create(); + +Selector.prototype = { + initialize: function(expression) { + this.expression = expression.strip(); + this.compileMatcher(); + }, + + compileMatcher: function() { + // Selectors with namespaced attributes can't use the XPath version + if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression)) + return this.compileXPathMatcher(); + + var e = this.expression, ps = Selector.patterns, h = Selector.handlers, + c = Selector.criteria, le, p, m; + + if (Selector._cache[e]) { + this.matcher = Selector._cache[e]; return; + } + this.matcher = ["this.matcher = function(root) {", + "var r = root, h = Selector.handlers, c = false, n;"]; + + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + this.matcher.push(typeof c[i] == 'function' ? c[i](m) : + new Template(c[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.matcher.push("return h.unique(n);\n}"); + eval(this.matcher.join('\n')); + Selector._cache[this.expression] = this.matcher; + }, + + compileXPathMatcher: function() { + var e = this.expression, ps = Selector.patterns, + x = Selector.xpath, le, m; + + if (Selector._cache[e]) { + this.xpath = Selector._cache[e]; return; + } + + this.matcher = ['.//*']; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + if (m = e.match(ps[i])) { + this.matcher.push(typeof x[i] == 'function' ? x[i](m) : + new Template(x[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.xpath = this.matcher.join(''); + Selector._cache[this.expression] = this.xpath; + }, + + findElements: function(root) { + root = root || document; + if (this.xpath) return document._getElementsByXPath(this.xpath, root); + return this.matcher(root); + }, + + match: function(element) { + return this.findElements(document).include(element); + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#"; + } +}; + +Object.extend(Selector, { + _cache: {}, + + xpath: { + descendant: "//*", + child: "/*", + adjacent: "/following-sibling::*[1]", + laterSibling: '/following-sibling::*', + tagName: function(m) { + if (m[1] == '*') return ''; + return "[local-name()='" + m[1].toLowerCase() + + "' or local-name()='" + m[1].toUpperCase() + "']"; + }, + className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", + id: "[@id='#{1}']", + attrPresence: "[@#{1}]", + attr: function(m) { + m[3] = m[5] || m[6]; + return new Template(Selector.xpath.operators[m[2]]).evaluate(m); + }, + pseudo: function(m) { + var h = Selector.xpath.pseudos[m[1]]; + if (!h) return ''; + if (typeof h === 'function') return h(m); + return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); + }, + operators: { + '=': "[@#{1}='#{3}']", + '!=': "[@#{1}!='#{3}']", + '^=': "[starts-with(@#{1}, '#{3}')]", + '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", + '*=': "[contains(@#{1}, '#{3}')]", + '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", + '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" + }, + pseudos: { + 'first-child': '[not(preceding-sibling::*)]', + 'last-child': '[not(following-sibling::*)]', + 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', + 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", + 'checked': "[@checked]", + 'disabled': "[@disabled]", + 'enabled': "[not(@disabled)]", + 'not': function(m) { + var e = m[6], p = Selector.patterns, + x = Selector.xpath, le, m, v; + + var exclusion = []; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in p) { + if (m = e.match(p[i])) { + v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m); + exclusion.push("(" + v.substring(1, v.length - 1) + ")"); + e = e.replace(m[0], ''); + break; + } + } + } + return "[not(" + exclusion.join(" and ") + ")]"; + }, + 'nth-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); + }, + 'nth-last-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); + }, + 'nth-of-type': function(m) { + return Selector.xpath.pseudos.nth("position() ", m); + }, + 'nth-last-of-type': function(m) { + return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); + }, + 'first-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); + }, + 'last-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); + }, + 'only-of-type': function(m) { + var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); + }, + nth: function(fragment, m) { + var mm, formula = m[6], predicate; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + if (mm = formula.match(/^(\d+)$/)) // digit only + return '[' + fragment + "= " + mm[1] + ']'; + if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (mm[1] == "-") mm[1] = -1; + var a = mm[1] ? Number(mm[1]) : 1; + var b = mm[2] ? Number(mm[2]) : 0; + predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + + "((#{fragment} - #{b}) div #{a} >= 0)]"; + return new Template(predicate).evaluate({ + fragment: fragment, a: a, b: b }); + } + } + } + }, + + criteria: { + tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', + className: 'n = h.className(n, r, "#{1}", c); c = false;', + id: 'n = h.id(n, r, "#{1}", c); c = false;', + attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;', + attr: function(m) { + m[3] = (m[5] || m[6]); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m); + }, + pseudo: function(m) { + if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); + return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); + }, + descendant: 'c = "descendant";', + child: 'c = "child";', + adjacent: 'c = "adjacent";', + laterSibling: 'c = "laterSibling";' + }, + + patterns: { + // combinators must be listed first + // (and descendant needs to be last combinator) + laterSibling: /^\s*~\s*/, + child: /^\s*>\s*/, + adjacent: /^\s*\+\s*/, + descendant: /^\s/, + + // selectors follow + tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, + id: /^#([\w\-\*]+)(\b|$)/, + className: /^\.([\w\-\*]+)(\b|$)/, + pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/, + attrPresence: /^\[([\w]+)\]/, + attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/ + }, + + handlers: { + // UTILITY FUNCTIONS + // joins two collections + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + a.push(node); + return a; + }, + + // marks an array of nodes for counting + mark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._counted = true; + return nodes; + }, + + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._counted = undefined; + return nodes; + }, + + // mark each child node with its position (for nth calls) + // "ofType" flag indicates whether we're indexing for nth-of-type + // rather than nth-child + index: function(parentNode, reverse, ofType) { + parentNode._counted = true; + if (reverse) { + for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { + node = nodes[i]; + if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; + } + } else { + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) + if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; + } + }, + + // filters out duplicates and extends all nodes + unique: function(nodes) { + if (nodes.length == 0) return nodes; + var results = [], n; + for (var i = 0, l = nodes.length; i < l; i++) + if (!(n = nodes[i])._counted) { + n._counted = true; + results.push(Element.extend(n)); + } + return Selector.handlers.unmark(results); + }, + + // COMBINATOR FUNCTIONS + descendant: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName('*')); + return results; + }, + + child: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) { + for (var j = 0, children = [], child; child = node.childNodes[j]; j++) + if (child.nodeType == 1 && child.tagName != '!') results.push(child); + } + return results; + }, + + adjacent: function(nodes) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + var next = this.nextElementSibling(node); + if (next) results.push(next); + } + return results; + }, + + laterSibling: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, Element.nextSiblings(node)); + return results; + }, + + nextElementSibling: function(node) { + while (node = node.nextSibling) + if (node.nodeType == 1) return node; + return null; + }, + + previousElementSibling: function(node) { + while (node = node.previousSibling) + if (node.nodeType == 1) return node; + return null; + }, + + // TOKEN FUNCTIONS + tagName: function(nodes, root, tagName, combinator) { + tagName = tagName.toUpperCase(); + var results = [], h = Selector.handlers; + if (nodes) { + if (combinator) { + // fastlane for ordinary descendant combinators + if (combinator == "descendant") { + for (var i = 0, node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName(tagName)); + return results; + } else nodes = this[combinator](nodes); + if (tagName == "*") return nodes; + } + for (var i = 0, node; node = nodes[i]; i++) + if (node.tagName.toUpperCase() == tagName) results.push(node); + return results; + } else return root.getElementsByTagName(tagName); + }, + + id: function(nodes, root, id, combinator) { + var targetNode = $(id), h = Selector.handlers; + if (!nodes && root == document) return targetNode ? [targetNode] : []; + if (nodes) { + if (combinator) { + if (combinator == 'child') { + for (var i = 0, node; node = nodes[i]; i++) + if (targetNode.parentNode == node) return [targetNode]; + } else if (combinator == 'descendant') { + for (var i = 0, node; node = nodes[i]; i++) + if (Element.descendantOf(targetNode, node)) return [targetNode]; + } else if (combinator == 'adjacent') { + for (var i = 0, node; node = nodes[i]; i++) + if (Selector.handlers.previousElementSibling(targetNode) == node) + return [targetNode]; + } else nodes = h[combinator](nodes); + } + for (var i = 0, node; node = nodes[i]; i++) + if (node == targetNode) return [targetNode]; + return []; + } + return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; + }, + + className: function(nodes, root, className, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + return Selector.handlers.byClassName(nodes, root, className); + }, + + byClassName: function(nodes, root, className) { + if (!nodes) nodes = Selector.handlers.descendant([root]); + var needle = ' ' + className + ' '; + for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { + nodeClassName = node.className; + if (nodeClassName.length == 0) continue; + if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) + results.push(node); + } + return results; + }, + + attrPresence: function(nodes, root, attr) { + var results = []; + for (var i = 0, node; node = nodes[i]; i++) + if (Element.hasAttribute(node, attr)) results.push(node); + return results; + }, + + attr: function(nodes, root, attr, value, operator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + var handler = Selector.operators[operator], results = []; + for (var i = 0, node; node = nodes[i]; i++) { + var nodeValue = Element.readAttribute(node, attr); + if (nodeValue === null) continue; + if (handler(nodeValue, value)) results.push(node); + } + return results; + }, + + pseudo: function(nodes, name, value, root, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + if (!nodes) nodes = root.getElementsByTagName("*"); + return Selector.pseudos[name](nodes, value, root); + } + }, + + pseudos: { + 'first-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.previousElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'last-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.nextElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'only-child': function(nodes, value, root) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) + results.push(node); + return results; + }, + 'nth-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root); + }, + 'nth-last-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true); + }, + 'nth-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, false, true); + }, + 'nth-last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true, true); + }, + 'first-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, false, true); + }, + 'last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, true, true); + }, + 'only-of-type': function(nodes, formula, root) { + var p = Selector.pseudos; + return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); + }, + + // handles the an+b logic + getIndices: function(a, b, total) { + if (a == 0) return b > 0 ? [b] : []; + return $R(1, total).inject([], function(memo, i) { + if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); + return memo; + }); + }, + + // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type + nth: function(nodes, formula, root, reverse, ofType) { + if (nodes.length == 0) return []; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + var h = Selector.handlers, results = [], indexed = [], m; + h.mark(nodes); + for (var i = 0, node; node = nodes[i]; i++) { + if (!node.parentNode._counted) { + h.index(node.parentNode, reverse, ofType); + indexed.push(node.parentNode); + } + } + if (formula.match(/^\d+$/)) { // just a number + formula = Number(formula); + for (var i = 0, node; node = nodes[i]; i++) + if (node.nodeIndex == formula) results.push(node); + } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (m[1] == "-") m[1] = -1; + var a = m[1] ? Number(m[1]) : 1; + var b = m[2] ? Number(m[2]) : 0; + var indices = Selector.pseudos.getIndices(a, b, nodes.length); + for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { + for (var j = 0; j < l; j++) + if (node.nodeIndex == indices[j]) results.push(node); + } + } + h.unmark(nodes); + h.unmark(indexed); + return results; + }, + + 'empty': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + // IE treats comments as element nodes + if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; + results.push(node); + } + return results; + }, + + 'not': function(nodes, selector, root) { + var h = Selector.handlers, selectorType, m; + var exclusions = new Selector(selector).findElements(root); + h.mark(exclusions); + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node._counted) results.push(node); + h.unmark(exclusions); + return results; + }, + + 'enabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node.disabled) results.push(node); + return results; + }, + + 'disabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.disabled) results.push(node); + return results; + }, + + 'checked': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.checked) results.push(node); + return results; + } + }, + + operators: { + '=': function(nv, v) { return nv == v; }, + '!=': function(nv, v) { return nv != v; }, + '^=': function(nv, v) { return nv.startsWith(v); }, + '$=': function(nv, v) { return nv.endsWith(v); }, + '*=': function(nv, v) { return nv.include(v); }, + '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, + '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } + }, + + matchElements: function(elements, expression) { + var matches = new Selector(expression).findElements(), h = Selector.handlers; + h.mark(matches); + for (var i = 0, results = [], element; element = elements[i]; i++) + if (element._counted) results.push(element); + h.unmark(matches); + return results; + }, + + findElement: function(elements, expression, index) { + if (typeof expression == 'number') { + index = expression; expression = false; + } + return Selector.matchElements(elements, expression || '*')[index || 0]; + }, + + findChildElements: function(element, expressions) { + var exprs = expressions.join(','), expressions = []; + exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + var results = [], h = Selector.handlers; + for (var i = 0, l = expressions.length, selector; i < l; i++) { + selector = new Selector(expressions[i].strip()); + h.concat(results, selector.findElements(element)); + } + return (l > 1) ? h.unique(results) : results; + } +}); + +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} +var Form = { + reset: function(form) { + $(form).reset(); + return form; + }, + + serializeElements: function(elements, getHash) { + var data = elements.inject({}, function(result, element) { + if (!element.disabled && element.name) { + var key = element.name, value = $(element).getValue(); + if (value != null) { + if (key in result) { + if (result[key].constructor != Array) result[key] = [result[key]]; + result[key].push(value); + } + else result[key] = value; + } + } + return result; + }); + + return getHash ? data : Hash.toQueryString(data); + } +}; + +Form.Methods = { + serialize: function(form, getHash) { + return Form.serializeElements(Form.getElements(form), getHash); + }, + + getElements: function(form) { + return $A($(form).getElementsByTagName('*')).inject([], + function(elements, child) { + if (Form.Element.Serializers[child.tagName.toLowerCase()]) + elements.push(Element.extend(child)); + return elements; + } + ); + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) return $A(inputs).map(Element.extend); + + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || (name && input.name != name)) + continue; + matchingInputs.push(Element.extend(input)); + } + + return matchingInputs; + }, + + disable: function(form) { + form = $(form); + Form.getElements(form).invoke('disable'); + return form; + }, + + enable: function(form) { + form = $(form); + Form.getElements(form).invoke('enable'); + return form; + }, + + findFirstElement: function(form) { + return $(form).getElements().find(function(element) { + return element.type != 'hidden' && !element.disabled && + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + form.findFirstElement().activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || {}); + + var params = options.parameters; + options.parameters = form.serialize(true); + + if (params) { + if (typeof params == 'string') params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(form.readAttribute('action'), options); + } +} + +/*--------------------------------------------------------------------------*/ + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; + }, + + select: function(element) { + $(element).select(); + return element; + } +} + +Form.Element.Methods = { + serialize: function(element) { + element = $(element); + if (!element.disabled && element.name) { + var value = element.getValue(); + if (value != undefined) { + var pair = {}; + pair[element.name] = value; + return Hash.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + try { + element.focus(); + if (element.select && (element.tagName.toLowerCase() != 'input' || + !['button', 'reset', 'submit'].include(element.type))) + element.select(); + } catch (e) {} + return element; + }, + + disable: function(element) { + element = $(element); + element.blur(); + element.disabled = true; + return element; + }, + + enable: function(element) { + element = $(element); + element.disabled = false; + return element; + } +} + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; +var $F = Form.Element.Methods.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + default: + return Form.Element.Serializers.textarea(element); + } + }, + + inputSelector: function(element) { + return element.checked ? element.value : null; + }, + + textarea: function(element) { + return element.value; + }, + + select: function(element) { + return this[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + }, + + selectOne: function(element) { + var index = element.selectedIndex; + return index >= 0 ? this.optionValue(element.options[index]) : null; + }, + + selectMany: function(element) { + var values, length = element.length; + if (!length) return null; + + for (var i = 0, values = []; i < length; i++) { + var opt = element.options[i]; + if (opt.selected) values.push(this.optionValue(opt)); + } + return values; + }, + + optionValue: function(opt) { + // extend element because hasAttribute may not be native + return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; + } +} + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + var changed = ('string' == typeof this.lastValue && 'string' == typeof value + ? this.lastValue != value : String(this.lastValue) != String(value)); + if (changed) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + Form.getElements(this.element).each(this.registerCallback.bind(this)); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + default: + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, + + element: function(event) { + return $(event.target || event.srcElement); + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + event.cancelBubble = true; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0, length = Event.observers.length; i < length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (Prototype.Browser.WebKit || element.attachEvent)) + name = 'keydown'; + + Event._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (Prototype.Browser.WebKit || element.attachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + try { + element.detachEvent('on' + name, observer); + } catch (e) {} + } + } +}); + +/* prevent memory leaks in IE */ +if (Prototype.Browser.IE) + Event.observe(window, 'unload', Event.unloadCache, false); +var Position = { + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if(element.tagName=='BODY') break; + var p = Element.getStyle(element, 'position'); + if (p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; + }, + + offsetParent: function(element) { + if (element.offsetParent) return element.offsetParent; + if (element == document.body) return element; + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return element; + + return document.body; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + page: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent == document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + if (!window.opera || element.tagName=='BODY') { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + + return [valueL, valueT]; + }, + + clone: function(source, target) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || {}) + + // find page position of source + source = $(source); + var p = Position.page(source); + + // find coordinate system to use + target = $(target); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(target,'position') == 'absolute') { + parent = Position.offsetParent(target); + delta = Position.page(parent); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if(options.setWidth) target.style.width = source.offsetWidth + 'px'; + if(options.setHeight) target.style.height = source.offsetHeight + 'px'; + }, + + absolutize: function(element) { + element = $(element); + if (element.style.position == 'absolute') return; + Position.prepare(); + + var offsets = Position.positionedOffset(element); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.width = width + 'px'; + element.style.height = height + 'px'; + }, + + relativize: function(element) { + element = $(element); + if (element.style.position == 'relative') return; + Position.prepare(); + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + } +} + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned. For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (Prototype.Browser.WebKit) { + Position.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return [valueL, valueT]; + } +} + +Element.addMethods(); \ No newline at end of file diff --git a/xCAT-web/js/readme.txt b/xCAT-web/js/readme.txt new file mode 100644 index 000000000..dfe66940e --- /dev/null +++ b/xCAT-web/js/readme.txt @@ -0,0 +1,23 @@ + +Current version: 1.5 from March 23, 2007 + +============================================================================================ + +If you want to use dhtmlXTree in a commercial product, you have to buy a commercial license. +Please contact us at info@scand.com + +Some comments on commercial usage: + +1.Commercial License allows you to use dhtmlXTree on unlimited number of Websites or sites +but only in one commercial project(application). According to this license we provide you +with support (consultations) and free-of-charge bug fixing during 1 month. +This license costs $99. + +2.Enterprise License allows you to use dhtmlXTree in unlimited number of projects on condition +that all of them are projects of one company. Support period is 1 year. +This license costs $399. + + +According to both licenses you are allowed to make changes to the code of Software but Scand LLC +still owns the code and you are not allowed to distribute changed code. + diff --git a/xCAT-web/js/scriptaculous.js b/xCAT-web/js/scriptaculous.js new file mode 100644 index 000000000..7c472a600 --- /dev/null +++ b/xCAT-web/js/scriptaculous.js @@ -0,0 +1,58 @@ +// script.aculo.us scriptaculous.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +var Scriptaculous = { + Version: '1.7.1_beta3', + require: function(libraryName) { + // inserting via DOM fails in Safari 2.0, so brute force approach + document.write(''); + }, + REQUIRED_PROTOTYPE: '1.5.1', + load: function() { + function convertVersionString(versionString){ + var r = versionString.split('.'); + return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]); + } + + if((typeof Prototype=='undefined') || + (typeof Element == 'undefined') || + (typeof Element.Methods=='undefined') || + (convertVersionString(Prototype.Version) < + convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE))) + throw("script.aculo.us requires the Prototype JavaScript framework >= " + + Scriptaculous.REQUIRED_PROTOTYPE); + + $A(document.getElementsByTagName("script")).findAll( function(s) { + return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/)) + }).each( function(s) { + var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,''); + var includes = s.src.match(/\?.*load=([a-z,]*)/); + (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each( + function(include) { Scriptaculous.require(path+include+'.js') }); + }); + } +} + +Scriptaculous.load(); \ No newline at end of file diff --git a/xCAT-web/js/slider.js b/xCAT-web/js/slider.js new file mode 100644 index 000000000..c1a84ebfb --- /dev/null +++ b/xCAT-web/js/slider.js @@ -0,0 +1,277 @@ +// script.aculo.us slider.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 + +// Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +if(!Control) var Control = {}; +Control.Slider = Class.create(); + +// options: +// axis: 'vertical', or 'horizontal' (default) +// +// callbacks: +// onChange(value) +// onSlide(value) +Control.Slider.prototype = { + initialize: function(handle, track, options) { + var slider = this; + + if(handle instanceof Array) { + this.handles = handle.collect( function(e) { return $(e) }); + } else { + this.handles = [$(handle)]; + } + + this.track = $(track); + this.options = options || {}; + + this.axis = this.options.axis || 'horizontal'; + this.increment = this.options.increment || 1; + this.step = parseInt(this.options.step || '1'); + this.range = this.options.range || $R(0,1); + + this.value = 0; // assure backwards compat + this.values = this.handles.map( function() { return 0 }); + this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false; + this.options.startSpan = $(this.options.startSpan || null); + this.options.endSpan = $(this.options.endSpan || null); + + this.restricted = this.options.restricted || false; + + this.maximum = this.options.maximum || this.range.end; + this.minimum = this.options.minimum || this.range.start; + + // Will be used to align the handle onto the track, if necessary + this.alignX = parseInt(this.options.alignX || '0'); + this.alignY = parseInt(this.options.alignY || '0'); + + this.trackLength = this.maximumOffset() - this.minimumOffset(); + + this.handleLength = this.isVertical() ? + (this.handles[0].offsetHeight != 0 ? + this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : + (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : + this.handles[0].style.width.replace(/px$/,"")); + + this.active = false; + this.dragging = false; + this.disabled = false; + + if(this.options.disabled) this.setDisabled(); + + // Allowed values array + this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false; + if(this.allowedValues) { + this.minimum = this.allowedValues.min(); + this.maximum = this.allowedValues.max(); + } + + this.eventMouseDown = this.startDrag.bindAsEventListener(this); + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.update.bindAsEventListener(this); + + // Initialize handles in reverse (make sure first handle is active) + this.handles.each( function(h,i) { + i = slider.handles.length-1-i; + slider.setValue(parseFloat( + (slider.options.sliderValue instanceof Array ? + slider.options.sliderValue[i] : slider.options.sliderValue) || + slider.range.start), i); + Element.makePositioned(h); // fix IE + Event.observe(h, "mousedown", slider.eventMouseDown); + }); + + Event.observe(this.track, "mousedown", this.eventMouseDown); + Event.observe(document, "mouseup", this.eventMouseUp); + Event.observe(document, "mousemove", this.eventMouseMove); + + this.initialized = true; + }, + dispose: function() { + var slider = this; + Event.stopObserving(this.track, "mousedown", this.eventMouseDown); + Event.stopObserving(document, "mouseup", this.eventMouseUp); + Event.stopObserving(document, "mousemove", this.eventMouseMove); + this.handles.each( function(h) { + Event.stopObserving(h, "mousedown", slider.eventMouseDown); + }); + }, + setDisabled: function(){ + this.disabled = true; + }, + setEnabled: function(){ + this.disabled = false; + }, + getNearestValue: function(value){ + if(this.allowedValues){ + if(value >= this.allowedValues.max()) return(this.allowedValues.max()); + if(value <= this.allowedValues.min()) return(this.allowedValues.min()); + + var offset = Math.abs(this.allowedValues[0] - value); + var newValue = this.allowedValues[0]; + this.allowedValues.each( function(v) { + var currentOffset = Math.abs(v - value); + if(currentOffset <= offset){ + newValue = v; + offset = currentOffset; + } + }); + return newValue; + } + if(value > this.range.end) return this.range.end; + if(value < this.range.start) return this.range.start; + return value; + }, + setValue: function(sliderValue, handleIdx){ + if(!this.active) { + this.activeHandleIdx = handleIdx || 0; + this.activeHandle = this.handles[this.activeHandleIdx]; + this.updateStyles(); + } + handleIdx = handleIdx || this.activeHandleIdx || 0; + if(this.initialized && this.restricted) { + if((handleIdx>0) && (sliderValuethis.values[handleIdx+1])) + sliderValue = this.values[handleIdx+1]; + } + sliderValue = this.getNearestValue(sliderValue); + this.values[handleIdx] = sliderValue; + this.value = this.values[0]; // assure backwards compat + + this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = + this.translateToPx(sliderValue); + + this.drawSpans(); + if(!this.dragging || !this.event) this.updateFinished(); + }, + setValueBy: function(delta, handleIdx) { + this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, + handleIdx || this.activeHandleIdx || 0); + }, + translateToPx: function(value) { + return Math.round( + ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * + (value - this.range.start)) + "px"; + }, + translateToValue: function(offset) { + return ((offset/(this.trackLength-this.handleLength) * + (this.range.end-this.range.start)) + this.range.start); + }, + getRange: function(range) { + var v = this.values.sortBy(Prototype.K); + range = range || 0; + return $R(v[range],v[range+1]); + }, + minimumOffset: function(){ + return(this.isVertical() ? this.alignY : this.alignX); + }, + maximumOffset: function(){ + return(this.isVertical() ? + (this.track.offsetHeight != 0 ? this.track.offsetHeight : + this.track.style.height.replace(/px$/,"")) - this.alignY : + (this.track.offsetWidth != 0 ? this.track.offsetWidth : + this.track.style.width.replace(/px$/,"")) - this.alignY); + }, + isVertical: function(){ + return (this.axis == 'vertical'); + }, + drawSpans: function() { + var slider = this; + if(this.spans) + $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) }); + if(this.options.startSpan) + this.setSpan(this.options.startSpan, + $R(0, this.values.length>1 ? this.getRange(0).min() : this.value )); + if(this.options.endSpan) + this.setSpan(this.options.endSpan, + $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum)); + }, + setSpan: function(span, range) { + if(this.isVertical()) { + span.style.top = this.translateToPx(range.start); + span.style.height = this.translateToPx(range.end - range.start + this.range.start); + } else { + span.style.left = this.translateToPx(range.start); + span.style.width = this.translateToPx(range.end - range.start + this.range.start); + } + }, + updateStyles: function() { + this.handles.each( function(h){ Element.removeClassName(h, 'selected') }); + Element.addClassName(this.activeHandle, 'selected'); + }, + startDrag: function(event) { + if(Event.isLeftClick(event)) { + if(!this.disabled){ + this.active = true; + + var handle = Event.element(event); + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var track = handle; + if(track==this.track) { + var offsets = Position.cumulativeOffset(this.track); + this.event = event; + this.setValue(this.translateToValue( + (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2) + )); + var offsets = Position.cumulativeOffset(this.activeHandle); + this.offsetX = (pointer[0] - offsets[0]); + this.offsetY = (pointer[1] - offsets[1]); + } else { + // find the handle (prevents issues with Safari) + while((this.handles.indexOf(handle) == -1) && handle.parentNode) + handle = handle.parentNode; + + if(this.handles.indexOf(handle)!=-1) { + this.activeHandle = handle; + this.activeHandleIdx = this.handles.indexOf(this.activeHandle); + this.updateStyles(); + + var offsets = Position.cumulativeOffset(this.activeHandle); + this.offsetX = (pointer[0] - offsets[0]); + this.offsetY = (pointer[1] - offsets[1]); + } + } + } + Event.stop(event); + } + }, + update: function(event) { + if(this.active) { + if(!this.dragging) this.dragging = true; + this.draw(event); + if(Prototype.Browser.WebKit) window.scrollBy(0,0); + Event.stop(event); + } + }, + draw: function(event) { + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var offsets = Position.cumulativeOffset(this.track); + pointer[0] -= this.offsetX + offsets[0]; + pointer[1] -= this.offsetY + offsets[1]; + this.event = event; + this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] )); + if(this.initialized && this.options.onSlide) + this.options.onSlide(this.values.length>1 ? this.values : this.value, this); + }, + endDrag: function(event) { + if(this.active && this.dragging) { + this.finishDrag(event, true); + Event.stop(event); + } + this.active = false; + this.dragging = false; + }, + finishDrag: function(event, success) { + this.active = false; + this.dragging = false; + this.updateFinished(); + }, + updateFinished: function() { + if(this.initialized && this.options.onChange) + this.options.onChange(this.values.length>1 ? this.values : this.value, this); + this.event = null; + } +} \ No newline at end of file diff --git a/xCAT-web/js/sound.js b/xCAT-web/js/sound.js new file mode 100644 index 000000000..164c79a08 --- /dev/null +++ b/xCAT-web/js/sound.js @@ -0,0 +1,60 @@ +// script.aculo.us sound.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// Based on code created by Jules Gravinese (http://www.webveteran.com/) +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +Sound = { + tracks: {}, + _enabled: true, + template: + new Template(''), + enable: function(){ + Sound._enabled = true; + }, + disable: function(){ + Sound._enabled = false; + }, + play: function(url){ + if(!Sound._enabled) return; + var options = Object.extend({ + track: 'global', url: url, replace: false + }, arguments[1] || {}); + + if(options.replace && this.tracks[options.track]) { + $R(0, this.tracks[options.track].id).each(function(id){ + var sound = $('sound_'+options.track+'_'+id); + sound.Stop && sound.Stop(); + sound.remove(); + }) + this.tracks[options.track] = null; + } + + if(!this.tracks[options.track]) + this.tracks[options.track] = { id: 0 } + else + this.tracks[options.track].id++; + + options.id = this.tracks[options.track].id; + if (Prototype.Browser.IE) { + var sound = document.createElement('bgsound'); + sound.setAttribute('id','sound_'+options.track+'_'+options.id); + sound.setAttribute('src',options.url); + sound.setAttribute('loop','1'); + sound.setAttribute('autostart','true'); + $$('body')[0].appendChild(sound); + } + else + new Insertion.Bottom($$('body')[0], Sound.template.evaluate(options)); + } +}; + +if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){ + if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 })) + Sound.template = new Template('') + else + Sound.play = function(){} +} diff --git a/xCAT-web/js/unittest.js b/xCAT-web/js/unittest.js new file mode 100644 index 000000000..d2dff8b2c --- /dev/null +++ b/xCAT-web/js/unittest.js @@ -0,0 +1,564 @@ +// script.aculo.us unittest.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com) +// (c) 2005-2007 Michael Schuerig (http://www.schuerig.de/michael/) +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +// experimental, Firefox-only +Event.simulateMouse = function(element, eventName) { + var options = Object.extend({ + pointerX: 0, + pointerY: 0, + buttons: 0, + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false + }, arguments[2] || {}); + var oEvent = document.createEvent("MouseEvents"); + oEvent.initMouseEvent(eventName, true, true, document.defaultView, + options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, $(element)); + + if(this.mark) Element.remove(this.mark); + this.mark = document.createElement('div'); + this.mark.appendChild(document.createTextNode(" ")); + document.body.appendChild(this.mark); + this.mark.style.position = 'absolute'; + this.mark.style.top = options.pointerY + "px"; + this.mark.style.left = options.pointerX + "px"; + this.mark.style.width = "5px"; + this.mark.style.height = "5px;"; + this.mark.style.borderTop = "1px solid red;" + this.mark.style.borderLeft = "1px solid red;" + + if(this.step) + alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options)); + + $(element).dispatchEvent(oEvent); +}; + +// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2. +// You need to downgrade to 1.0.4 for now to get this working +// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much +Event.simulateKey = function(element, eventName) { + var options = Object.extend({ + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false, + keyCode: 0, + charCode: 0 + }, arguments[2] || {}); + + var oEvent = document.createEvent("KeyEvents"); + oEvent.initKeyEvent(eventName, true, true, window, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, + options.keyCode, options.charCode ); + $(element).dispatchEvent(oEvent); +}; + +Event.simulateKeys = function(element, command) { + for(var i=0; i' + + '' + + '' + + '' + + '
    StatusTestMessage
    '; + this.logsummary = $('logsummary') + this.loglines = $('loglines'); + }, + _toHTML: function(txt) { + return txt.escapeHTML().replace(/\n/g,"
    "); + }, + addLinksToResults: function(){ + $$("tr.failed .nameCell").each( function(td){ // todo: limit to children of this.log + td.title = "Run only this test" + Event.observe(td, 'click', function(){ window.location.search = "?tests=" + td.innerHTML;}); + }); + $$("tr.passed .nameCell").each( function(td){ // todo: limit to children of this.log + td.title = "Run all tests" + Event.observe(td, 'click', function(){ window.location.search = "";}); + }); + } +} + +Test.Unit.Runner = Class.create(); +Test.Unit.Runner.prototype = { + initialize: function(testcases) { + this.options = Object.extend({ + testLog: 'testlog' + }, arguments[1] || {}); + this.options.resultsURL = this.parseResultsURLQueryParameter(); + this.options.tests = this.parseTestsQueryParameter(); + if (this.options.testLog) { + this.options.testLog = $(this.options.testLog) || null; + } + if(this.options.tests) { + this.tests = []; + for(var i = 0; i < this.options.tests.length; i++) { + if(/^test/.test(this.options.tests[i])) { + this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"])); + } + } + } else { + if (this.options.test) { + this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])]; + } else { + this.tests = []; + for(var testcase in testcases) { + if(/^test/.test(testcase)) { + this.tests.push( + new Test.Unit.Testcase( + this.options.context ? ' -> ' + this.options.titles[testcase] : testcase, + testcases[testcase], testcases["setup"], testcases["teardown"] + )); + } + } + } + } + this.currentTest = 0; + this.logger = new Test.Unit.Logger(this.options.testLog); + setTimeout(this.runTests.bind(this), 1000); + }, + parseResultsURLQueryParameter: function() { + return window.location.search.parseQuery()["resultsURL"]; + }, + parseTestsQueryParameter: function(){ + if (window.location.search.parseQuery()["tests"]){ + return window.location.search.parseQuery()["tests"].split(','); + }; + }, + // Returns: + // "ERROR" if there was an error, + // "FAILURE" if there was a failure, or + // "SUCCESS" if there was neither + getResult: function() { + var hasFailure = false; + for(var i=0;i 0) { + return "ERROR"; + } + if (this.tests[i].failures > 0) { + hasFailure = true; + } + } + if (hasFailure) { + return "FAILURE"; + } else { + return "SUCCESS"; + } + }, + postResults: function() { + if (this.options.resultsURL) { + new Ajax.Request(this.options.resultsURL, + { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false }); + } + }, + runTests: function() { + var test = this.tests[this.currentTest]; + if (!test) { + // finished! + this.postResults(); + this.logger.summary(this.summary()); + return; + } + if(!test.isWaiting) { + this.logger.start(test.name); + } + test.run(); + if(test.isWaiting) { + this.logger.message("Waiting for " + test.timeToWait + "ms"); + setTimeout(this.runTests.bind(this), test.timeToWait || 1000); + } else { + this.logger.finish(test.status(), test.summary()); + this.currentTest++; + // tail recursive, hopefully the browser will skip the stackframe + this.runTests(); + } + }, + summary: function() { + var assertions = 0; + var failures = 0; + var errors = 0; + var messages = []; + for(var i=0;i 0) return 'failed'; + if (this.errors > 0) return 'error'; + return 'passed'; + }, + assert: function(expression) { + var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"'; + try { expression ? this.pass() : + this.fail(message); } + catch(e) { this.error(e); } + }, + assertEqual: function(expected, actual) { + var message = arguments[2] || "assertEqual"; + try { (expected == actual) ? this.pass() : + this.fail(message + ': expected "' + Test.Unit.inspect(expected) + + '", actual "' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, + assertInspect: function(expected, actual) { + var message = arguments[2] || "assertInspect"; + try { (expected == actual.inspect()) ? this.pass() : + this.fail(message + ': expected "' + Test.Unit.inspect(expected) + + '", actual "' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, + assertEnumEqual: function(expected, actual) { + var message = arguments[2] || "assertEnumEqual"; + try { $A(expected).length == $A(actual).length && + expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ? + this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + + ', actual ' + Test.Unit.inspect(actual)); } + catch(e) { this.error(e); } + }, + assertNotEqual: function(expected, actual) { + var message = arguments[2] || "assertNotEqual"; + try { (expected != actual) ? this.pass() : + this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, + assertIdentical: function(expected, actual) { + var message = arguments[2] || "assertIdentical"; + try { (expected === actual) ? this.pass() : + this.fail(message + ': expected "' + Test.Unit.inspect(expected) + + '", actual "' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, + assertNotIdentical: function(expected, actual) { + var message = arguments[2] || "assertNotIdentical"; + try { !(expected === actual) ? this.pass() : + this.fail(message + ': expected "' + Test.Unit.inspect(expected) + + '", actual "' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, + assertNull: function(obj) { + var message = arguments[1] || 'assertNull' + try { (obj==null) ? this.pass() : + this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); } + catch(e) { this.error(e); } + }, + assertMatch: function(expected, actual) { + var message = arguments[2] || 'assertMatch'; + var regex = new RegExp(expected); + try { (regex.exec(actual)) ? this.pass() : + this.fail(message + ' : regex: "' + Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, + assertHidden: function(element) { + var message = arguments[1] || 'assertHidden'; + this.assertEqual("none", element.style.display, message); + }, + assertNotNull: function(object) { + var message = arguments[1] || 'assertNotNull'; + this.assert(object != null, message); + }, + assertType: function(expected, actual) { + var message = arguments[2] || 'assertType'; + try { + (actual.constructor == expected) ? this.pass() : + this.fail(message + ': expected "' + Test.Unit.inspect(expected) + + '", actual "' + (actual.constructor) + '"'); } + catch(e) { this.error(e); } + }, + assertNotOfType: function(expected, actual) { + var message = arguments[2] || 'assertNotOfType'; + try { + (actual.constructor != expected) ? this.pass() : + this.fail(message + ': expected "' + Test.Unit.inspect(expected) + + '", actual "' + (actual.constructor) + '"'); } + catch(e) { this.error(e); } + }, + assertInstanceOf: function(expected, actual) { + var message = arguments[2] || 'assertInstanceOf'; + try { + (actual instanceof expected) ? this.pass() : + this.fail(message + ": object was not an instance of the expected type"); } + catch(e) { this.error(e); } + }, + assertNotInstanceOf: function(expected, actual) { + var message = arguments[2] || 'assertNotInstanceOf'; + try { + !(actual instanceof expected) ? this.pass() : + this.fail(message + ": object was an instance of the not expected type"); } + catch(e) { this.error(e); } + }, + assertRespondsTo: function(method, obj) { + var message = arguments[2] || 'assertRespondsTo'; + try { + (obj[method] && typeof obj[method] == 'function') ? this.pass() : + this.fail(message + ": object doesn't respond to [" + method + "]"); } + catch(e) { this.error(e); } + }, + assertReturnsTrue: function(method, obj) { + var message = arguments[2] || 'assertReturnsTrue'; + try { + var m = obj[method]; + if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)]; + m() ? this.pass() : + this.fail(message + ": method returned false"); } + catch(e) { this.error(e); } + }, + assertReturnsFalse: function(method, obj) { + var message = arguments[2] || 'assertReturnsFalse'; + try { + var m = obj[method]; + if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)]; + !m() ? this.pass() : + this.fail(message + ": method returned true"); } + catch(e) { this.error(e); } + }, + assertRaise: function(exceptionName, method) { + var message = arguments[2] || 'assertRaise'; + try { + method(); + this.fail(message + ": exception expected but none was raised"); } + catch(e) { + ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e); + } + }, + assertElementsMatch: function() { + var expressions = $A(arguments), elements = $A(expressions.shift()); + if (elements.length != expressions.length) { + this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions'); + return false; + } + elements.zip(expressions).all(function(pair, index) { + var element = $(pair.first()), expression = pair.last(); + if (element.match(expression)) return true; + this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect()); + }.bind(this)) && this.pass(); + }, + assertElementMatches: function(element, expression) { + this.assertElementsMatch([element], expression); + }, + benchmark: function(operation, iterations) { + var startAt = new Date(); + (iterations || 1).times(operation); + var timeTaken = ((new Date())-startAt); + this.info((arguments[2] || 'Operation') + ' finished ' + + iterations + ' iterations in ' + (timeTaken/1000)+'s' ); + return timeTaken; + }, + _isVisible: function(element) { + element = $(element); + if(!element.parentNode) return true; + this.assertNotNull(element); + if(element.style && Element.getStyle(element, 'display') == 'none') + return false; + + return this._isVisible(element.parentNode); + }, + assertNotVisible: function(element) { + this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1])); + }, + assertVisible: function(element) { + this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1])); + }, + benchmark: function(operation, iterations) { + var startAt = new Date(); + (iterations || 1).times(operation); + var timeTaken = ((new Date())-startAt); + this.info((arguments[2] || 'Operation') + ' finished ' + + iterations + ' iterations in ' + (timeTaken/1000)+'s' ); + return timeTaken; + } +} + +Test.Unit.Testcase = Class.create(); +Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), { + initialize: function(name, test, setup, teardown) { + Test.Unit.Assertions.prototype.initialize.bind(this)(); + this.name = name; + + if(typeof test == 'string') { + test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,'); + test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)'); + this.test = function() { + eval('with(this){'+test+'}'); + } + } else { + this.test = test || function() {}; + } + + this.setup = setup || function() {}; + this.teardown = teardown || function() {}; + this.isWaiting = false; + this.timeToWait = 1000; + }, + wait: function(time, nextPart) { + this.isWaiting = true; + this.test = nextPart; + this.timeToWait = time; + }, + run: function() { + try { + try { + if (!this.isWaiting) this.setup.bind(this)(); + this.isWaiting = false; + this.test.bind(this)(); + } finally { + if(!this.isWaiting) { + this.teardown.bind(this)(); + } + } + } + catch(e) { this.error(e); } + } +}); + +// *EXPERIMENTAL* BDD-style testing to please non-technical folk +// This draws many ideas from RSpec http://rspec.rubyforge.org/ + +Test.setupBDDExtensionMethods = function(){ + var METHODMAP = { + shouldEqual: 'assertEqual', + shouldNotEqual: 'assertNotEqual', + shouldEqualEnum: 'assertEnumEqual', + shouldBeA: 'assertType', + shouldNotBeA: 'assertNotOfType', + shouldBeAn: 'assertType', + shouldNotBeAn: 'assertNotOfType', + shouldBeNull: 'assertNull', + shouldNotBeNull: 'assertNotNull', + + shouldBe: 'assertReturnsTrue', + shouldNotBe: 'assertReturnsFalse', + shouldRespondTo: 'assertRespondsTo' + }; + Test.BDDMethods = {}; + for(m in METHODMAP) { + Test.BDDMethods[m] = eval( + 'function(){'+ + 'var args = $A(arguments);'+ + 'var scope = args.shift();'+ + 'scope.'+METHODMAP[m]+'.apply(scope,(args || []).concat([this])); }'); + } + [Array.prototype, String.prototype, Number.prototype].each( + function(p){ Object.extend(p, Test.BDDMethods) } + ); +} + +Test.context = function(name, spec, log){ + Test.setupBDDExtensionMethods(); + + var compiledSpec = {}; + var titles = {}; + for(specName in spec) { + switch(specName){ + case "setup": + case "teardown": + compiledSpec[specName] = spec[specName]; + break; + default: + var testName = 'test'+specName.gsub(/\s+/,'-').camelize(); + var body = spec[specName].toString().split('\n').slice(1); + if(/^\{/.test(body[0])) body = body.slice(1); + body.pop(); + body = body.map(function(statement){ + return statement.strip() + }); + compiledSpec[testName] = body.join('\n'); + titles[testName] = specName; + } + } + new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name }); +}; \ No newline at end of file diff --git a/xCAT-web/js/windows.js b/xCAT-web/js/windows.js new file mode 100644 index 000000000..8e19d0826 --- /dev/null +++ b/xCAT-web/js/windows.js @@ -0,0 +1,58 @@ +var winNum = 1; +var zIndex = -100; + +function winNewWin(title){ + var dob; + var d; + dob = document.getElementById('content'); + + d = document.createElement('div'); + d.className = 'block'; + var winid = 'window' + winNum; + d.id = 'window' + winNum; + d.style.zIndex = zIndex; + zIndex++; + d.appendChild(winNewTitleBar(title)); + var cont = winNewContent(); + d.appendChild(cont); + dob.appendChild(d); + new Draggable(winid, {handle: 'handle'}); + winNum++; + /* return s the place to start writing */ + return cont; +} + +function winNewTitleBar(title){ + var ti = document.createElement('h3'); + ti.className = 'handle'; + var winid = 'window' + winNum; + var newHTML = ""; + newHTML += " " + newHTML += ""; + newHTML += " "; + newHTML += title; + ti.innerHTML = newHTML; + return ti; +} + +function winNewContent(){ + var doc = document.createElement('div'); + doc.className = 'blockContent'; + doc.id = 'winContent' + winNum ; + return doc; +} + +function winKill(wid){ + // Todo: make this random effects + var w = document.getElementById(wid); + Effect.Puff(w); + w.parentNode.removeChild(w); + // var killU = w.parentNode; + // killU.removeChild(w); +} diff --git a/xCAT-web/js/xcat.js b/xCAT-web/js/xcat.js new file mode 100644 index 000000000..9892fcdf6 --- /dev/null +++ b/xCAT-web/js/xcat.js @@ -0,0 +1,192 @@ +var zIndex = 1; +function addLoadEvent(func) { + var oldonload = window.onload; + if (typeof window.onload != 'function') { + window.onload = func; + } else { + window.onload = function() { + if (oldonload) { + oldonload(); + } + func; + } + } +} + + +var activeNode; + +function startList() { + if (document.getElementById('menubeans')) { + navRoot = document.getElementById('menubeans'); + for (i = 0; i < navRoot.childNodes.length; i++) { + node = navRoot.childNodes[i]; + if (node.nodeName == 'LI') { + if (node.className == 'active') { + activeNode = i; + } + } + } + for (i = 0; i < navRoot.childNodes.length; i++) { + node = navRoot.childNodes[i]; + if (node.nodeName == 'LI') { + node.onmouseover = function() { + navRoot.childNodes[activeNode].className = ''; + this.className = 'active'; + } + node.onmouseout = function() { + this.className = ''; + navRoot.childNodes[activeNode].className = 'active'; + } + node.onmousedown = function() { + navRoot.childNodes[activeNode].className = ''; + this.className = 'active'; + activeNode = this.id - 1; + } + + } + } + } +} + +var activeNN = '2-li-0'; +function chActiveMenu(newk){ + // find active node + //alert("value for AN" + activeNN); + //alert("value for newk" + newk); + /* set old one to nothing */ + document.getElementById(activeNN).className = ''; + /* set new one to active */ + document.getElementById(newk).className = 'active'; + activeNN = newk; +} +function resetForm(fobj){ + fobj.reset(); + closeTree(); +} + +function getFormVals(fobj) { + var str = ''; + var ft = ''; + var fv = ''; + var fn = ''; + var els = ''; + for(var i = 0;i < fobj.elements.length;i++) { + els = fobj.elements[i]; + ft = els.title; + fv = els.value; + fn = els.name; + switch(els.type) { + case "text": + case "hidden": + case "password": + case "textarea": + // is it a required field? + if(encodeURI(ft) == "required" && encodeURI(fv).length < 1) { + alert(fn + ' is a required field, please complete.'); + els.focus(); + return false; + } + str += fn + "=" + encodeURI(fv) + "&"; + break; + + case "checkbox": + case "radio": + if(els.checked) str += fn + "=" + encodeURI(fv) + "&"; + break; + + case "select-one": + str += fn + "=" + + els.options[els.selectedIndex].value + "&"; + break; + } // switch + } // for + str = str.substr(0,(str.length - 1)); + return str; +} + + +function killChildren(domE){ + for(var i = 0; i -1)) { + img_id = document.nodelist.elements[i].id; + group_name = img_id.substring(7,img_id.length-3); //the image id is of the form "img_gr_groupname-im" + Event.observe(img_id, 'click', function(event) { + XCATui.updateNodeList(group_name); + }); + } + }*/ + + for (var i = 0; iThere was a problem loading the node for the group ' + nodeGroupName + ''; + return html; +} + +XCATui.getLoadingSpanHTML = function(nodeGroupName) { + var spanId = "nodegroup_" + nodeGroupName + "_loading"; + var html = ''; + return html; +} + +/** + * This is the onCreate callback for the AJAX request made in XCATui.updateNodeList. + * It updates the interface to show that the request is loading. + * See http://www.prototypejs.org/api/ajax/options + */ +XCATui.updateNodeListLoading = function(nodeGroupName) { + + var spanId = 'img_gr_' + nodeGroupName; + new Insertion.Bottom(spanId, XCATui.getLoadingSpanHTML(nodeGroupName)); + + var loadingSpanId = "nodegroup_" + nodeGroupName + "_loading"; + new Effect.Appear(loadingSpanId); +} + +/** + * This is the onFailure callback for the AJAX request made in XCATui.updateNodeList. + * It updates the interface to show that the request failed. + * See http://www.prototypejs.org/api/ajax/options + */ +XCATui.updateNodeListFailure = function(nodeGroupName) { + var spanId = 'img_gr_' + nodeGroupName; + new Insertion.Bottom(spanId, XCATui.getFailureSpanHTML(nodeGroupName)); + + var failureSpanId = "nodegroup_" + nodeGroupName + "_failure"; + new Effect.Shake(failureSpanId); +} + +/** + * Add table rows representing nodes to the table that represents the node group + * identified by the given name. + */ +XCATui.updateNodeList = function(nodeGroupName) { + + var tableId = "div_" + nodeGroupName; + var imageId = tableId + '-im'; + var expandSpanId = "img_gr_" + nodeGroupName; + + var tableObj = $(tableId); + + if(null == tableObj) { + alert('Error: section ' + tableId + ' not found.'); + return false; + } + + var imageTag = $(imageId); + var expandSpanObj = $(expandSpanId); + + if(!tableObj.style.display || tableObj.style.display == 'inline') {// currently visible + + tableObj.style.display = 'none'; + imageTag.src = "./images/plus-sign.gif"; + expandSpanObj.title = "Click to expand section"; + + } else { //currently invisible + imageTag.src = "./images/minus-sign.gif"; + expandSpanObj.title = "Click to collapse section"; + + var target = "div_" + nodeGroupName; + var pars = 'nodeGroupName=' + nodeGroupName; + var URL = 'nodes_by_group.php'; + + // Check whether the table already exists and has already been updated? + + //var URL = "webservice.php?method=getXCATNodeRows&nodeGroupName=" + encodeURIComponent(nodeGroupName); + + new Ajax.Updater(target, URL, { + method: 'post', parameters: pars, + onCreate: function() { XCATui.updateNodeListLoading(nodeGroupName) }, // Needs Prototype 1.5.1 + onFailure: function() {XCATui.updateNodeListFailure(nodeGroupName) }, + onComplete: function() {new Effect.Fade("nodegroup_" + nodeGroupName + "_loading")} + }); + + // the inner table is currently invisible + tableObj.style.display = 'inline'; + + } + + //return true; + + + + //XCATui.toggleSection(nodeGroupName); +} \ No newline at end of file diff --git a/xCAT-web/lib/XCAT/HTML/HTMLProducer.class.php b/xCAT-web/lib/XCAT/HTML/HTMLProducer.class.php new file mode 100644 index 000000000..ba198cc54 --- /dev/null +++ b/xCAT-web/lib/XCAT/HTML/HTMLProducer.class.php @@ -0,0 +1,153 @@ + + + +
    + + Nodes +
    + +
    HW Type
    +
    OS
    +
    Mode
    +
    Status
    +
    HW Ctrl Pt
    +
    Comment
    + +EOS; + + return $html; +} + +function getXCATNodeTableFooter() { +$html = << +EOS; + return $html; +} + +/** + * @param String nodeGroupName The name of the node group. + */ +function getToggleString($nodeGroupName) { + global $imagedir; + global $colTxt, $exTxt; + global $bulgif; + global $minusgif; + global $plusgif; + $html = << + +EOS; + + return $html; +} + +/** + * @param String nodeGroup The group. + */ +function getXCATGroupTableRow($nodeGroup) { + +$config = &Config::getInstance(); +$imagedir = $config->getValue("IMAGEDIR"); +$nodeGroupName = $nodeGroup->getName(); +$img_string = XCATNodeGroupUtil::getImageString($nodeGroup->getStatus()); + +$html = << + +EOS; + $html .= HTMLProducer::getToggleString($nodeGroupName); + $html .= <<$nodeGroupName + +
     
    +
     
    +
     
    +
    $img_string
    +
     
    +
     
    + + +EOE; + return $html; +} + + /** + * @param XCATNodeGroup nodeGroup The node group for which we want to generate the html. + * returns the table that contains all the nodes information of that group + */ +function getXCATNodeGroupSection($nodeGroup) { + $config = &Config::getInstance(); + $imagedir = $config->getValue("IMAGEDIR"); + $right_arrow_gif = $imagedir . "/grey_arrow_r.gif"; + $left_arrow_gif = $imagedir . "/grey_arrow_l.gif"; + + $html .= << +EOS; + + $nodes = $nodeGroup->getNodes(); + + foreach($nodes as $nodeName => $node) { + $html .= HTMLProducer::getXCATNodeTableRow($node); + } + + $html .= "\"Previous    \"Next  "; + $html .= << +EOS; + + return $html; +} + + /** + * @param XCATNode node The node for which we want to generate the html. + */ +function getXCATNodeTableRow($node) { + + $config = &Config::getInstance(); + $imagedir = $config->getValue("IMAGEDIR"); + + //echo $node->getName(); + $html = " + getName(). "\" />" .$node->getName(). "" . + "
    " . $node->getHwType(). "
    ". + "
    " . $node->getOs(). "
    ". + "
    " . $node->getMode(). "
    "; + + $stat = $node->getStatus(); + $img_string = XCATNodeGroupUtil::getImageString($stat); + + $html .= "
    " . $img_string . "
    ". + "
    " . $node->getHwCtrlPt(). "
    ". + "
    " . $node->getComment(). "
    "; + +EOS; + return $html; + } +} +?> diff --git a/xCAT-web/lib/XCAT/HTML/ImageResources.class.php b/xCAT-web/lib/XCAT/HTML/ImageResources.class.php new file mode 100644 index 000000000..25d7bb396 --- /dev/null +++ b/xCAT-web/lib/XCAT/HTML/ImageResources.class.php @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/xCAT-web/lib/XCAT/XCATCommand/XCATCommandRunner.class.php b/xCAT-web/lib/XCAT/XCATCommand/XCATCommandRunner.class.php new file mode 100644 index 000000000..30fd60a6b --- /dev/null +++ b/xCAT-web/lib/XCAT/XCATCommand/XCATCommandRunner.class.php @@ -0,0 +1,200 @@ +XCATRoot = $config->getValue("XCATROOT"); + $this->CurrDir = $config->getValue("CURRDIR"); + + $this->XCATNodeManager = &XCATNodeManager::getInstance(); + $this->XCATNodeGroupManager = &XCATNodeGroupManager::getInstance(); + } + + /** + * @param String cmdString The command to execute. + * @return An array containing the command output as the first element + * and the command return status as the second element. + */ + function runCommand($cmdString) { + $cmdOutput = NULL; + $cmdReturnStat = NULL; + exec($cmdString, $cmdOutput, $cmdReturnStat); + + $outputStat = array(); + $outputStat["output"] = $cmdOutput; + $outputStat["returnStat"] = $cmdReturnStat; + + return $outputStat; + } + + /** + * Will always return an up to date list of node names. + * + * @return An array containing names of all nodes. + */ + function getAllNodeNames() { + $cmdString = "sudo " . $this->XCATRoot . "/nodels"; + $outputStat = $this->runCommand($cmdString); + + return $outputStat["output"]; + } + + /** + * Will always return an up to date list of node names belonging to the group. + * + * @param String groupName The name of the XCATNodeGroup + * @return An array containing the name of all nodes in the group. + */ + function getNodeNamesByGroupName($groupName) { + $cmdString = "sudo " . $this->XCATRoot . "/nodels $groupName"; + $outputStat = $this->runCommand($cmdString); + + return $outputStat["output"]; + } + + /** + * @param String nodeName The name of the node. + */ + function getXCATNodeByName($nodeName) { + + $cmdString = "sudo " . $this->XCATRoot . "/nodestat $nodeName"; + $outputStat = $this->runCommand($cmdString); + + $xcn = new XCATNode(); + $xcn->setName($nodeName); + $xcn->setStatus(XCATNodeGroupUtil::determineNodeStatus($outputStat["output"][0])); + $xcn->setHwType("HW Type"); + $xcn->setOs("OS"); + $xcn->setMode("Mode"); + $xcn->setHwCtrlPt("HW Ctrl Pt"); + $xcn->setComment("Comment"); + + // Add the node to the manager, now that we've loaded it. + $this->XCATNodeManager->addNode($xcn); + + + return $xcn; + } + + /** + * @return An array containing the name of every node group. + */ + function getAllGroupNames() { + $cmdString = "sudo " . $this->XCATRoot . "/listattr"; + $outputStat = $this->runCommand($cmdString); + + return $outputStat["output"]; + } + + /** + * @param String groupName The name of the group we want to get. + * @return An XCATNodeGroup object representing + * the node group with the given name. This object will + * contain the XCATNodes belonging to this XCATNodeGroup. + */ + function getXCATNodeByGroupName($groupName) { + $nodeNames = $this->getNodeNamesByGroupName($groupName); + + $xcatNodes = array(); + + foreach($nodeNames as $nodeName) { + $xcatNode = $this->getXCATNodeByName($nodeName); + array_push($xcatNodes, $xcatNode); + } + + $xcatNodeGroup = new XCATNodeGroup(); + $xcatNodeGroup->setName($groupName); + $xcatNodeGroup->setNodes($xcatNodes); + + + return $xcatNodeGroup; + } + + + /** + * This function will run the command to get all + * the groups, and then for each group, it will get + * their nodes, and for each node, it will get its + * information once. + */ + function getAllXCATGroups() { + $xcatGroupNames = $this->getAllGroupNames(); + + $xcatGroups = array(); + + $groupStatArr = $this->getGroupStatus(); //get the status of all the groups + + foreach($xcatGroupNames as $groupName) { + $xcatGroup = $this->getXCATGroupByName($groupName, $groupStatArr); + array_push($xcatGroups, $xcatGroup); + } + + + return $xcatGroups; + } + + function getXCATGroupByName($groupName, $groupStatArr){ + + $xcg = new XCATNodeGroup(); + $xcg->setName($groupName); + $xcg->setStatus(XCATNodeGroupUtil::determineNodeStatus($groupStatArr[$groupName])); + + return $xcg; + } + + + /** + * Will always return an up to date status of the groups + * + * @return An array containing the status of all the groups + */ + function getGroupStatus() { + $cmdString = "sudo " . $this->CurrDir . "/cmds/grpattr"; + $outputStat = $this->runCommand($cmdString); + $groupStats = $outputStat["output"]; + $groupStatArr = array(); + foreach($groupStats as $key => $groupStat) { + if (strpos($groupStat,':') != FALSE){ + $stat = substr($groupStat,strpos($groupStat,':') + 2); //there's a space between the colon and the status + $grp = substr($groupStat,0, strpos($groupStat,':')); + $groupStatArr[$grp] = $stat; + } + } + + return $groupStatArr; + } + + function getNodeOrGroupStatus($nodegroupName, $group) { + $stat = ""; + if ($group == FALSE){ + $cmdString = "sudo " . $this->XCATRoot . "/nodestat " . $nodegroupName; + $outputStat = $this->runCommand($cmdString); + $nodegroupStat = $outputStat["output"][0]; + + if (strpos($nodegroupStat,':') != FALSE){ + $stat = substr($nodegroupStat,strpos($nodegroupStat,':') + 2); //there's a space between the colon and the status + } + }else{ + $StatArr = $this->getGroupStatus(); + $stat = $StatArr[$nodegroupName]; + } + + if ($stat != "") $stat = XCATNodeGroupUtil::determineNodeStatus($stat); + + return $stat; + } + +} +?> \ No newline at end of file diff --git a/xCAT-web/lib/XCAT/XCATNode/XCATNode.class.php b/xCAT-web/lib/XCAT/XCATNode/XCATNode.class.php new file mode 100644 index 000000000..da4d23f7a --- /dev/null +++ b/xCAT-web/lib/XCAT/XCATNode/XCATNode.class.php @@ -0,0 +1,74 @@ +name; + } + + function getHwType() { + return $this->hwType; + } + + function getOs() { + return $this->os; + } + + function getMode() { + return $this->mode; + } + + function getStatus() { + return $this->status; + } + + function getHwCtrlPt() { + return $this->hwCtrlPt; + } + + function getComment() { + return $this->comment; + } + + function setName($pName) { + $this->name = $pName; + } + + function setHwType($pHwType) { + $this->hwType = $pHwType; + } + + function setOs($pOs) { + $this->os = $pOs; + } + + function setMode($pMode) { + $this->mode = $pMode; + } + + function setStatus($pStatus) { + $this->status = $pStatus; + } + + function setHwCtrlPt($pHwCtrlPt) { + $this->hwCtrlPt = $pHwCtrlPt; + } + + function setComment($pComment) { + $this->comment = $pComment; + } +} +?> diff --git a/xCAT-web/lib/XCAT/XCATNode/XCATNodeGroupUtil.class.php b/xCAT-web/lib/XCAT/XCATNode/XCATNodeGroupUtil.class.php new file mode 100644 index 000000000..958645722 --- /dev/null +++ b/xCAT-web/lib/XCAT/XCATNode/XCATNodeGroupUtil.class.php @@ -0,0 +1,56 @@ +getValue("IMAGEDIR"); + $greengif = $imagedir . "/green-ball-m.gif"; + $yellowgif = $imagedir . "/yellow-ball-m.gif"; + $redgif = $imagedir . "/red-ball-m.gif"; + + //node/group is good + if (strstr($status, "good") == TRUE ){ + $stat_content = "\"Node "; + }elseif (strstr($status, "bad") == TRUE){ //node is bad + $stat_content = "\"Node "; + }else{ //other status + $stat_content = "\"Other "; + } + + return $stat_content; + } +} +?> diff --git a/xCAT-web/lib/XCAT/XCATNode/XCATNodeManager.class.php b/xCAT-web/lib/XCAT/XCATNode/XCATNodeManager.class.php new file mode 100644 index 000000000..d1b23901c --- /dev/null +++ b/xCAT-web/lib/XCAT/XCATNode/XCATNodeManager.class.php @@ -0,0 +1,51 @@ +nodes; + } + + function setNodes($pNodes) { + $this->nodes = $pNodes; + } + + function addNode($node) { + $this->nodes[$node->getName()] = $node; + } + + function removeNode($node) { + $this->nodes[$node->getName()] = NULL; + } + + function getNodeByName($nodeName) { + $node = NULL; + + if(array_key_exists($nodeName, $this->nodes)) { + $node = $this->nodes[$nodeName]; + } + + return $node; + } +} +?> diff --git a/xCAT-web/lib/XCAT/XCATNode/XCATNodeStatus.class.php b/xCAT-web/lib/XCAT/XCATNode/XCATNodeStatus.class.php new file mode 100644 index 000000000..e114f5ad4 --- /dev/null +++ b/xCAT-web/lib/XCAT/XCATNode/XCATNodeStatus.class.php @@ -0,0 +1,19 @@ + diff --git a/xCAT-web/lib/XCAT/XCATNode/XCATNodeUtil.class.php b/xCAT-web/lib/XCAT/XCATNode/XCATNodeUtil.class.php new file mode 100644 index 000000000..e7d15f19d --- /dev/null +++ b/xCAT-web/lib/XCAT/XCATNode/XCATNodeUtil.class.php @@ -0,0 +1,31 @@ + diff --git a/xCAT-web/lib/XCAT/XCATNodeGroup/XCATNodeGroup.class.php b/xCAT-web/lib/XCAT/XCATNodeGroup/XCATNodeGroup.class.php new file mode 100644 index 000000000..a307bb566 --- /dev/null +++ b/xCAT-web/lib/XCAT/XCATNodeGroup/XCATNodeGroup.class.php @@ -0,0 +1,45 @@ +nodes = array(); + } + + function getName() { + return $this->name; + } + + function setName($pName) { + $this->name = $pName; + } + + function getNodes() { + return $this->nodes; + } + + function getStatus() { + return $this->status; + } + + function setNodes($pNodes) { + $this->nodes = $pNodes; + } + + function setStatus($pStatus) { + $this->status = $pStatus; + } + + function addNode($node) { + $this->nodes[$node->getName()] = $node; + } + + function removeNode($node) { + $this->nodes[$node->getName()] = NULL; + } +} +?> diff --git a/xCAT-web/lib/XCAT/XCATNodeGroup/XCATNodeGroupManager.class.php b/xCAT-web/lib/XCAT/XCATNodeGroup/XCATNodeGroupManager.class.php new file mode 100644 index 000000000..b9c6db1a5 --- /dev/null +++ b/xCAT-web/lib/XCAT/XCATNodeGroup/XCATNodeGroupManager.class.php @@ -0,0 +1,51 @@ +nodeGroups; + } + + function setNodeGroups($pNodeGroups) { + $this->nodeGroups = $pNodeGroups; + } + + function addNodeGroup($nodeGroup) { + $this->nodeGroups[$nodeGroup->getName()] = $nodeGroup; + } + + function removeNodeGroup($nodeGroup) { + $this->nodeGroups[$nodeGroup->getName()] = NULL; + } + + function getNodeGroupByName($nodeGroupName) { + $nodeGroup = NULL; + + if(array_key_exists($nodeGroupName, $this->nodeGroups)) { + $nodeGroup = $this->nodeGroups[$nodeGroupName]; + } + + return $nodeGroup; + } +} +?> diff --git a/xCAT-web/lib/XCAT/webservice/XCATWebservice.class.php b/xCAT-web/lib/XCAT/webservice/XCATWebservice.class.php new file mode 100644 index 000000000..a5b6cdf33 --- /dev/null +++ b/xCAT-web/lib/XCAT/webservice/XCATWebservice.class.php @@ -0,0 +1,55 @@ + \ No newline at end of file diff --git a/xCAT-web/lib/config.php b/xCAT-web/lib/config.php new file mode 100644 index 000000000..8fec80221 --- /dev/null +++ b/xCAT-web/lib/config.php @@ -0,0 +1,27 @@ +configMap[$key]; + } + + function setValue($key, $value) { + $this->configMap[$key] = $value; + } +} +?> \ No newline at end of file diff --git a/xCAT-web/menu.css b/xCAT-web/menu.css new file mode 100644 index 000000000..fbfe310f7 --- /dev/null +++ b/xCAT-web/menu.css @@ -0,0 +1,275 @@ +/* + + template: Artificial Intelligence + author: luka cvrk (www.solucija.com) + +*/ + +body { + margin: 0; + padding: 10px; + background: #fff url(images/bg.gif) repeat-x; + font: normal 12px Tahoma, Verdana, Arial, Helvetica, Sans-Serif; + line-height: 1.6em; + color: #333; +} + +.middle{ + font: normal 1em Tahoma, Verdana, Arial, Helvetica, Sans-Serif; +} + +.BlueBack{ + font: bold .9em Tahoma, Verdana, Arial, Helvetica, Sans-Serif; + color:#003399; +} + +.White{ + font: bold .9em Tahoma, Verdana, Arial, Helvetica, Sans-Serif; + color:#FFFFFF; +} + +/* LINKS AND HEADINGS STYLING */ + +a { color: #546F92; background: inherit; } +a:hover { color: #808080; background: inherit; } +h1 { font: bold 2em "Trebuchet MS", Helvetica, Sans-Serif; margin: 0; color: #000000; background-color:transparent; } +h2 { font: bold 1.4em "Trebuchet MS", Helvetica, Sans-Serif; } +h3 { clear: both; font-size: 1.1em; color: #000; background: #fff url(images/h3bg.gif) no-repeat center left; padding: 0 14px; margin: 0;} +.blue { color: #546F92; background: inherit; } +.image { float: left; border: 2px solid #e2e2e2; padding: 3px; margin: 0 15px 10px 0; } +.no-border {border:0;} + +/* CONTENT WRAPPER */ + +#main { + width: 900px; + margin: 25px auto 0 auto; +} + +/* TOP LEFT LOGO */ + +#logo { + float: left; +} + #logo h1 { color: #FFFFFF; background: inherit;} + #logo h3 { color: #999999; background: inherit;} + +/* TOP RIGHT HORIZONTAL MENU */ + +#menu { + float: right; + /*background: #fff url(images/barul.gif) no-repeat bottom right; */ + color: #808080; + padding: 18px 1px 11px 0; + margin: 0; +} + #menu li { + padding: 14px 18px 14px 18px; + color: #444; + /*background: #fff url(images/bar.gif) no-repeat bottom left; */ + display: inline; + } + #menu li.active { + /*background: #fff url(images/baractive.gif) no-repeat bottom left; */ + color: #FFFF00; + font-weight: bold; + padding: 14px 18px 14px 18px; + margin: 0 1px 0 0; + } + #menu li a { + /*background: #f8f8f8; */ + color: #808080; + text-decoration: none; font: bold 2em "Trebuchet MS", Helvetica, Sans-Serif; margin: 0; color: #fff; background-color:transparent; + } + #menu li a:hover { + color: #000; + /*background: #f8f8f8; */ + } + .key { text-decoration: underline; } /* ACCESKEY UNDERLINED LETTER */ + +/* LEFT INTRO SIDE */ + +#intro_left { + clear: left; + float: left; + width: 250px; + background: #8D9CAF url(images/leftintrobg.gif) repeat-x; + color: #E5EAF2; + margin: 0 0 1px 0; +} + #intro_left a { color: #fff; background: inherit; } + #intro_left p { padding: 14px 15px; margin: 0; } + +/* LEFT VERTICAL MENU */ + +#menu_level1 { + float: left; + color: #3F5B7F; + background: #C2CEDE; + font-weight: bold; + font-size:12px; + width: 100%; + height:100%; + padding: 0px; +} + #menu_level1 li { list-style: none; } + #menu_level1 a:link,a:visited { + display: block; + color: #3F5B7F; + font-weight: bold; + background: #C2CEDE; + width: 100%; + height:100%; + padding: 0px; + font-weight: bold; + text-decoration: none; + } + #menu_level1 a:hover { background: #9EB1C9 ; color: #fff; text-decoration: underline; } + +#menu_level2 { +float: left; +color: #3F5B7F; +font-weight: bold; +background: #BDBDBD ; +font-size:11px; +border-bottom: 1px solid #fff; +width: 110px; +} + #menu_level2 li { list-style: none; } + #menu_level2 a:link, a:visited { + display: block; + color: #3F5B7F; + width: 110px; + height:100%; + padding: 0px 0px 0px 15px; + font-weight: bold; + background: #BDBDBD ; + text-decoration: none; + + } + #menu_level2 a img { border: 0 none; } + #menu_level2 a:hover { background: #999999; color: #fff; } + #menu_level2 a.current { background: #999999; color: #fff; } + #menu_level2.no-link { padding: 0px 0px 0px 3px; } + +#menu_level3 { +float: left; +/*color: #FFFF00;*/ +color: #3F5B7F; +font-weight: bold; +background: #CCCCCC ; +border-bottom: 1px solid #fff; +font-size:11px; +width: 100%; +} + #menu_level3 li { list-style: none; } + #menu_level3 a:link, a:visited { + display: block; + color: #3F5B7F; + width: 110px; + height:100%; + padding: 0px 0px 0px 15px; + background: #CCCCCC ; + /*margin: 0;*/ + font-weight: bold; + text-decoration: none; + + } + #menu_level3 a img { border: 0 none; } + #menu_level3 a:hover { background: #999999; color: #fff; } + #menu_level3 a.current { background: #999999; color: #fff; } + #menu_level3.current {padding: 0px 0px 0px 15px; background: #FFFFFF ; } + + +/* RIGHT INTRO SIDE */ + +#intro_right { + float: right; + width: 340px; + padding: 25px 280px 0 20px; + background: #BCCEDA url(images/intro.jpg) no-repeat; + color: #547592; + margin: 0 0 6px 0; + height: 174px; + border: 2px solid #e2e2e2; +} + #intro_right h1 { margin: 0 0 20px 0; } + #intro_right .white { color: #FFF; background-color:transparent; } + +/* LEFT SIDE */ + +#left { + float: left; + width: 250px; +} + .box { + padding: 4px 15px; + border: 1px solid #ccc; + margin: 0 0 10px 0; + } + .note { + padding: 15px 15px 20px 100px; + border: 1px solid #CFCB66; + margin: 0 0 5px 0; + background: #FFFFAD url(images/package.gif) no-repeat; + color: #585616; + } + +/* RIGHT SIDE */ + +#right { + float: right; + width: 643px; +} + .leftcol { + float: left; + padding: 12px 15px; + width: 285px; + border:#ccc 1px solid; + text-align: justify; + margin: 0 0 10px 0; + } + .rightcol { + float: right; + padding: 10px 15px; + width: 285px; + border:#ccc 1px solid; + margin: 0 0 10px 0; + } + .special { + clear: both; + margin:0px 0 20px 0; + padding: 5px 20px; + border: 1px solid #ccc; + background: #eee; + color: #444; + } + +/* FOOTER */ + +#footer { clear: both; padding: 5px; border-top: 1px solid #ccc; } + +.main_table{ + border-collapse:collapse; +} + +.inner_table{ + /*border-collapse:collapse;*/ + border: 3px; + background-color:green; + margin-left : 15px; + margin-right : 0px; + margin-top : 0px; +} + +td.BorderMe{ + border:1px dotted #CCC; +} + +tr.indent td{ + padding:0px 0 0px 10px; +} + +.none{ + display:none; +} diff --git a/xCAT-web/nav.php b/xCAT-web/nav.php new file mode 100644 index 000000000..093cc1b5f --- /dev/null +++ b/xCAT-web/nav.php @@ -0,0 +1,88 @@ + + + + + + + + diff --git a/xCAT-web/node_table.html b/xCAT-web/node_table.html new file mode 100644 index 000000000..ba469baa4 --- /dev/null +++ b/xCAT-web/node_table.html @@ -0,0 +1,31 @@ + + + + +Untitled Document + + + + + + + + + + + + + + + + + + + + + + + +
    Nodes
    HW Type
    OS
    Mode
    Status
    HW Ctrl Pt
    Comment
           
    + + diff --git a/xCAT-web/nodegroups.php b/xCAT-web/nodegroups.php new file mode 100644 index 000000000..a9def4658 --- /dev/null +++ b/xCAT-web/nodegroups.php @@ -0,0 +1,11 @@ +getAllXCATNodeGroups(); +echo "$var."; +print_r($var); +?> diff --git a/xCAT-web/nodes.html b/xCAT-web/nodes.html new file mode 100644 index 000000000..846b41848 --- /dev/null +++ b/xCAT-web/nodes.html @@ -0,0 +1,61 @@ + + + + +Untitled Document + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +   Filter:      Group + +or    Node Range +    
     
     
     
     
    + + diff --git a/xCAT-web/nodes_by_group.php b/xCAT-web/nodes_by_group.php new file mode 100644 index 000000000..401a0bb2b --- /dev/null +++ b/xCAT-web/nodes_by_group.php @@ -0,0 +1,14 @@ +getXCATNodeByGroupName($nodeGroupName); +echo HTMLProducer::getXCATNodeGroupSection($nodeGroup); + + +?> diff --git a/xCAT-web/phpinfo.php b/xCAT-web/phpinfo.php new file mode 100644 index 000000000..530f40fdc --- /dev/null +++ b/xCAT-web/phpinfo.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/xCAT-web/prefs.php b/xCAT-web/prefs.php new file mode 100644 index 000000000..5daa96b24 --- /dev/null +++ b/xCAT-web/prefs.php @@ -0,0 +1,62 @@ + + + +
    + + + + + + + + + + +
    Number of nodes per display: + "> +

    +

    +
    +
    + + + + + + + \ No newline at end of file diff --git a/xCAT-web/rack.php b/xCAT-web/rack.php new file mode 100644 index 000000000..c6ac755d6 --- /dev/null +++ b/xCAT-web/rack.php @@ -0,0 +1,359 @@ + + + + +
    + +

    +

    Node List on Management Server

    + +
    + + + + + + + + + + + +
    +

    Which Nodes:

    +
     Group:  or  Node Range:
    + Show Nodes in Racks (have to first set the physical location)
    +
    + + +
    + + + + + + + + + + + \n"; +echo "\n"; +echo "\n"; +$index =0; +?> + +

    Node
    Actions:

    + + + + +
    + + + + + + + +
    + + + + +
    + + + + + + + + +
    +
    + +
    Select All   NameHW TypeOSModeStatusHW Ctrl PtComment
    \n" ?> + + + + + + + +
    + +

    Tips:

    +
      +
    • Select 1 or more nodes & click on an action button.  Or choose + 1 of the main tasks on the left.  The Help link at the top left really does help.  It describes what all these buttons do. +
    • The Status colors:  green=reachable, red=not reachable, yellow=unknown/error, blue=node not managed. +
    • If too many nodes are displayed, use the Group or Node Range selections + to focus what is displayed. +
    • The Console/Settings link at the top left is the way to set preferences.  There is a verbose + option there to have this interface display the commands it is running. +
    • If you are running this over a phone line, we recommend using the Old Webmin theme (which is now the default) for faster loading.  Of course, the MSC Linux theme looks nicer.  (If you change the theme, you will have to navigate + back to this page in the Cluster category.) +
    • Did you know you can run as many browser windows with this interface as + you want?  This can be handy to view information from multiple pages of this interface + at the same time. +
    +

    CSM Version: $rpmVersions{'csm.server'}       CSM Web Interface Version: $rpmVersions{'xcsm.web'}

    +
    + + diff --git a/xCAT-web/softmaint/updategui.php b/xCAT-web/softmaint/updategui.php new file mode 100644 index 000000000..497e87f31 --- /dev/null +++ b/xCAT-web/softmaint/updategui.php @@ -0,0 +1,138 @@ + + +
    + +
    + + + +
    +
    + + + + + +\n"; + echo "
  • Searching for the xCAT Web Interface download site...
  • \n"; + $site = findSite(); + echo "
  • Selected site ${site['dir']}. Searching for the $rpmname RPM...
  • \n"; + $pkg = findPkg($site['dir'], $rpmname); + if (!$pkg) { echo "

    Can not find $rpmname in ${site['dir']}.

    \n"; return; } + echo "
  • Latest RPM version available is $pkg.
  • \n"; + if (isInstalled($pkg)) { echo "
  • $pkg (or higher) is already installed.
  • \n"; return; } + echo "
  • Downloading $pkg ...
  • \n"; + if (!downloadPkg($site['download'], $pkg, $tmpdir)) { echo "

    Error downloading $pkg from ${site['download']}.

    \n"; return; } + echo "
  • Download complete. Installing $pkg ...
  • \n"; + $rc = installPkg("$tmpdir/$pkg"); + if ($rc) { echo "

    Installation of $pkg failed with rc=$rc.

    \n"; } + echo "
  • Installation of $pkg completed successfully.
  • \n"; + echo "\n"; + + //dumpGlobals(); + //runcmd('/WINDOWS/system32/ipconfig', 1); +} + + +// Install the specified rpm +function installPkg($pkgname) { //------------------------------------ + $aixrpmopt = isAIX() ? '--ignoreos' : ''; + $rc = runcmd("/bin/rpm -U $aixrpmopt $pkgname", 1); + return $rc; +} + + +// Download the specified rpm from the specified url and put it in the specified dir +function downloadPkg($url, $rpmname, $tmpdir) { //------------------------------------ + //trace("copy($url/$rpmname, $tmpdir/$rpmname)"); + $result = copy("$url/$rpmname", "$tmpdir/$rpmname"); + return $result; +} + + +// At the chosen site, find the latest version of xcat.web +function findPkg($url, $rpmname) { //------------------------------------ + //trace("file($url)"); + $html = file($url); + if (!$html) { return NULL; } + //foreach ($html as $line) { if (preg_match('/xcat.web/',$line)) { trace($line); } } + $matches = preg_grep('/href=.*' . $rpmname . '-.*?\.rpm/i', $html); + if (!count($matches)) { return NULL; } + foreach ($matches as &$line) { + $pattern = '/' . $rpmname . '-.*?\.rpm/i'; + //trace($pattern); + //$line = preg_replace($pattern, '$1', $line); + preg_match($pattern, $line, $m); + $line = $m[0]; + //trace($line); + } + usort($matches, 'byversion'); + //foreach ($matches as $l) { trace($l); } + return $matches[0]; +} + + +# Parse out the version and release numbers of the 2 rpm file names and sort in descending order +# This sort function should be used like: usort($rpms, 'byversion'); +# Makes the following assumptions: +# - the version and release are single digits separated by dots (e.g. 1.2.3) - so we can compare it as a string +# - the release has at most 1 dot in it - so we can compare it as a float +function byversion($a, $b) { //------------------------------------ + $pat = '/-([0-9.]+)-([0-9.]+)\.\D/'; + preg_match($pat, $a, $a_matches); + preg_match($pat, $b, $b_matches); + $result = strcmp($a_matches[1], $b_matches[1]); + if ($result) { return $result * -1; } // they were not equal, so return result + if ($a_matches[2] == $b_matches[2]) { return 0; } + return ($a_matches[2] < $b_matches[2]) ? 1 : -1; + } + + +// Choose whether to use the PPD lab server, the general internal IBM server, or the external server. +function findSite() { //------------------------------------ + + $internalSitePok = 'rs6000.pok.ibm.com'; + $internalDir = '/afs/apd/u/bp/xcat/'; + $internalURLPok = "http://$internalSitePok$internalDir"; + $internalURLDirPok = "http://$internalSitePok$internalDir"; + $internalSite = 'www.pok.ibm.com'; + $internalURL = "http://$internalSite$internalDir"; + $internalURLDir = "http://$internalSite$internalDir"; + $externalURL = "ftp://ftp.software.ibm.com/eserver/pseries/cluster/csm/fixes"; //todo: change these + $externalURLDir = "http://www14.software.ibm.com/webapp/set2/sas/f/csm/utilities/xCSMfixhome.html"; + + # 1st try rs6000.pok.ibm.com if we are inside the BSO firewall + $hostname = resolveHost($_SERVER["SERVER_NAME"]); + //$hostname = resolveHost('9.56.216.79'); + //trace("Local hostname: $hostname"); + if (preg_match('/ppd\.pok\.ibm\.com$/', $hostname)) { + return array ('dir'=>$internalURLDirPok, 'download'=>$internalURLPok); + } + + // Try pinging the IBM internal site + $cntflag = isWindows() ? '-n' : '-c'; + $rc = runcmd("/bin/ping -w 4 $cntflag 1 $internalSite", 2, $out); + //trace("rc=$rc"); + if ($rc == 0) { + return array ('dir'=>$internalURLDir, 'download'=>$internalURL); + } + + // Fall back to external site + return array ('dir'=>$externalURLDir, 'download'=>$externalURL); +} +?> diff --git a/xCAT-web/style.css b/xCAT-web/style.css new file mode 100644 index 000000000..a731c2334 --- /dev/null +++ b/xCAT-web/style.css @@ -0,0 +1,158 @@ +div#nav { + /* background-color : #d8dff1; */ + position: absolute; + left: 0px; + top: 95px; +} + +div#content { + /* background-color : #fcfaeb; */ + position: absolute; + left: 125px; + top: 95px; +} + +P#logo { + background-color: #d8dff1; /* #f5f3cf #fffdb0 */ + vertical-align : middle;; + padding-top: 5px; + text-align: center; + padding-bottom: 5px; + margin-bottom: 10px; +} + +p#Banner { + font-weight: bolder; + font-size: 20pt; + font-style: italic; + color: gray; + text-align: center; + margin: 10px; +} + +/* pre { padding: 0px; margin: 0px; } */ + +.TableRow { background-color : #d8dff1; } +.Info {color : blue;} +.Error {font-weight : bold; color : red;} +.Trace {color: gray; padding: 0px; margin: 0px;} + +.BasicHeading {background-color : #8CA4D9;} +.BlueBack {background-color : #C2CEDE; /* #8CA4D9; */ } + +.LightBlueBack {background-color : #bfccea;} + +.FilterBox {background-color : #e8e8e8;} +.LightGrayBack {background-color : #e8e8e8;} + +.Middle {vertical-align : middle;} +.Center {text-align : center;} +.YellowBackground {background-color : yellow;} +.SmallText {font-size : small;} +.XSmallText {font-size : x-small;} +.XXSmallText {font-size : xx-small;} +.SmallTextarea {font-size:9pt; font-family:Arial;} +.Font8 {font-size : 8pt;} +.Boxed { border: 1px solid gray; } +.NoMargin { margin: 0px; } + +.but{ + background-color : #fbf999; + padding-top : 0px; + padding-left : 0px; + padding-right : 0px; + padding-bottom : 0px; + margin-top : 0px; + margin-left : 0px; + margin-right : 0px; + margin-bottom : 0px; +} + +.txtfld {font-size : 8pt; font-family : sans-serif; font-weight : bold;} + +.bt { + font-size : 8pt; + font-weight : bold; + font-family : sans-serif; + background-image : url(images/bt.gif); + text-align: center; +} + +INPUT {vertical-align : middle; font-size : 8pt; font-family : sans-serif; font-weight : bold;} +SELECT {vertical-align : middle; font-size : 8pt; font-family : sans-serif; font-weight : bold;} +TEXTAREA {vertical-align : middle; font-size : 8pt; font-family : sans-serif; font-weight : bold;} +TABLE {font-size : 10pt; font-family : Arial;} +BODY {font-size : 10pt; font-family : Arial; +} + +/* The rest of the styles are for the navigation area */ +#foo { + margin-left : 0px; + margin-right : 0px; + margin-top : 0px; + margin-bottom : 0px; + padding-top : 0px; + padding-left : 0px; + padding-right : 0px; + padding-bottom : 0px; + } + +table#navtable { + width: 105px; + font-size : 8pt; + padding-left : 1px; + border: 0px; + white-space: nowrap; +} + +.NavCurLink { background-color: #fcfaeb; } + + +#nav A{ + /* text-decoration : none; */ + font-weight : bold; + font-family : sans-serif; +} + +#nav P{ + text-indent : 0px; + padding: 0px; + margin: 0px; + font-weight : bold; + font-family : sans-serif; + cursor : default; + vertical-align : middle; +} + +#nav TD{ + white-space: nowrap; + vertical-align : middle; + font-weight : bold; + font-family : sans-serif; + /* border: 1px solid gray; */ +} + +#nav IMG { + border: 0px; +} + +.InnerTable{ + margin-left : 0px; + margin-right : 0px; + margin-top : 0px; + margin-bottom : 0px; + border: 0px; + width: 100%; + font-size : 8pt; + white-space: nowrap; +} + +.Hidden { visibility : hidden ;} + +/* OuterTable is not currently used */ +.OuterTable{ + margin-left : 1px; + margin-right : 1px; + margin-top : 0px; + margin-bottom : 0px; +} diff --git a/xCAT-web/suggestions.html b/xCAT-web/suggestions.html new file mode 100644 index 000000000..bacf63ca7 --- /dev/null +++ b/xCAT-web/suggestions.html @@ -0,0 +1,130 @@ + + + + + + + +Send Sugestions to the CSM Development Team + + + + + +

    Send Sugestions to the CSM Development Team

    + +

    Suggestions, bug reports, bug fixes, etc., are always welcome.  Please post them to the CSM forum.  Contributions are also welcome, such as minor new features, better +images, or even whole new pages.  See the developer section of +the CSM Linux GUI white paper for more info.  Thanks!

    + +

    Todo List

    + +

    The following items are already on our todo list, in +approximately priority order:

    + +
      +
    • When the license expires, have to give a good msg from the main page
    • +
    • Finish modifying the following pages with the More... links: definenode, + definehwdev, dev properties, condition properties, response properties.
    • +
    • Change the xcsm-utils resources so they can also be defined in the xcsm + dir
    • +
    • Add LastEvent to the Condition Attributes page, for releases in which it + is supported
    • +
    • Have the task pane save the current task in the cookie and have each page set the current task
    • +
    • Add list of CSM env vars to Tips section of CSM Config page.
    • +
    • Add hw ctrl info to the Add Nodes page
    • +
    • Finish rack layout of partitioned pSeries systems
    • +
    • Add -c option (adapter config) to updatenode page.  Also run cfmupdatenode + if only -F specified and smsupdatenode if only -S specified.
    • +
    • Monitoring: +
        +
      • Add to Sensor page: +
          +
        • create a mkresources file to facilitate creating/modifying sensors on nodes
        • +
        • force the execution of a sensor immediately
        • +
        +
      • +
      • Provide an Operator monitoring mode (log in as non-root user and get simpler + monitoring page)
      • +
      • Add option to monitor in node group view and be able to drill down to the + problem node
      • +
      • Make drill down on the problem more obvious
      • +
      • Add additional conditions/sensors for a more complete "Health Monitoring"
      • +
      +
    • +
    • Be able to log in as non-root id and su to root (exploit webmin acls)
    • +
    • Display the 1st time user/set up cluster wizard page link automatically + if no nodes are defined.
    • +
    • Scalabilty improvements:
        +
      • When a large # of nodes are displayed on the main + page, only get the 1st 50.
      • +
      +
    • +
    • Fix running perfmondata -h in Tips section of perfmon page, and add it + to xcsm/utils
    • +
    • Get gnuplot (and plotutils on SLES) installed + automatically.
    • +
    • Add to the Diagnose MS page to restart the HWCTRLRM with + tracing on or off:
        +
      • stopsrc -s IBM.HWCTRLRM
      • +
      • If IBM.HWCTRLRM does not stop within a minute, + "ps -ef | grep HW" and kill any + processes.
      • +
      • Delete the contents of /var/log/csm
      • +
      • startsrc -s IBM.HWCTRLRM + -eHC_JAVA_VERBOSE=/tmp/jni.txt
      • +
      • send the trace files in /var/log/csm and + /tmp/jni.txt
      • +
      +
    • +
    • Add some items from csmsnap design to Diagnose MS page
    • +
    • Clean up code for HW Ctrl page
    • +
    • Add BIOS/firmware flashing page
    • +
    • Debug or improve opening a console for a node
    • +
    • Install page:
        +
      • Set up page should use InstallTemplate attribute + as default for the template file
      • +
      • Implement remaining buttons:  Test Console, + Copy Distros, Run Install Probe.
      • +
      • Add nodegrp option to page and add a link to this page on the task list.
      • +
      • Update monitoring of install progress
      • +
      • Add list & manipulation of install images (sets of distro rpms) that + are in /csminstall
      • +
      +
    • +
    • Add copycsmpkgs to Updatenode page.
    • +
    • Make the perf mon page extensible so users can add their + own conditions
    • +
    • Make the main page extensible so users can add their own + attributes to be shown in the node table.
    • +
    • Make the main page monitor the node Status and update it
    • +
    • Network management: +
        +
      • Query status, link speed, throughput of all network adapters on a node
      • +
      +
    • +
    • Implement a summary page
    • +
    • Software Maintenance page:  Allow distro CDs to be + made available as ISOs and mounted
    • +
    • Modify the web interface so it can start up before CSM is + installed, so it can be used to install CSM on the mgmt + svr.
    • +
    • Have this interface launched from the new CSM LWC console and from IBM + Director.
    • +
    + +

    Known Defects and Limitations

    + +
      +
    • Perfmon graph page doesn't download gnuplot for SuSE + correctly.
    • +
    • When you use the browser's Back button to return to the + main page, the image checkboxes of the rack nodes are not + checked.
    • +
    + +

    The Change Log describing recent enhancements is in the +README file.

    + + diff --git a/xCAT-web/themes/alert.css b/xCAT-web/themes/alert.css new file mode 100644 index 000000000..4846e6185 --- /dev/null +++ b/xCAT-web/themes/alert.css @@ -0,0 +1,119 @@ +.overlay_alert { + background-color: #85BBEF; + filter:alpha(opacity=60); + -moz-opacity: 0.6; + opacity: 0.6; +} + +.alert_nw { + width: 5px; + height: 5px; + background: transparent url(alert/top_left.gif) no-repeat bottom left; +} + +.alert_n { + height: 5px; + background: transparent url(alert/top.gif) repeat-x bottom left; +} + +.alert_ne { + width: 5px; + height: 5px; + background: transparent url(alert/top_right.gif) no-repeat bottom left +} + +.alert_e { + width: 5px; + background: transparent url(alert/right.gif) repeat-y 0 0; +} + +.alert_w { + width: 5px; + background: transparent url(alert/left.gif) repeat-y 0 0; +} + +.alert_sw { + width: 5px; + height: 5px; + background: transparent url(alert/bottom_left.gif) no-repeat 0 0; +} + +.alert_s { + height: 5px; + background: transparent url(alert/bottom.gif) repeat-x 0 0; +} + +.alert_se, .alert_sizer { + width: 5px; + height: 5px; + background: transparent url(alert/bottom_right.gif) no-repeat 0 0; +} + +.alert_close { + width:0px; + height:0px; + display:none; +} + +.alert_minimize { + width:0px; + height:0px; + display:none; +} + +.alert_maximize { + width:0px; + height:0px; + display:none; +} + +.alert_title { + float:left; + height:1px; + width:100%; +} + +.alert_content { + overflow:visible; + color: #000; + font-family: Tahoma, Arial, sans-serif; + font: 12px arial; + background: #FFF; +} + +/* For alert/confirm dialog */ +.alert_window { + background: #FFF; + padding:20px; + margin-left:auto; + margin-right:auto; + width:400px; +} + +.alert_message { + font: 12px arial; + width:100%; + color:#F00; + padding-bottom:10px; +} + +.alert_buttons { + text-align:center; + width:100%; +} + +.alert_buttons input { + width:20%; + margin:10px; +} + +.alert_progress { + float:left; + margin:auto; + text-align:center; + width:100%; + height:16px; + background: #FFF url('alert/progress.gif') no-repeat center center +} + + diff --git a/xCAT-web/themes/alert/bottom.gif b/xCAT-web/themes/alert/bottom.gif new file mode 100644 index 000000000..9870f5224 Binary files /dev/null and b/xCAT-web/themes/alert/bottom.gif differ diff --git a/xCAT-web/themes/alert/bottom_left.gif b/xCAT-web/themes/alert/bottom_left.gif new file mode 100644 index 000000000..583f113fa Binary files /dev/null and b/xCAT-web/themes/alert/bottom_left.gif differ diff --git a/xCAT-web/themes/alert/bottom_right.gif b/xCAT-web/themes/alert/bottom_right.gif new file mode 100644 index 000000000..230ba12be Binary files /dev/null and b/xCAT-web/themes/alert/bottom_right.gif differ diff --git a/xCAT-web/themes/alert/left.gif b/xCAT-web/themes/alert/left.gif new file mode 100644 index 000000000..4a3fab520 Binary files /dev/null and b/xCAT-web/themes/alert/left.gif differ diff --git a/xCAT-web/themes/alert/overlay.png b/xCAT-web/themes/alert/overlay.png new file mode 100644 index 000000000..2f3344e5c Binary files /dev/null and b/xCAT-web/themes/alert/overlay.png differ diff --git a/xCAT-web/themes/alert/progress.gif b/xCAT-web/themes/alert/progress.gif new file mode 100644 index 000000000..529e72f45 Binary files /dev/null and b/xCAT-web/themes/alert/progress.gif differ diff --git a/xCAT-web/themes/alert/right.gif b/xCAT-web/themes/alert/right.gif new file mode 100644 index 000000000..85ba9e2a5 Binary files /dev/null and b/xCAT-web/themes/alert/right.gif differ diff --git a/xCAT-web/themes/alert/top.gif b/xCAT-web/themes/alert/top.gif new file mode 100644 index 000000000..8f6193664 Binary files /dev/null and b/xCAT-web/themes/alert/top.gif differ diff --git a/xCAT-web/themes/alert/top_left.gif b/xCAT-web/themes/alert/top_left.gif new file mode 100644 index 000000000..fabb33a18 Binary files /dev/null and b/xCAT-web/themes/alert/top_left.gif differ diff --git a/xCAT-web/themes/alert/top_right.gif b/xCAT-web/themes/alert/top_right.gif new file mode 100644 index 000000000..9fec6fa2b Binary files /dev/null and b/xCAT-web/themes/alert/top_right.gif differ diff --git a/xCAT-web/themes/alert_lite.css b/xCAT-web/themes/alert_lite.css new file mode 100644 index 000000000..a74fc71c3 --- /dev/null +++ b/xCAT-web/themes/alert_lite.css @@ -0,0 +1,88 @@ +.overlay_alert_lite { + background-color: #85BBEF; + filter:alpha(opacity=60); + -moz-opacity: 0.6; + opacity: 0.6; +} + +.alert_lite_sizer { + width:0px; + height:0px; + display:none; +} + +.alert_lite_close { + width:0px; + height:0px; + display:none; +} + +.alert_lite_minimize { + width:0px; + height:0px; + display:none; +} + +.alert_lite_maximize { + width:0px; + height:0px; + display:none; +} + +.alert_lite_title { + width:0px; + height:0px; + display:none; +} + +.alert_lite_content { + overflow:auto; + color: #000; + font-family: Tahoma, Arial, sans-serif; + font-size: 10px; + background: #FFF; +} + + +/* For alert/confirm dialog */ +.alert_lite_window { + border:1px solid #F00; + background: #FFF; + padding:20px; + margin-left:auto; + margin-right:auto; + width:400px; +} + +.alert_lite_message { + font-size:16px; + text-align:center; + width:100%; + color:#F00; + padding-bottom:10px; +} + +.alert_lite_buttons { + text-align:center; + width:100%; +} + +.alert_lite_buttons input { + width:20%; + margin:10px; +} + +.alert_lite_progress { + float:left; + margin:auto; + text-align:center; + width:100%; + height:16px; + background: #FFF url('alert/progress.gif') no-repeat center center +} + +table.alert_lite_header { + border:1px solid #F00; + background:#FFF +} + diff --git a/xCAT-web/themes/alphacube.css b/xCAT-web/themes/alphacube.css new file mode 100644 index 000000000..6d2862c0e --- /dev/null +++ b/xCAT-web/themes/alphacube.css @@ -0,0 +1,150 @@ +.overlay_alphacube { + background-color: #85BBEF; + filter:alpha(opacity=60); + -moz-opacity: 0.6; + opacity: 0.6; +} + +.alphacube_nw { + background: transparent url(alphacube/left-top.gif) no-repeat 0 0; + width:10px; + height:25px; +} + +.alphacube_n { + background: transparent url(alphacube/top-middle.gif) repeat-x 0 0; + height:25px; +} + +.alphacube_ne { + background: transparent url(alphacube/right-top.gif) no-repeat 0 0; + width:10px; + height:25px; +} + +.alphacube_w { + background: transparent url(alphacube/frame-left.gif) repeat-y top left; + width:7px; +} + +.alphacube_e { + background: transparent url(alphacube/frame-right.gif) repeat-y top right; + width:7px; +} + +.alphacube_sw { + background: transparent url(alphacube/bottom-left-c.gif) no-repeat 0 0; + width:7px; + height:7px; +} + +.alphacube_s { + background: transparent url(alphacube/bottom-middle.gif) repeat-x 0 0; + height:7px; +} + +.alphacube_se, .alphacube_sizer { + background: transparent url(alphacube/bottom-right-c.gif) no-repeat 0 0; + width:7px; + height:7px; +} + +.alphacube_sizer { + cursor:se-resize; +} + +.alphacube_close { + width: 23px; + height: 23px; + background: transparent url(alphacube/button-close-focus.gif) no-repeat 0 0; + position:absolute; + top:0px; + right:11px; + cursor:pointer; + z-index:1000; +} + +.alphacube_minimize { + width: 23px; + height: 23px; + background: transparent url(alphacube/button-min-focus.gif) no-repeat 0 0; + position:absolute; + top:0px; + right:55px; + cursor:pointer; + z-index:1000; +} + +.alphacube_maximize { + width: 23px; + height: 23px; + background: transparent url(alphacube/button-max-focus.gif) no-repeat 0 0; + position:absolute; + top:0px; + right:33px; + cursor:pointer; + z-index:1000; +} + +.alphacube_title { + float:left; + height:14px; + font-size:14px; + text-align:center; + margin-top:2px; + width:100%; + color:#123456; +} + +.alphacube_content { + overflow:auto; + color: #000; + font-family: Tahoma, Arial, sans-serif; + font: 12px arial; + background:#FDFDFD; +} + +/* For alert/confirm dialog */ +.alphacube_window { + border:1px solid #F00; + background: #FFF; + padding:20px; + margin-left:auto; + margin-right:auto; + width:400px; +} + +.alphacube_message { + font: 12px arial; + text-align:center; + width:100%; + padding-bottom:10px; +} + +.alphacube_buttons { + text-align:center; + width:100%; +} + +.alphacube_buttons input { + width:20%; + margin:10px; +} + +.alphacube_progress { + float:left; + margin:auto; + text-align:center; + width:100%; + height:16px; + background: #FFF url('alert/progress.gif') no-repeat center center +} + +.alphacube_wired_frame { + background: #FFF; + filter:alpha(opacity=60); + -moz-opacity: 0.6; + opacity: 0.6; +} + + diff --git a/xCAT-web/themes/alphacube/bottom-left-c.gif b/xCAT-web/themes/alphacube/bottom-left-c.gif new file mode 100644 index 000000000..531acdc51 Binary files /dev/null and b/xCAT-web/themes/alphacube/bottom-left-c.gif differ diff --git a/xCAT-web/themes/alphacube/bottom-middle.gif b/xCAT-web/themes/alphacube/bottom-middle.gif new file mode 100644 index 000000000..d4ce3be02 Binary files /dev/null and b/xCAT-web/themes/alphacube/bottom-middle.gif differ diff --git a/xCAT-web/themes/alphacube/bottom-right-c.gif b/xCAT-web/themes/alphacube/bottom-right-c.gif new file mode 100644 index 000000000..2164c22c1 Binary files /dev/null and b/xCAT-web/themes/alphacube/bottom-right-c.gif differ diff --git a/xCAT-web/themes/alphacube/button-close-focus.gif b/xCAT-web/themes/alphacube/button-close-focus.gif new file mode 100644 index 000000000..99f635c10 Binary files /dev/null and b/xCAT-web/themes/alphacube/button-close-focus.gif differ diff --git a/xCAT-web/themes/alphacube/button-max-focus.gif b/xCAT-web/themes/alphacube/button-max-focus.gif new file mode 100644 index 000000000..1708a1e0d Binary files /dev/null and b/xCAT-web/themes/alphacube/button-max-focus.gif differ diff --git a/xCAT-web/themes/alphacube/button-min-focus.gif b/xCAT-web/themes/alphacube/button-min-focus.gif new file mode 100644 index 000000000..ff69d1b20 Binary files /dev/null and b/xCAT-web/themes/alphacube/button-min-focus.gif differ diff --git a/xCAT-web/themes/alphacube/frame-left.gif b/xCAT-web/themes/alphacube/frame-left.gif new file mode 100644 index 000000000..543f13db6 Binary files /dev/null and b/xCAT-web/themes/alphacube/frame-left.gif differ diff --git a/xCAT-web/themes/alphacube/frame-right.gif b/xCAT-web/themes/alphacube/frame-right.gif new file mode 100644 index 000000000..5d7afef93 Binary files /dev/null and b/xCAT-web/themes/alphacube/frame-right.gif differ diff --git a/xCAT-web/themes/alphacube/left-top.gif b/xCAT-web/themes/alphacube/left-top.gif new file mode 100644 index 000000000..8373aaae1 Binary files /dev/null and b/xCAT-web/themes/alphacube/left-top.gif differ diff --git a/xCAT-web/themes/alphacube/right-top.gif b/xCAT-web/themes/alphacube/right-top.gif new file mode 100644 index 000000000..77cf65ef8 Binary files /dev/null and b/xCAT-web/themes/alphacube/right-top.gif differ diff --git a/xCAT-web/themes/alphacube/top-middle.gif b/xCAT-web/themes/alphacube/top-middle.gif new file mode 100644 index 000000000..9cab17d53 Binary files /dev/null and b/xCAT-web/themes/alphacube/top-middle.gif differ diff --git a/xCAT-web/themes/behavior.htc b/xCAT-web/themes/behavior.htc new file mode 100644 index 000000000..437c5ec92 --- /dev/null +++ b/xCAT-web/themes/behavior.htc @@ -0,0 +1,51 @@ + + + + \ No newline at end of file diff --git a/xCAT-web/themes/darkX.css b/xCAT-web/themes/darkX.css new file mode 100644 index 000000000..e3df3e0a4 --- /dev/null +++ b/xCAT-web/themes/darkX.css @@ -0,0 +1,121 @@ +.overlay_darkX { + background-color: #85BBEF; + filter:alpha(opacity=60); + -moz-opacity: 0.6; + opacity: 0.6; +} + +.darkX_nw { + background: transparent url(darkX/titlebar-left-focused.png) no-repeat 0 0; + width:6px; + height:21px; +} +.darkX_n { + background: transparent url(darkX/titlebar-mid-focused.png) repeat-x 0 0; + height:21px; +} +.darkX_ne { + background: transparent url(darkX/titlebar-right-focused.png) no-repeat 0 0; + width:6px; + height:21px; +} +.darkX_w { + background: transparent url(darkX/frame-left-focused.png) repeat-y top left; + width:3px; +} + +.darkX_e { + background: transparent url(darkX/frame-right-focused.png) repeat-y top right; + width:3px; +} + +.darkX_sw { + background: transparent url(darkX/frame-bottom-left-focused.png) no-repeat 0 0; + width:5px; + height:3px; +} +.darkX_s { + background: transparent url(darkX/frame-bottom-mid-focused.png) repeat-x 0 0; + height:3px; +} +.darkX_se, .darkX_sizer { + background: transparent url(darkX/frame-bottom-right-focused.png) no-repeat 0 0; + width:5px; + height:3px; +} + +.darkX_sizer { + cursor:se-resize; +} + +.darkX_close { + width: 21px; + height: 21px; + background: transparent url(darkX/button-close-focused.png) no-repeat 0 0; + position:absolute; + top:0px; + right:5px; + cursor:pointer; + z-index:1000; +} + +.darkX_minimize { + width: 21px; + height: 21px; + background: transparent url(darkX/button-minimize-focused.png) no-repeat 0 0; + position:absolute; + top:0px; + right:26px; + cursor:pointer; + z-index:1000; +} + +.darkX_maximize { + width: 21px; + height: 21px; + background: transparent url(darkX/button-maximize-focused.png) no-repeat 0 0; + position:absolute; + top:0px; + right:47px; + cursor:pointer; + z-index:1000; +} + + +.darkX_title { + float:left; + height:14px; + font-size:12px; + text-align:center; + margin-top:2px; + width:100%; + color:#FFF; +} + +.darkX_content { + overflow:auto; + color: #E6DF2A; + font-family: Tahoma, Arial, sans-serif; + font-size: 14px; + background:#5E5148; +} + + +/* FOR IE */ +* html .darkX_minimize { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/darkX/button-minimize-focused.png", sizingMethod="crop"); +} + +* html .darkX_maximize { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/darkX/button-maximize-focused.png", sizingMethod="scale"); +} + +* html .darkX_close { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/darkX/button-close-focused.png", sizingMethod="crop"); +} diff --git a/xCAT-web/themes/darkX/button-close-focused.png b/xCAT-web/themes/darkX/button-close-focused.png new file mode 100644 index 000000000..da5a8ac43 Binary files /dev/null and b/xCAT-web/themes/darkX/button-close-focused.png differ diff --git a/xCAT-web/themes/darkX/button-maximize-focused.png b/xCAT-web/themes/darkX/button-maximize-focused.png new file mode 100644 index 000000000..b0bd9cd6f Binary files /dev/null and b/xCAT-web/themes/darkX/button-maximize-focused.png differ diff --git a/xCAT-web/themes/darkX/button-minimize-focused.png b/xCAT-web/themes/darkX/button-minimize-focused.png new file mode 100644 index 000000000..b04e18f59 Binary files /dev/null and b/xCAT-web/themes/darkX/button-minimize-focused.png differ diff --git a/xCAT-web/themes/darkX/frame-bottom-left-focused.png b/xCAT-web/themes/darkX/frame-bottom-left-focused.png new file mode 100644 index 000000000..7d89f7b0a Binary files /dev/null and b/xCAT-web/themes/darkX/frame-bottom-left-focused.png differ diff --git a/xCAT-web/themes/darkX/frame-bottom-mid-focused.png b/xCAT-web/themes/darkX/frame-bottom-mid-focused.png new file mode 100644 index 000000000..e50eacccc Binary files /dev/null and b/xCAT-web/themes/darkX/frame-bottom-mid-focused.png differ diff --git a/xCAT-web/themes/darkX/frame-bottom-right-focused.png b/xCAT-web/themes/darkX/frame-bottom-right-focused.png new file mode 100644 index 000000000..891565911 Binary files /dev/null and b/xCAT-web/themes/darkX/frame-bottom-right-focused.png differ diff --git a/xCAT-web/themes/darkX/frame-left-focused.png b/xCAT-web/themes/darkX/frame-left-focused.png new file mode 100644 index 000000000..ff9f336f2 Binary files /dev/null and b/xCAT-web/themes/darkX/frame-left-focused.png differ diff --git a/xCAT-web/themes/darkX/frame-right-focused.png b/xCAT-web/themes/darkX/frame-right-focused.png new file mode 100644 index 000000000..f69ea0069 Binary files /dev/null and b/xCAT-web/themes/darkX/frame-right-focused.png differ diff --git a/xCAT-web/themes/darkX/titlebar-left-focused.png b/xCAT-web/themes/darkX/titlebar-left-focused.png new file mode 100644 index 000000000..1436e8410 Binary files /dev/null and b/xCAT-web/themes/darkX/titlebar-left-focused.png differ diff --git a/xCAT-web/themes/darkX/titlebar-mid-focused.png b/xCAT-web/themes/darkX/titlebar-mid-focused.png new file mode 100644 index 000000000..f7be50b14 Binary files /dev/null and b/xCAT-web/themes/darkX/titlebar-mid-focused.png differ diff --git a/xCAT-web/themes/darkX/titlebar-right-focused.png b/xCAT-web/themes/darkX/titlebar-right-focused.png new file mode 100644 index 000000000..ae23785d4 Binary files /dev/null and b/xCAT-web/themes/darkX/titlebar-right-focused.png differ diff --git a/xCAT-web/themes/debug.css b/xCAT-web/themes/debug.css new file mode 100644 index 000000000..d7981e9ca --- /dev/null +++ b/xCAT-web/themes/debug.css @@ -0,0 +1,25 @@ +div.inspector div.inspectable { + padding: 0.25em 0 0.25em 1em; + background-color: Gray; + color: white; + border: outset 2px white; + cursor: pointer; +} + +div.inspector div.child { + margin: 0 0 0 1em; +} + +#debug_window_content { /* DIV container for debug sizing*/ + width:250px; + height:100px; + background-color:#000; +} + +#debug { /* DIV container for debug contents*/ + padding:3px; + color:#0f0; + font-family:monaco, Tahoma, Verdana, Arial, Helvetica, sans-serif; + font-size:10px; +} + diff --git a/xCAT-web/themes/default.css b/xCAT-web/themes/default.css new file mode 100644 index 000000000..591451723 --- /dev/null +++ b/xCAT-web/themes/default.css @@ -0,0 +1,155 @@ +.overlay_dialog { + background-color: #666666; + filter:alpha(opacity=60); + -moz-opacity: 0.6; + opacity: 0.6; +} + +.overlay___invisible__ { + background-color: #666666; + filter:alpha(opacity=0); + -moz-opacity: 0; + opacity: 0; +} + +.dialog_nw { + width: 9px; + height: 23px; + background: transparent url(default/top_left.gif) no-repeat 0 0; +} + +.dialog_n { + background: transparent url(default/top_mid.gif) repeat-x 0 0; + height: 23px; +} + +.dialog_ne { + width: 9px; + height: 23px; + background: transparent url(default/top_right.gif) no-repeat 0 0; +} + +.dialog_e { + width: 2px; + background: transparent url(default/center_right.gif) repeat-y 0 0; +} + +.dialog_w { + width: 2px; + background: transparent url(default/center_left.gif) repeat-y 0 0; +} + +.dialog_sw { + width: 9px; + height: 19px; + background: transparent url(default/bottom_left.gif) no-repeat 0 0; +} + +.dialog_s { + background: transparent url(default/bottom_mid.gif) repeat-x 0 0; + height: 19px; +} + +.dialog_se { + width: 9px; + height: 19px; + background: transparent url(default/bottom_right.gif) no-repeat 0 0; +} + +.dialog_sizer { + width: 9px; + height: 19px; + background: transparent url(default/sizer.gif) no-repeat 0 0; + cursor:se-resize; +} + +.dialog_close { + width: 14px; + height: 14px; + background: transparent url(default/close.gif) no-repeat 0 0; + position:absolute; + top:5px; + left:8px; + cursor:pointer; + z-index:2000; +} + +.dialog_minimize { + width: 14px; + height: 15px; + background: transparent url(default/minimize.gif) no-repeat 0 0; + position:absolute; + top:5px; + left:28px; + cursor:pointer; + z-index:2000; +} + +.dialog_maximize { + width: 14px; + height: 15px; + background: transparent url(default/maximize.gif) no-repeat 0 0; + position:absolute; + top:5px; + left:49px; + cursor:pointer; + z-index:2000; +} + +.dialog_title { + float:left; + height:14px; + font-family: Tahoma, Arial, sans-serif; + font-size:12px; + text-align:center; + width:100%; + color:#000; +} + +.dialog_content { + overflow:auto; + color: #DDD; + font-family: Tahoma, Arial, sans-serif; + font-size: 10px; + background-color:#123; +} + +.top_draggable, .bottom_draggable { + cursor:move; +} + +.status_bar { + font-size:12px; +} +.status_bar input{ + font-size:12px; +} + +.wired_frame { + display: block; + position: absolute; + border: 1px #000 dashed; +} + +/* DO NOT CHANGE THESE VALUES*/ +.dialog { + display: block; + position: absolute; +} + +.dialog table.table_window { + border-collapse: collapse; + border-spacing: 0; + width: 100%; + margin: 0px; + padding:0px; +} + +.dialog table.table_window td , .dialog table.table_window th { + padding: 0; +} + +.dialog .title_window { + -moz-user-select:none; +} + diff --git a/xCAT-web/themes/default/bottom_left.gif b/xCAT-web/themes/default/bottom_left.gif new file mode 100644 index 000000000..4c73d3563 Binary files /dev/null and b/xCAT-web/themes/default/bottom_left.gif differ diff --git a/xCAT-web/themes/default/bottom_mid.gif b/xCAT-web/themes/default/bottom_mid.gif new file mode 100644 index 000000000..9205d3019 Binary files /dev/null and b/xCAT-web/themes/default/bottom_mid.gif differ diff --git a/xCAT-web/themes/default/bottom_right.gif b/xCAT-web/themes/default/bottom_right.gif new file mode 100644 index 000000000..8d002eeb8 Binary files /dev/null and b/xCAT-web/themes/default/bottom_right.gif differ diff --git a/xCAT-web/themes/default/bottom_right_resize.gif b/xCAT-web/themes/default/bottom_right_resize.gif new file mode 100644 index 000000000..649b0d870 Binary files /dev/null and b/xCAT-web/themes/default/bottom_right_resize.gif differ diff --git a/xCAT-web/themes/default/center_left.gif b/xCAT-web/themes/default/center_left.gif new file mode 100644 index 000000000..79e7a1cca Binary files /dev/null and b/xCAT-web/themes/default/center_left.gif differ diff --git a/xCAT-web/themes/default/center_right.gif b/xCAT-web/themes/default/center_right.gif new file mode 100644 index 000000000..554c55c86 Binary files /dev/null and b/xCAT-web/themes/default/center_right.gif differ diff --git a/xCAT-web/themes/default/clear.gif b/xCAT-web/themes/default/clear.gif new file mode 100644 index 000000000..c10b16694 Binary files /dev/null and b/xCAT-web/themes/default/clear.gif differ diff --git a/xCAT-web/themes/default/close.gif b/xCAT-web/themes/default/close.gif new file mode 100644 index 000000000..31ef5a394 Binary files /dev/null and b/xCAT-web/themes/default/close.gif differ diff --git a/xCAT-web/themes/default/inspect.gif b/xCAT-web/themes/default/inspect.gif new file mode 100644 index 000000000..ebeeb02f6 Binary files /dev/null and b/xCAT-web/themes/default/inspect.gif differ diff --git a/xCAT-web/themes/default/maximize.gif b/xCAT-web/themes/default/maximize.gif new file mode 100644 index 000000000..892a0f08c Binary files /dev/null and b/xCAT-web/themes/default/maximize.gif differ diff --git a/xCAT-web/themes/default/minimize.gif b/xCAT-web/themes/default/minimize.gif new file mode 100644 index 000000000..a7214167b Binary files /dev/null and b/xCAT-web/themes/default/minimize.gif differ diff --git a/xCAT-web/themes/default/overlay.png b/xCAT-web/themes/default/overlay.png new file mode 100644 index 000000000..648e71ed6 Binary files /dev/null and b/xCAT-web/themes/default/overlay.png differ diff --git a/xCAT-web/themes/default/resize.gif b/xCAT-web/themes/default/resize.gif new file mode 100644 index 000000000..c44070259 Binary files /dev/null and b/xCAT-web/themes/default/resize.gif differ diff --git a/xCAT-web/themes/default/sizer.gif b/xCAT-web/themes/default/sizer.gif new file mode 100644 index 000000000..649b0d870 Binary files /dev/null and b/xCAT-web/themes/default/sizer.gif differ diff --git a/xCAT-web/themes/default/top_left.gif b/xCAT-web/themes/default/top_left.gif new file mode 100644 index 000000000..774538ac6 Binary files /dev/null and b/xCAT-web/themes/default/top_left.gif differ diff --git a/xCAT-web/themes/default/top_mid.gif b/xCAT-web/themes/default/top_mid.gif new file mode 100644 index 000000000..6124e78bd Binary files /dev/null and b/xCAT-web/themes/default/top_mid.gif differ diff --git a/xCAT-web/themes/default/top_right.gif b/xCAT-web/themes/default/top_right.gif new file mode 100644 index 000000000..fbc94bf2b Binary files /dev/null and b/xCAT-web/themes/default/top_right.gif differ diff --git a/xCAT-web/themes/iefix/blank.gif b/xCAT-web/themes/iefix/blank.gif new file mode 100644 index 000000000..75b945d25 Binary files /dev/null and b/xCAT-web/themes/iefix/blank.gif differ diff --git a/xCAT-web/themes/iefix/iepngfix.css b/xCAT-web/themes/iefix/iepngfix.css new file mode 100644 index 000000000..249388be5 --- /dev/null +++ b/xCAT-web/themes/iefix/iepngfix.css @@ -0,0 +1,3 @@ +/* PNG fix for all themes that uses PNG images on IE */ +td, div { behavior: url(../themes/iefix/iepngfix.htc) } + diff --git a/xCAT-web/themes/iefix/iepngfix.htc b/xCAT-web/themes/iefix/iepngfix.htc new file mode 100644 index 000000000..a6c683b9f --- /dev/null +++ b/xCAT-web/themes/iefix/iepngfix.htc @@ -0,0 +1,54 @@ + + + + + \ No newline at end of file diff --git a/xCAT-web/themes/lighting.css b/xCAT-web/themes/lighting.css new file mode 100644 index 000000000..0d955c3d7 --- /dev/null +++ b/xCAT-web/themes/lighting.css @@ -0,0 +1,960 @@ +.overlay___invisible__ { + background-color: #666; + filter:alpha(opacity=0); + -moz-opacity: 0; + opacity: 0; +} + +.top_draggable, .bottom_draggable { + cursor:move; +} + +.status_bar { + font-size:12px; +} +.status_bar input{ + font-size:12px; +} + +.wired_frame { + display:block; + position:absolute; + border:1px #000 dashed; +} + + + +.overlay_bluelighting { + background-color:#FFF; + filter:alpha(opacity=60); + -moz-opacity:0.6; + opacity:0.6; +} + +.bluelighting_wired_frame { + background:#FFF; + filter:alpha(opacity=60); + -moz-opacity:0.6; + opacity:0.6; +} + +.bluelighting_nw { + background:transparent url(lighting/top-left-blue.png) no-repeat 0 0; + width:9px; + height:28px; +} + +.bluelighting_n { + background:transparent url(lighting/top-middle-blue.png) repeat-x 0 0; + height:28px; +} + +.bluelighting_ne { + background:transparent url(lighting/top-right-blue.png) no-repeat 0 0; + width:15px; + height:28px; +} + +.bluelighting_w { + background:transparent url(lighting/left-blue.png) repeat-y top left; + width:9px; +} + +.bluelighting_e { + background:transparent url(lighting/right-blue.png) repeat-y top right; + width:15px; +} + +.bluelighting_sw { + background:transparent url(lighting/bottom-left-blue.png) no-repeat 0 0; + width:9px; + height:15px; +} + +.bluelighting_s { + background:transparent url(lighting/bottom-middle-blue.png) repeat-x 0 0; + height:15px; +} + +.bluelighting_se, .bluelighting_sizer { + background:transparent url(lighting/bottom-right-blue.png) no-repeat 0 0; + width:15px; + height:15px; +} + +.bluelighting_sizer { + cursor:se-resize; +} + +.bluelighting_close { + width:15px; + height:9px; + background:transparent url(lighting/button-close-blue.png) no-repeat 0 0; + position:absolute; + top:11px; + right:10px; + cursor:pointer; + z-index:1000; +} + +.bluelighting_maximize { + width:15px; + height:9px; + background:transparent url(lighting/button-maximize-blue.png) no-repeat 0 0; + position:absolute; + top:11px; + right:25px; + cursor:pointer; + z-index:1000; +} + +.bluelighting_minimize { + width:15px; + height:9px; + background:transparent url(lighting/button-minimize-blue.png) no-repeat 0 0; + position:absolute; + top:11px; + right:40px; + cursor:pointer; + z-index:1000; +} + +.bluelighting_title { + float:left; + height:14px; + font-size:14px; + font-weight:bold; + font-family:Verdana, Arial, sans-serif; + text-align:center; + margin-top:2px; + width:100%; + color:#17385B; +} + +.bluelighting_content { + overflow:auto; + color:#000; + font-family:Verdana, Arial, sans-serif; + font-size:12px; + background:#BFDBFF; +} + +/* For alert/confirm dialog */ +.bluelighting_window { + border:1px solid #F00; + background:#FFF; + padding:20px; + margin-left:auto; + margin-right:auto; + width:400px; +} + +.bluelighting_message { + font-size:12px; + text-align:center; + width:100%; + padding-bottom:10px; +} + +.bluelighting_buttons { + text-align:center; + width:100%; +} + +.bluelighting_buttons input { + border:1px solid #999; + border-top-color:#CCC; + border-left-color:#CCC; + padding:2px; + background-color:#FFF; + color:#333; + background-image:url(lighting/background_buttons.gif); + background-repeat:repeat-x; + font-family:Verdana, Arial, sans-serif; + font-size:10px; + font-weight:bold; + text-align:center; +} + +.bluelighting_progress { + float:left; + margin:auto; + text-align:center; + width:100%; + height:16px; + background:transparent url('lighting/spinner.gif') no-repeat center center +} + +/* FOR IE */ +* html .bluelighting_nw { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-left-blue.png", sizingMethod="crop"); +} + +* html .bluelighting_n { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-middle-blue.png", sizingMethod="scale"); +} + +* html .bluelighting_ne { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-right-blue.png", sizingMethod="crop"); +} + +* html .bluelighting_w { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/left-blue.png", sizingMethod="scale"); +} + +* html .bluelighting_e { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/right-blue.png", sizingMethod="scale"); +} + +* html .bluelighting_sw { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-left-blue.png", sizingMethod="crop"); +} + +* html .bluelighting_s { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-middle-blue.png", sizingMethod="scale"); +} + +* html .bluelighting_se, * html .bluelighting_sizer { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-right-blue.png", sizingMethod="crop"); +} + +* html .bluelighting_close { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-close-blue.png", sizingMethod="crop"); +} + +* html .bluelighting_minimize { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-minimize-blue.png", sizingMethod="crop"); +} + +* html .bluelighting_maximize { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-maximize-blue.png", sizingMethod="crop"); +} + +* html .bluelighting_content { + background:#B8D7FF; +} + + + +.overlay_greylighting { + background-color:#FFF; + filter:alpha(opacity=60); + -moz-opacity:0.6; + opacity:0.6; +} + +.greylighting_wired_frame { + background:#FFF; + filter:alpha(opacity=60); + -moz-opacity:0.6; + opacity:0.6; +} + +.greylighting_nw { + background:transparent url(lighting/top-left-grey.png) no-repeat 0 0; + width:9px; + height:28px; +} + +.greylighting_n { + background:transparent url(lighting/top-middle-grey.png) repeat-x 0 0; + height:28px; +} + +.greylighting_ne { + background:transparent url(lighting/top-right-grey.png) no-repeat 0 0; + width:15px; + height:28px; +} + +.greylighting_w { + background:transparent url(lighting/left-grey.png) repeat-y top left; + width:9px; +} + +.greylighting_e { + background:transparent url(lighting/right-grey.png) repeat-y top right; + width:15px; +} + +.greylighting_sw { + background:transparent url(lighting/bottom-left-grey.png) no-repeat 0 0; + width:9px; + height:15px; +} + +.greylighting_s { + background:transparent url(lighting/bottom-middle-grey.png) repeat-x 0 0; + height:15px; +} + +.greylighting_se, .greylighting_sizer { + background:transparent url(lighting/bottom-right-grey.png) no-repeat 0 0; + width:15px; + height:15px; +} + +.greylighting_sizer { + cursor:se-resize; +} + +.greylighting_close { + width:15px; + height:9px; + background:transparent url(lighting/button-close-grey.png) no-repeat 0 0; + position:absolute; + top:11px; + right:10px; + cursor:pointer; + z-index:1000; +} + +.greylighting_maximize { + width:15px; + height:9px; + background:transparent url(lighting/button-maximize-grey.png) no-repeat 0 0; + position:absolute; + top:11px; + right:25px; + cursor:pointer; + z-index:1000; +} + +.greylighting_minimize { + width:15px; + height:9px; + background:transparent url(lighting/button-minimize-grey.png) no-repeat 0 0; + position:absolute; + top:11px; + right:40px; + cursor:pointer; + z-index:1000; +} + +.greylighting_title { + float:left; + height:14px; + font-size:14px; + font-weight:bold; + font-family:Verdana, Arial, sans-serif; + text-align:center; + margin-top:2px; + width:100%; + color:#525252; +} + +.greylighting_content { + overflow:auto; + color:#000; + font-family:Verdana, Arial, sans-serif; + font-size:12px; + background:#CDCDCD; +} + +/* For alert/confirm dialog */ +.greylighting_window { + border:1px solid #F00; + background:#FFF; + padding:20px; + margin-left:auto; + margin-right:auto; + width:400px; +} + +.greylighting_message { + font-size:12px; + text-align:center; + width:100%; + padding-bottom:10px; +} + +.greylighting_buttons { + text-align:center; + width:100%; +} + +.greylighting_buttons input { + border:1px solid #999; + border-top-color:#CCC; + border-left-color:#CCC; + padding:2px; + background-color:#FFF; + color:#333; + background-image:url(lighting/background_buttons.gif); + background-repeat:repeat-x; + font-family:Verdana, Arial, sans-serif; + font-size:10px; + font-weight:bold; + text-align:center; +} + +.greylighting_progress { + float:left; + margin:auto; + text-align:center; + width:100%; + height:16px; + background:transparent url('lighting/spinner.gif') no-repeat center center +} + +/* FOR IE */ +* html .greylighting_nw { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-left-grey.png", sizingMethod="crop"); +} + +* html .greylighting_n { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-middle-grey.png", sizingMethod="scale"); +} + +* html .greylighting_ne { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-right-grey.png", sizingMethod="crop"); +} + +* html .greylighting_w { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/left-grey.png", sizingMethod="scale"); +} + +* html .greylighting_e { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/right-grey.png", sizingMethod="scale"); +} + +* html .greylighting_sw { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-left-grey.png", sizingMethod="crop"); +} + +* html .greylighting_s { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-middle-grey.png", sizingMethod="scale"); +} + +* html greylighting_se, * html .greylighting_sizer { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-right-grey.png", sizingMethod="crop"); +} + +* html .greylighting_close { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-close-grey.png", sizingMethod="crop"); +} + +* html .greylighting_minimize { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-minimize-grey.png", sizingMethod="crop"); +} + +* html .greylighting_maximize { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-maximize-grey.png", sizingMethod="crop"); +} + +* html .greylighting_content { + background:#C7C7C7; +} + + + +.overlay_greenlighting { + background-color:#FFF; + filter:alpha(opacity=60); + -moz-opacity:0.6; + opacity:0.6; +} + +.greenlighting_wired_frame { + background:#FFF; + filter:alpha(opacity=60); + -moz-opacity:0.6; + opacity:0.6; +} + +.greenlighting_nw { + background:transparent url(lighting/top-left-green.png) no-repeat 0 0; + width:9px; + height:28px; +} + +.greenlighting_n { + background:transparent url(lighting/top-middle-green.png) repeat-x 0 0; + height:28px; +} + +.greenlighting_ne { + background:transparent url(lighting/top-right-green.png) no-repeat 0 0; + width:15px; + height:28px; +} + +.greenlighting_w { + background:transparent url(lighting/left-green.png) repeat-y top left; + width:9px; +} + +.greenlighting_e { + background:transparent url(lighting/right-green.png) repeat-y top right; + width:15px; +} + +.greenlighting_sw { + background:transparent url(lighting/bottom-left-green.png) no-repeat 0 0; + width:9px; + height:15px; +} + +.greenlighting_s { + background:transparent url(lighting/bottom-middle-green.png) repeat-x 0 0; + height:15px; +} + +.greenlighting_se, .greenlighting_sizer { + background:transparent url(lighting/bottom-right-green.png) no-repeat 0 0; + width:15px; + height:15px; +} + +.greenlighting_sizer { + cursor:se-resize; +} + +.greenlighting_close { + width:15px; + height:9px; + background:transparent url(lighting/button-close-green.png) no-repeat 0 0; + position:absolute; + top:11px; + right:10px; + cursor:pointer; + z-index:1000; +} + +.greenlighting_maximize { + width:15px; + height:9px; + background:transparent url(lighting/button-maximize-green.png) no-repeat 0 0; + position:absolute; + top:11px; + right:25px; + cursor:pointer; + z-index:1000; +} + +.greenlighting_minimize { + width:15px; + height:9px; + background:transparent url(lighting/button-minimize-green.png) no-repeat 0 0; + position:absolute; + top:11px; + right:40px; + cursor:pointer; + z-index:1000; +} + +.greenlighting_title { + float:left; + height:14px; + font-size:14px; + font-weight:bold; + font-family:Verdana, Arial, sans-serif; + text-align:center; + margin-top:2px; + width:100%; + color:#2A6002; +} + +.greenlighting_content { + overflow:auto; + color:#000; + font-family:Verdana, Arial, sans-serif; + font-size:12px; + background:#ACFCAF; +} + +/* For alert/confirm dialog */ +.greenlighting_window { + border:1px solid #F00; + background:#FFF; + padding:20px; + margin-left:auto; + margin-right:auto; + width:400px; +} + +.greenlighting_message { + font-size:12px; + text-align:center; + width:100%; + padding-bottom:10px; +} + +.greenlighting_buttons { + text-align:center; + width:100%; +} + +.greenlighting_buttons input { + border:1px solid #999; + border-top-color:#CCC; + border-left-color:#CCC; + padding:2px; + background-color:#FFF; + color:#333; + background-image:url(lighting/background_buttons.gif); + background-repeat:repeat-x; + font-family:Verdana, Arial, sans-serif; + font-size:10px; + font-weight:bold; + text-align:center; +} + +.greenlighting_progress { + float:left; + margin:auto; + text-align:center; + width:100%; + height:16px; + background:transparent url('lighting/spinner.gif') no-repeat center center +} + +/* FOR IE */ +* html .greenlighting_nw { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-left-green.png", sizingMethod="crop"); +} + +* html .greenlighting_n { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-middle-green.png", sizingMethod="scale"); +} + +* html .greenlighting_ne { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-right-green.png", sizingMethod="crop"); +} + +* html .greenlighting_w { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/left-green.png", sizingMethod="scale"); +} + +* html .greenlighting_e { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/right-green.png", sizingMethod="scale"); +} + +* html .greenlighting_sw { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-left-green.png", sizingMethod="crop"); +} + +* html .greenlighting_s { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-middle-green.png", sizingMethod="scale"); +} + +* html greenlighting_se, * html .greenlighting_sizer { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-right-green.png", sizingMethod="crop"); +} + +* html .greenlighting_close { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-close-green.png", sizingMethod="crop"); +} + +* html .greenlighting_minimize { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-minimize-green.png", sizingMethod="crop"); +} + +* html .greenlighting_maximize { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-maximize-green.png", sizingMethod="crop"); +} + +* html .greenlighting_content { + background:#A4FCA7; +} + + + +.overlay_darkbluelighting { + background-color:#FFF; + filter:alpha(opacity=60); + -moz-opacity:0.6; + opacity:0.6; +} + +.darkbluelighting_wired_frame { + background:#FFF; + filter:alpha(opacity=60); + -moz-opacity:0.6; + opacity:0.6; +} + +.darkbluelighting_nw { + background:transparent url(lighting/top-left-darkblue.png) no-repeat 0 0; + width:9px; + height:28px; +} + +.darkbluelighting_n { + background:transparent url(lighting/top-middle-darkblue.png) repeat-x 0 0; + height:28px; +} + +.darkbluelighting_ne { + background:transparent url(lighting/top-right-darkblue.png) no-repeat 0 0; + width:15px; + height:28px; +} + +.darkbluelighting_w { + background:transparent url(lighting/left-darkblue.png) repeat-y top left; + width:9px; +} + +.darkbluelighting_e { + background:transparent url(lighting/right-darkblue.png) repeat-y top right; + width:15px; +} + +.darkbluelighting_sw { + background:transparent url(lighting/bottom-left-darkblue.png) no-repeat 0 0; + width:9px; + height:15px; +} + +.darkbluelighting_s { + background:transparent url(lighting/bottom-middle-darkblue.png) repeat-x 0 0; + height:15px; +} + +.darkbluelighting_se, .darkbluelighting_sizer { + background:transparent url(lighting/bottom-right-darkblue.png) no-repeat 0 0; + width:15px; + height:15px; +} + +.darkbluelighting_sizer { + cursor:se-resize; +} + +.darkbluelighting_close { + width:15px; + height:9px; + background:transparent url(lighting/button-close-darkblue.png) no-repeat 0 0; + position:absolute; + top:11px; + right:10px; + cursor:pointer; + z-index:1000; +} + +.darkbluelighting_maximize { + width:15px; + height:9px; + background:transparent url(lighting/button-maximize-darkblue.png) no-repeat 0 0; + position:absolute; + top:11px; + right:25px; + cursor:pointer; + z-index:1000; +} + +.darkbluelighting_minimize { + width:15px; + height:9px; + background:transparent url(lighting/button-minimize-darkblue.png) no-repeat 0 0; + position:absolute; + top:11px; + right:40px; + cursor:pointer; + z-index:1000; +} + +.darkbluelighting_title { + float:left; + height:14px; + font-size:14px; + font-weight:bold; + font-family:Verdana, Arial, sans-serif; + text-align:center; + margin-top:2px; + width:100%; + color:#E4EFFD; +} + +.darkbluelighting_content { + overflow:auto; + color:#FFF; + font-family:Verdana, Arial, sans-serif; + font-size:12px; + background:#0413C0; +} + +/* For alert/confirm dialog */ +.darkbluelighting_window { + border:1px solid #F00; + background:#FFF; + padding:20px; + margin-left:auto; + margin-right:auto; + width:400px; +} + +.darkbluelighting_message { + font-size:12px; + text-align:center; + width:100%; + padding-bottom:10px; +} + +.darkbluelighting_buttons { + text-align:center; + width:100%; +} + +.darkbluelighting_buttons input { + border:1px solid #999; + border-top-color:#CCC; + border-left-color:#CCC; + padding:2px; + background-color:#FFF; + color:#333; + background-image:url(lighting/background_buttons.gif); + background-repeat:repeat-x; + font-family:Verdana, Arial, sans-serif; + font-size:10px; + font-weight:bold; + text-align:center; +} + +.darkbluelighting_progress { + float:left; + margin:auto; + text-align:center; + width:100%; + height:16px; + background:transparent url('lighting/spinner.gif') no-repeat center center +} + +/* FOR IE */ +* html .darkbluelighting_nw { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-left-darkblue.png", sizingMethod="crop"); +} + +* html .darkbluelighting_n { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-middle-darkblue.png", sizingMethod="scale"); +} + +* html .darkbluelighting_ne { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/top-right-darkblue.png", sizingMethod="crop"); +} + +* html .darkbluelighting_w { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/left-darkblue.png", sizingMethod="scale"); +} + +* html .darkbluelighting_e { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/right-darkblue.png", sizingMethod="scale"); +} + +* html .darkbluelighting_sw { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-left-darkblue.png", sizingMethod="crop"); +} + +* html .darkbluelighting_s { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-middle-darkblue.png", sizingMethod="scale"); +} + +* html darkbluelighting_se, * html .darkbluelighting_sizer { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/bottom-right-darkblue.png", sizingMethod="crop"); +} + +* html .darkbluelighting_close { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-close-darkblue.png", sizingMethod="crop"); +} + +* html .darkbluelighting_minimize { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-minimize-darkblue.png", sizingMethod="crop"); +} + +* html .darkbluelighting_maximize { + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/lighting/button-maximize-darkblue.png", sizingMethod="crop"); +} + +* html .darkbluelighting_content { + background:#020EBA; +} + diff --git a/xCAT-web/themes/lighting/background_buttons.gif b/xCAT-web/themes/lighting/background_buttons.gif new file mode 100644 index 000000000..2fa58b29f Binary files /dev/null and b/xCAT-web/themes/lighting/background_buttons.gif differ diff --git a/xCAT-web/themes/lighting/bottom-left-blue.png b/xCAT-web/themes/lighting/bottom-left-blue.png new file mode 100644 index 000000000..4592c19be Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-left-blue.png differ diff --git a/xCAT-web/themes/lighting/bottom-left-darkblue.png b/xCAT-web/themes/lighting/bottom-left-darkblue.png new file mode 100644 index 000000000..ce238f125 Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-left-darkblue.png differ diff --git a/xCAT-web/themes/lighting/bottom-left-green.png b/xCAT-web/themes/lighting/bottom-left-green.png new file mode 100644 index 000000000..38f9e9e34 Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-left-green.png differ diff --git a/xCAT-web/themes/lighting/bottom-left-grey.png b/xCAT-web/themes/lighting/bottom-left-grey.png new file mode 100644 index 000000000..2ace75e1b Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-left-grey.png differ diff --git a/xCAT-web/themes/lighting/bottom-middle-blue.png b/xCAT-web/themes/lighting/bottom-middle-blue.png new file mode 100644 index 000000000..439bcaa1d Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-middle-blue.png differ diff --git a/xCAT-web/themes/lighting/bottom-middle-darkblue.png b/xCAT-web/themes/lighting/bottom-middle-darkblue.png new file mode 100644 index 000000000..3fae972a1 Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-middle-darkblue.png differ diff --git a/xCAT-web/themes/lighting/bottom-middle-green.png b/xCAT-web/themes/lighting/bottom-middle-green.png new file mode 100644 index 000000000..89a732f66 Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-middle-green.png differ diff --git a/xCAT-web/themes/lighting/bottom-middle-grey.png b/xCAT-web/themes/lighting/bottom-middle-grey.png new file mode 100644 index 000000000..52bfee0de Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-middle-grey.png differ diff --git a/xCAT-web/themes/lighting/bottom-right-blue.png b/xCAT-web/themes/lighting/bottom-right-blue.png new file mode 100644 index 000000000..dffb75f08 Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-right-blue.png differ diff --git a/xCAT-web/themes/lighting/bottom-right-darkblue.png b/xCAT-web/themes/lighting/bottom-right-darkblue.png new file mode 100644 index 000000000..62a4d8b8a Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-right-darkblue.png differ diff --git a/xCAT-web/themes/lighting/bottom-right-green.png b/xCAT-web/themes/lighting/bottom-right-green.png new file mode 100644 index 000000000..4aee20fc6 Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-right-green.png differ diff --git a/xCAT-web/themes/lighting/bottom-right-grey.png b/xCAT-web/themes/lighting/bottom-right-grey.png new file mode 100644 index 000000000..caa27fe13 Binary files /dev/null and b/xCAT-web/themes/lighting/bottom-right-grey.png differ diff --git a/xCAT-web/themes/lighting/button-close-blue.png b/xCAT-web/themes/lighting/button-close-blue.png new file mode 100644 index 000000000..523e0dbc2 Binary files /dev/null and b/xCAT-web/themes/lighting/button-close-blue.png differ diff --git a/xCAT-web/themes/lighting/button-close-darkblue.png b/xCAT-web/themes/lighting/button-close-darkblue.png new file mode 100644 index 000000000..a7b8daa89 Binary files /dev/null and b/xCAT-web/themes/lighting/button-close-darkblue.png differ diff --git a/xCAT-web/themes/lighting/button-close-green.png b/xCAT-web/themes/lighting/button-close-green.png new file mode 100644 index 000000000..63f755b6c Binary files /dev/null and b/xCAT-web/themes/lighting/button-close-green.png differ diff --git a/xCAT-web/themes/lighting/button-close-grey.png b/xCAT-web/themes/lighting/button-close-grey.png new file mode 100644 index 000000000..0c5375f42 Binary files /dev/null and b/xCAT-web/themes/lighting/button-close-grey.png differ diff --git a/xCAT-web/themes/lighting/button-maximize-blue.png b/xCAT-web/themes/lighting/button-maximize-blue.png new file mode 100644 index 000000000..2cdf404cf Binary files /dev/null and b/xCAT-web/themes/lighting/button-maximize-blue.png differ diff --git a/xCAT-web/themes/lighting/button-maximize-darkblue.png b/xCAT-web/themes/lighting/button-maximize-darkblue.png new file mode 100644 index 000000000..4ef5758fa Binary files /dev/null and b/xCAT-web/themes/lighting/button-maximize-darkblue.png differ diff --git a/xCAT-web/themes/lighting/button-maximize-green.png b/xCAT-web/themes/lighting/button-maximize-green.png new file mode 100644 index 000000000..d4c1e5787 Binary files /dev/null and b/xCAT-web/themes/lighting/button-maximize-green.png differ diff --git a/xCAT-web/themes/lighting/button-maximize-grey.png b/xCAT-web/themes/lighting/button-maximize-grey.png new file mode 100644 index 000000000..35da0e0fc Binary files /dev/null and b/xCAT-web/themes/lighting/button-maximize-grey.png differ diff --git a/xCAT-web/themes/lighting/button-minimize-blue.png b/xCAT-web/themes/lighting/button-minimize-blue.png new file mode 100644 index 000000000..92db9a0db Binary files /dev/null and b/xCAT-web/themes/lighting/button-minimize-blue.png differ diff --git a/xCAT-web/themes/lighting/button-minimize-darkblue.png b/xCAT-web/themes/lighting/button-minimize-darkblue.png new file mode 100644 index 000000000..e3d865464 Binary files /dev/null and b/xCAT-web/themes/lighting/button-minimize-darkblue.png differ diff --git a/xCAT-web/themes/lighting/button-minimize-green.png b/xCAT-web/themes/lighting/button-minimize-green.png new file mode 100644 index 000000000..4846e2f43 Binary files /dev/null and b/xCAT-web/themes/lighting/button-minimize-green.png differ diff --git a/xCAT-web/themes/lighting/button-minimize-grey.png b/xCAT-web/themes/lighting/button-minimize-grey.png new file mode 100644 index 000000000..310bcbbbc Binary files /dev/null and b/xCAT-web/themes/lighting/button-minimize-grey.png differ diff --git a/xCAT-web/themes/lighting/left-blue.png b/xCAT-web/themes/lighting/left-blue.png new file mode 100644 index 000000000..30275bbb9 Binary files /dev/null and b/xCAT-web/themes/lighting/left-blue.png differ diff --git a/xCAT-web/themes/lighting/left-darkblue.png b/xCAT-web/themes/lighting/left-darkblue.png new file mode 100644 index 000000000..36c10e0d7 Binary files /dev/null and b/xCAT-web/themes/lighting/left-darkblue.png differ diff --git a/xCAT-web/themes/lighting/left-green.png b/xCAT-web/themes/lighting/left-green.png new file mode 100644 index 000000000..c8aee739f Binary files /dev/null and b/xCAT-web/themes/lighting/left-green.png differ diff --git a/xCAT-web/themes/lighting/left-grey.png b/xCAT-web/themes/lighting/left-grey.png new file mode 100644 index 000000000..acc2af03a Binary files /dev/null and b/xCAT-web/themes/lighting/left-grey.png differ diff --git a/xCAT-web/themes/lighting/pngbehavior.htc b/xCAT-web/themes/lighting/pngbehavior.htc new file mode 100644 index 000000000..36ea182e7 --- /dev/null +++ b/xCAT-web/themes/lighting/pngbehavior.htc @@ -0,0 +1,67 @@ + + + +